From owner-svn-src-head@freebsd.org Mon Apr 30 12:16:56 2018 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 7C03FFA8609; Mon, 30 Apr 2018 12:16:56 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 26D636C941; Mon, 30 Apr 2018 12:16:56 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 085671310E; Mon, 30 Apr 2018 12:16:56 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w3UCGt25060986; Mon, 30 Apr 2018 12:16:55 GMT (envelope-from wulf@FreeBSD.org) Received: (from wulf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w3UCGsao060973; Mon, 30 Apr 2018 12:16:54 GMT (envelope-from wulf@FreeBSD.org) Message-Id: <201804301216.w3UCGsao060973@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wulf set sender to wulf@FreeBSD.org using -f From: Vladimir Kondratyev Date: Mon, 30 Apr 2018 12:16:54 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r333113 - in head: etc/defaults etc/rc.d usr.sbin/bluetooth/bthidd X-SVN-Group: head X-SVN-Commit-Author: wulf X-SVN-Commit-Paths: in head: etc/defaults etc/rc.d usr.sbin/bluetooth/bthidd X-SVN-Commit-Revision: 333113 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 30 Apr 2018 12:16:57 -0000 Author: wulf Date: Mon Apr 30 12:16:54 2018 New Revision: 333113 URL: https://svnweb.freebsd.org/changeset/base/333113 Log: bthidd(8): Add evdev protocol support for bluetooth keyboards and mouses User-visible changes: "-u" is added to to list of command line options supported by bthidd. Use it to enable evdev support. uinput and evdev modules should be kld-loaded or compiled into the kernel in that case. bthidd_evdev_support rc.conf variable is added to control enabling of evdev support in bthidd startup script. Possible values are: "YES", "NO", "AUTO"(default). Setting bthidd_evdev_support to "AUTO" inserts "-u" option if kernel is compiled with EVDEV_SUPPORT option enabled. Support for consumer HID usage page keyboard events is implemented. Most of them are available only through evdev protocol. kern.evdev.rcpt_mask sysctl is checked, so "sysctl kern.evdev.rcpt_mask=12" should be executed if EVDEV_SUPPORT is compiled into kernel. It is recommended to regenerate bthidd.conf entries with bthidcontrol(8) "Query" command to set user-friendly names of bluetooth devices. Reviewed by: emax, gonzo, wblock (docs), bcr (docs, early version) Differential Revision: https://reviews.freebsd.org/D13456 Added: head/usr.sbin/bluetooth/bthidd/btuinput.c (contents, props changed) head/usr.sbin/bluetooth/bthidd/btuinput.h (contents, props changed) Modified: head/etc/defaults/rc.conf head/etc/rc.d/bthidd head/usr.sbin/bluetooth/bthidd/Makefile head/usr.sbin/bluetooth/bthidd/bthid_config.h head/usr.sbin/bluetooth/bthidd/bthidd.8 head/usr.sbin/bluetooth/bthidd/bthidd.c head/usr.sbin/bluetooth/bthidd/bthidd.h head/usr.sbin/bluetooth/bthidd/client.c head/usr.sbin/bluetooth/bthidd/hid.c head/usr.sbin/bluetooth/bthidd/kbd.c head/usr.sbin/bluetooth/bthidd/parser.y head/usr.sbin/bluetooth/bthidd/server.c head/usr.sbin/bluetooth/bthidd/session.c Modified: head/etc/defaults/rc.conf ============================================================================== --- head/etc/defaults/rc.conf Mon Apr 30 10:49:29 2018 (r333112) +++ head/etc/defaults/rc.conf Mon Apr 30 12:16:54 2018 (r333113) @@ -433,6 +433,7 @@ sdpd_username="nobody" # it initializes bthidd_enable="NO" # Enable bthidd(8) (or NO) bthidd_config="/etc/bluetooth/bthidd.conf" # bthidd(8) configuration file bthidd_hids="/var/db/bthidd.hids" # bthidd(8) known HID devices file +bthidd_evdev_support="AUTO" # AUTO depends on EVDEV_SUPPORT kernel option rfcomm_pppd_server_enable="NO" # Enable rfcomm_pppd(8) in server mode (or NO) rfcomm_pppd_server_profile="one two" # Profile to use from /etc/ppp/ppp.conf Modified: head/etc/rc.d/bthidd ============================================================================== --- head/etc/rc.d/bthidd Mon Apr 30 10:49:29 2018 (r333112) +++ head/etc/rc.d/bthidd Mon Apr 30 12:16:54 2018 (r333113) @@ -17,8 +17,25 @@ command="/usr/sbin/${name}" pidfile="/var/run/${name}.pid" start_precmd="bthidd_prestart" +evdev_enabled() +{ + case ${bthidd_evdev_support} in + [Aa][Uu][Tt][Oo]) + check_kern_features evdev_support + return $? + ;; + *) + checkyesno bthidd_evdev_support + return $? + ;; + esac +} + bthidd_prestart() { + if evdev_enabled; then + load_kld -m uinput uinput + fi load_kld -m kbdmux kbdmux load_kld -m vkbd vkbd load_kld -m ng_btsocket ng_btsocket @@ -29,6 +46,9 @@ load_rc_config $name config="${bthidd_config:-/etc/bluetooth/${name}.conf}" hids="${bthidd_hids:-/var/db/${name}.hids}" command_args="-c ${config} -H ${hids} -p ${pidfile}" +if evdev_enabled; then + command_args="$command_args -u" +fi required_files="${config}" run_rc_command "$1" Modified: head/usr.sbin/bluetooth/bthidd/Makefile ============================================================================== --- head/usr.sbin/bluetooth/bthidd/Makefile Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/Makefile Mon Apr 30 12:16:54 2018 (r333113) @@ -4,8 +4,8 @@ PROG= bthidd MAN= bthidd.8 # bthidd.conf.5 -SRCS= bthidd.c client.c hid.c kbd.c lexer.l parser.y server.c \ - session.c +SRCS= bthidd.c btuinput.c client.c hid.c kbd.c lexer.l parser.y \ + server.c session.c CFLAGS+= -I${.CURDIR} Modified: head/usr.sbin/bluetooth/bthidd/bthid_config.h ============================================================================== --- head/usr.sbin/bluetooth/bthidd/bthid_config.h Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/bthid_config.h Mon Apr 30 12:16:54 2018 (r333113) @@ -53,7 +53,11 @@ struct hid_device unsigned battery_power : 1; unsigned normally_connectable : 1; unsigned keyboard : 1; - unsigned reserved : 11; + unsigned mouse : 1; + unsigned has_wheel : 1; + unsigned has_hwheel : 1; + unsigned has_cons : 1; + unsigned reserved : 7; report_desc_t desc; /* HID report descriptor */ LIST_ENTRY(hid_device) next; /* link to the next */ }; Modified: head/usr.sbin/bluetooth/bthidd/bthidd.8 ============================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.8 Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/bthidd.8 Mon Apr 30 12:16:54 2018 (r333113) @@ -25,7 +25,7 @@ .\" $Id: bthidd.8,v 1.1 2006/09/07 21:36:55 max Exp $ .\" $FreeBSD$ .\" -.Dd September 7, 2006 +.Dd April 30, 2018 .Dt BTHIDD 8 .Os .Sh NAME @@ -40,6 +40,7 @@ .Op Fl H Ar file .Op Fl p Ar file .Op Fl t Ar val +.Op Fl u .Sh DESCRIPTION The .Nm @@ -82,6 +83,11 @@ disconnected .Dq passive Bluetooth HID devices and will attempt to establish an outgoing connection. The default rescan interval is 10 seconds. +.It Fl u +Enable support for input event device protocol. +Requires evdev and uinput drivers to be loaded with +.Xr kldload 8 +or compiled into the kernel. .El .Sh KNOWN LIMITATIONS The Modified: head/usr.sbin/bluetooth/bthidd/bthidd.c ============================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.c Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/bthidd.c Mon Apr 30 12:16:54 2018 (r333113) @@ -69,14 +69,15 @@ main(int32_t argc, char *argv[]) struct sigaction sa; char const *pid_file = BTHIDD_PIDFILE; char *ep; - int32_t opt, detach, tval; + int32_t opt, detach, tval, uinput; memset(&srv, 0, sizeof(srv)); memset(&srv.bdaddr, 0, sizeof(srv.bdaddr)); detach = 1; tval = 10; /* sec */ + uinput = 0; - while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) { + while ((opt = getopt(argc, argv, "a:c:dH:hp:t:u")) != -1) { switch (opt) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &srv.bdaddr)) { @@ -111,6 +112,10 @@ main(int32_t argc, char *argv[]) usage(); break; + case 'u': /* enable evdev support */ + uinput = 1; + break; + case 'h': default: usage(); @@ -158,6 +163,8 @@ main(int32_t argc, char *argv[]) server_init(&srv) < 0 || write_pid_file(pid_file) < 0) exit(1); + srv.uinput = uinput; + for (done = 0; !done; ) { if (elapsed(tval)) client_rescan(&srv); @@ -263,6 +270,7 @@ usage(void) " -h display this message\n" \ " -p file specify PID file name\n" \ " -t tval specify client rescan interval (sec)\n" \ +" -u enable evdev protocol support\n" \ "", BTHIDD_IDENT); exit(255); } Modified: head/usr.sbin/bluetooth/bthidd/bthidd.h ============================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.h Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/bthidd.h Mon Apr 30 12:16:54 2018 (r333113) @@ -48,6 +48,7 @@ struct bthid_server int32_t ctrl; /* control channel (listen) */ int32_t intr; /* intr. channel (listen) */ int32_t maxfd; /* max fd in sets */ + int32_t uinput; /* enable evdev support */ fd_set rfdset; /* read descriptor set */ fd_set wfdset; /* write descriptor set */ LIST_HEAD(, bthid_session) sessions; @@ -63,6 +64,10 @@ struct bthid_session int32_t intr; /* interrupt channel */ int32_t vkbd; /* virual keyboard */ void *ctx; /* product specific dev state */ + int32_t ukbd; /* evdev user input */ + int32_t umouse;/* evdev user input */ + int32_t obutt; /* previous mouse buttons */ + int32_t consk; /* last consumer page key */ bdaddr_t bdaddr;/* remote bdaddr */ uint16_t state; /* session state */ #define CLOSED 0 @@ -87,6 +92,7 @@ int32_t client_connect (bthid_server_p srv, int fd) bthid_session_p session_open (bthid_server_p srv, hid_device_p const d); bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr); bthid_session_p session_by_fd (bthid_server_p srv, int32_t fd); +int32_t session_run (bthid_session_p s); void session_close (bthid_session_p s); void hid_initialise (bthid_session_p s); Added: head/usr.sbin/bluetooth/bthidd/btuinput.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/bluetooth/bthidd/btuinput.c Mon Apr 30 12:16:54 2018 (r333113) @@ -0,0 +1,618 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2015-2017 Vladimir Kondratyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#define L2CAP_SOCKET_CHECKED +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bthid_config.h" +#include "bthidd.h" +#include "btuinput.h" + +static int16_t const mbuttons[8] = { + BTN_LEFT, + BTN_MIDDLE, + BTN_RIGHT, + BTN_SIDE, + BTN_EXTRA, + BTN_FORWARD, + BTN_BACK, + BTN_TASK +}; + +static uint16_t const led_codes[3] = { + LED_CAPSL, /* CLKED */ + LED_NUML, /* NLKED */ + LED_SCROLLL, /* SLKED */ +}; + +#define NONE KEY_RESERVED + +static uint16_t const keymap[0x100] = { + /* 0x00 - 0x27 */ + NONE, NONE, NONE, NONE, KEY_A, KEY_B, KEY_C, KEY_D, + KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, + KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, + KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, + KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + /* 0x28 - 0x3f */ + KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, + KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, + KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, + KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, + KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, + KEY_F3, KEY_F4, KEY_F5, KEY_F6, + /* 0x40 - 0x5f */ + KEY_F7, KEY_F8, KEY_F9, KEY_F10, + KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK, + KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, + KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, + KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, + KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, + KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, + KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, + /* 0x60 - 0x7f */ + KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, + KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, + KEY_F13, KEY_F14, KEY_F15, KEY_F16, + KEY_F17, KEY_F18, KEY_F19, KEY_F20, + KEY_F21, KEY_F22, KEY_F23, KEY_F24, + KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, + KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, + KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE, + /* 0x80 - 0x9f */ + KEY_VOLUMEUP, KEY_VOLUMEDOWN, NONE, NONE, + NONE, KEY_KPCOMMA, NONE, KEY_RO, + KEY_KATAKANAHIRAGANA, KEY_YEN,KEY_HENKAN, KEY_MUHENKAN, + KEY_KPJPCOMMA, NONE, NONE, NONE, + KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, + KEY_ZENKAKUHANKAKU, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + /* 0xa0 - 0xbf */ + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + /* 0xc0 - 0xdf */ + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + NONE, NONE, NONE, NONE, + /* 0xe0 - 0xff */ + KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, + KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA, + KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,KEY_NEXTSONG, + KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, + KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, + KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, + KEY_SLEEP, KEY_COFFEE, KEY_REFRESH, KEY_CALC, + NONE, NONE, NONE, NONE, +}; + +/* Consumer page usage mapping */ +static uint16_t const consmap[0x300] = { + [0x030] = KEY_POWER, + [0x031] = KEY_RESTART, + [0x032] = KEY_SLEEP, + [0x034] = KEY_SLEEP, + [0x035] = KEY_KBDILLUMTOGGLE, + [0x036] = BTN_MISC, + [0x040] = KEY_MENU, + [0x041] = KEY_SELECT, + [0x042] = KEY_UP, + [0x043] = KEY_DOWN, + [0x044] = KEY_LEFT, + [0x045] = KEY_RIGHT, + [0x046] = KEY_ESC, + [0x047] = KEY_KPPLUS, + [0x048] = KEY_KPMINUS, + [0x060] = KEY_INFO, + [0x061] = KEY_SUBTITLE, + [0x063] = KEY_VCR, + [0x065] = KEY_CAMERA, + [0x069] = KEY_RED, + [0x06a] = KEY_GREEN, + [0x06b] = KEY_BLUE, + [0x06c] = KEY_YELLOW, + [0x06d] = KEY_ZOOM, + [0x06f] = KEY_BRIGHTNESSUP, + [0x070] = KEY_BRIGHTNESSDOWN, + [0x072] = KEY_BRIGHTNESS_TOGGLE, + [0x073] = KEY_BRIGHTNESS_MIN, + [0x074] = KEY_BRIGHTNESS_MAX, + [0x075] = KEY_BRIGHTNESS_AUTO, + [0x082] = KEY_VIDEO_NEXT, + [0x083] = KEY_LAST, + [0x084] = KEY_ENTER, + [0x088] = KEY_PC, + [0x089] = KEY_TV, + [0x08a] = KEY_WWW, + [0x08b] = KEY_DVD, + [0x08c] = KEY_PHONE, + [0x08d] = KEY_PROGRAM, + [0x08e] = KEY_VIDEOPHONE, + [0x08f] = KEY_GAMES, + [0x090] = KEY_MEMO, + [0x091] = KEY_CD, + [0x092] = KEY_VCR, + [0x093] = KEY_TUNER, + [0x094] = KEY_EXIT, + [0x095] = KEY_HELP, + [0x096] = KEY_TAPE, + [0x097] = KEY_TV2, + [0x098] = KEY_SAT, + [0x09a] = KEY_PVR, + [0x09c] = KEY_CHANNELUP, + [0x09d] = KEY_CHANNELDOWN, + [0x0a0] = KEY_VCR2, + [0x0b0] = KEY_PLAY, + [0x0b1] = KEY_PAUSE, + [0x0b2] = KEY_RECORD, + [0x0b3] = KEY_FASTFORWARD, + [0x0b4] = KEY_REWIND, + [0x0b5] = KEY_NEXTSONG, + [0x0b6] = KEY_PREVIOUSSONG, + [0x0b7] = KEY_STOPCD, + [0x0b8] = KEY_EJECTCD, + [0x0bc] = KEY_MEDIA_REPEAT, + [0x0b9] = KEY_SHUFFLE, + [0x0bf] = KEY_SLOW, + [0x0cd] = KEY_PLAYPAUSE, + [0x0cf] = KEY_VOICECOMMAND, + [0x0e2] = KEY_MUTE, + [0x0e5] = KEY_BASSBOOST, + [0x0e9] = KEY_VOLUMEUP, + [0x0ea] = KEY_VOLUMEDOWN, + [0x0f5] = KEY_SLOW, + [0x181] = KEY_BUTTONCONFIG, + [0x182] = KEY_BOOKMARKS, + [0x183] = KEY_CONFIG, + [0x184] = KEY_WORDPROCESSOR, + [0x185] = KEY_EDITOR, + [0x186] = KEY_SPREADSHEET, + [0x187] = KEY_GRAPHICSEDITOR, + [0x188] = KEY_PRESENTATION, + [0x189] = KEY_DATABASE, + [0x18a] = KEY_MAIL, + [0x18b] = KEY_NEWS, + [0x18c] = KEY_VOICEMAIL, + [0x18d] = KEY_ADDRESSBOOK, + [0x18e] = KEY_CALENDAR, + [0x18f] = KEY_TASKMANAGER, + [0x190] = KEY_JOURNAL, + [0x191] = KEY_FINANCE, + [0x192] = KEY_CALC, + [0x193] = KEY_PLAYER, + [0x194] = KEY_FILE, + [0x196] = KEY_WWW, + [0x199] = KEY_CHAT, + [0x19c] = KEY_LOGOFF, + [0x19e] = KEY_COFFEE, + [0x19f] = KEY_CONTROLPANEL, + [0x1a2] = KEY_APPSELECT, + [0x1a3] = KEY_NEXT, + [0x1a4] = KEY_PREVIOUS, + [0x1a6] = KEY_HELP, + [0x1a7] = KEY_DOCUMENTS, + [0x1ab] = KEY_SPELLCHECK, + [0x1ae] = KEY_KEYBOARD, + [0x1b1] = KEY_SCREENSAVER, + [0x1b4] = KEY_FILE, + [0x1b6] = KEY_IMAGES, + [0x1b7] = KEY_AUDIO, + [0x1b8] = KEY_VIDEO, + [0x1bc] = KEY_MESSENGER, + [0x1bd] = KEY_INFO, + [0x201] = KEY_NEW, + [0x202] = KEY_OPEN, + [0x203] = KEY_CLOSE, + [0x204] = KEY_EXIT, + [0x207] = KEY_SAVE, + [0x208] = KEY_PRINT, + [0x209] = KEY_PROPS, + [0x21a] = KEY_UNDO, + [0x21b] = KEY_COPY, + [0x21c] = KEY_CUT, + [0x21d] = KEY_PASTE, + [0x21f] = KEY_FIND, + [0x221] = KEY_SEARCH, + [0x222] = KEY_GOTO, + [0x223] = KEY_HOMEPAGE, + [0x224] = KEY_BACK, + [0x225] = KEY_FORWARD, + [0x226] = KEY_STOP, + [0x227] = KEY_REFRESH, + [0x22a] = KEY_BOOKMARKS, + [0x22d] = KEY_ZOOMIN, + [0x22e] = KEY_ZOOMOUT, + [0x22f] = KEY_ZOOMRESET, + [0x233] = KEY_SCROLLUP, + [0x234] = KEY_SCROLLDOWN, + [0x23d] = KEY_EDIT, + [0x25f] = KEY_CANCEL, + [0x269] = KEY_INSERT, + [0x26a] = KEY_DELETE, + [0x279] = KEY_REDO, + [0x289] = KEY_REPLY, + [0x28b] = KEY_FORWARDMAIL, + [0x28c] = KEY_SEND, + [0x2c7] = KEY_KBDINPUTASSIST_PREV, + [0x2c8] = KEY_KBDINPUTASSIST_NEXT, + [0x2c9] = KEY_KBDINPUTASSIST_PREVGROUP, + [0x2ca] = KEY_KBDINPUTASSIST_NEXTGROUP, + [0x2cb] = KEY_KBDINPUTASSIST_ACCEPT, + [0x2cc] = KEY_KBDINPUTASSIST_CANCEL, +}; + +static int32_t +uinput_open_common(hid_device_p const p, bdaddr_p local, const uint8_t *name) +{ + struct uinput_setup uisetup; + uint8_t phys[UINPUT_MAX_NAME_SIZE]; + uint8_t uniq[UINPUT_MAX_NAME_SIZE]; + int32_t fd; + + /* Take local and remote bdaddr */ + bt_ntoa(local, phys); + bt_ntoa(&p->bdaddr, uniq); + + /* Take device name from bthidd.conf. Fallback to generic name. */ + if (p->name != NULL) + name = p->name; + + /* Set device name and bus/vendor information */ + memset(&uisetup, 0, sizeof(uisetup)); + snprintf(uisetup.name, UINPUT_MAX_NAME_SIZE, + "%s, bdaddr %s", name, uniq); + uisetup.id.bustype = BUS_BLUETOOTH; + uisetup.id.vendor = p->vendor_id; + uisetup.id.product = p->product_id; + uisetup.id.version = p->version; + + fd = open("/dev/uinput", O_RDWR | O_NONBLOCK); + + if (ioctl(fd, UI_SET_PHYS, phys) < 0 || + ioctl(fd, UI_SET_BSDUNIQ, uniq) < 0 || + ioctl(fd, UI_DEV_SETUP, &uisetup) < 0) + return (-1); + + return (fd); +} + +/* + * Setup uinput device as 8button mouse with wheel(s) + * TODO: bring in more feature detection code from ums + */ +int32_t +uinput_open_mouse(hid_device_p const p, bdaddr_p local) +{ + size_t i; + int32_t fd; + + assert(p != NULL); + + if ((fd = uinput_open_common(p, local, "Bluetooth Mouse")) < 0) + goto bail_out; + + /* Advertise events and axes */ + if (ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_REL) < 0 || + ioctl(fd, UI_SET_RELBIT, REL_X) < 0 || + ioctl(fd, UI_SET_RELBIT, REL_Y) < 0 || + (p->has_wheel && ioctl(fd, UI_SET_RELBIT, REL_WHEEL) < 0) || + (p->has_hwheel && ioctl(fd, UI_SET_RELBIT, REL_HWHEEL) < 0) || + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_POINTER) < 0) + goto bail_out; + + /* Advertise mouse buttons */ + for (i = 0; i < nitems(mbuttons); i++) + if (ioctl(fd, UI_SET_KEYBIT, mbuttons[i]) < 0) + goto bail_out; + + if (ioctl(fd, UI_DEV_CREATE) >= 0) + return (fd); /* SUCCESS */ + +bail_out: + if (fd >= 0) + close(fd); + return (-1); +} + +/* + * Setup uinput keyboard + */ +int32_t +uinput_open_keyboard(hid_device_p const p, bdaddr_p local) +{ + size_t i; + int32_t fd; + + assert(p != NULL); + + if ((fd = uinput_open_common(p, local, "Bluetooth Keyboard")) < 0) + goto bail_out; + + /* Advertise key events and LEDs */ + if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_LED) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 || + ioctl(fd, UI_SET_EVBIT, EV_REP) < 0 || + ioctl(fd, UI_SET_LEDBIT, LED_CAPSL) < 0 || + ioctl(fd, UI_SET_LEDBIT, LED_NUML) < 0 || + ioctl(fd, UI_SET_LEDBIT, LED_SCROLLL)) + goto bail_out; + + /* Advertise keycodes */ + for (i = 0; i < nitems(keymap); i++) + if (keymap[i] != NONE && + ioctl(fd, UI_SET_KEYBIT, keymap[i]) < 0) + goto bail_out; + + /* Advertise consumer page keys if any */ + if (p->has_cons) { + for (i = 0; i < nitems(consmap); i++) { + if (consmap[i] != NONE && + ioctl(fd, UI_SET_KEYBIT, consmap[i]) < 0) + goto bail_out; + } + } + + if (ioctl(fd, UI_DEV_CREATE) >= 0) + return (fd); /* SUCCESS */ + +bail_out: + if (fd >= 0) + close(fd); + return (-1); +} + +/* from sys/dev/evdev/evdev.h */ +#define EVDEV_RCPT_HW_MOUSE (1<<2) +#define EVDEV_RCPT_HW_KBD (1<<3) + +#define MASK_POLL_INTERVAL 5 /* seconds */ +#define MASK_SYSCTL "kern.evdev.rcpt_mask" + +static int32_t +uinput_get_rcpt_mask(void) +{ + static struct timespec last = { 0, 0 }; + struct timespec now; + static int32_t mask = 0; + size_t len; + time_t elapsed; + + if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == -1) + return mask; + + elapsed = now.tv_sec - last.tv_sec; + if (now.tv_nsec < last.tv_nsec) + elapsed--; + + if (elapsed >= MASK_POLL_INTERVAL) { + len = sizeof(mask); + if (sysctlbyname(MASK_SYSCTL, &mask, &len, NULL, 0) < 0) { + if (errno == ENOENT) + /* kernel is compiled w/o EVDEV_SUPPORT */ + mask = EVDEV_RCPT_HW_MOUSE | EVDEV_RCPT_HW_KBD; + else + mask = 0; + } + last = now; + } + return mask; +} + +static int32_t +uinput_write_event(int32_t fd, uint16_t type, uint16_t code, int32_t value) +{ + struct input_event ie; + + assert(fd >= 0); + + memset(&ie, 0, sizeof(ie)); + ie.type = type; + ie.code = code; + ie.value = value; + return (write(fd, &ie, sizeof(ie))); +} + +int32_t +uinput_rep_mouse(int32_t fd, int32_t x, int32_t y, int32_t z, int32_t t, + int32_t buttons, int32_t obuttons) +{ + size_t i; + int32_t rcpt_mask, mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_MOUSE)) + return (0); + + if ((x != 0 && uinput_write_event(fd, EV_REL, REL_X, x) < 0) || + (y != 0 && uinput_write_event(fd, EV_REL, REL_Y, y) < 0) || + (z != 0 && uinput_write_event(fd, EV_REL, REL_WHEEL, -z) < 0) || + (t != 0 && uinput_write_event(fd, EV_REL, REL_HWHEEL, t) < 0)) + return (-1); + + for (i = 0; i < nitems(mbuttons); i++) { + mask = 1 << i; + if ((buttons & mask) == (obuttons & mask)) + continue; + if (uinput_write_event(fd, EV_KEY, mbuttons[i], + (buttons & mask) != 0) < 0) + return (-1); + } + + if (uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) < 0) + return (-1); + + return (0); +} + +/* + * Translate and report keyboard page key events + */ +int32_t +uinput_rep_key(int32_t fd, int32_t key, int32_t make) +{ + int32_t rcpt_mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_KBD)) + return (0); + + if (key >= 0 && key < (int32_t)nitems(keymap) && + keymap[key] != NONE) { + if (uinput_write_event(fd, EV_KEY, keymap[key], make) > 0 && + uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0) + return (0); + } + return (-1); +} + +/* + * Translate and report consumer page key events + */ +int32_t +uinput_rep_cons(int32_t fd, int32_t key, int32_t make) +{ + int32_t rcpt_mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_KBD)) + return (0); + + if (key >= 0 && key < (int32_t)nitems(consmap) && + consmap[key] != NONE) { + if (uinput_write_event(fd, EV_KEY, consmap[key], make) > 0 && + uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0) + return (0); + } + return (-1); +} + +/* + * Translate and report LED events + */ +int32_t +uinput_rep_leds(int32_t fd, int state, int mask) +{ + size_t i; + int32_t rcpt_mask; + + assert(fd >= 0); + + rcpt_mask = uinput_get_rcpt_mask(); + if (!(rcpt_mask & EVDEV_RCPT_HW_KBD)) + return (0); + + for (i = 0; i < nitems(led_codes); i++) { + if (mask & (1 << i) && + uinput_write_event(fd, EV_LED, led_codes[i], + state & (1 << i) ? 1 : 0) < 0) + return (-1); + } + + return (0); +} + +/* + * Process status change from evdev + */ +int32_t +uinput_kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len) +{ + struct input_event ie; + int32_t leds, oleds; + size_t i; + + assert(s != NULL); + assert(s->vkbd >= 0); + assert(len == sizeof(struct input_event)); + + memcpy(&ie, data, sizeof(ie)); + switch (ie.type) { + case EV_LED: + ioctl(s->vkbd, KDGETLED, &oleds); + leds = oleds; + for (i = 0; i < nitems(led_codes); i++) { + if (led_codes[i] == ie.code) { + if (ie.value) + leds |= 1 << i; + else + leds &= ~(1 << i); + if (leds != oleds) + ioctl(s->vkbd, KDSETLED, leds); + break; + } + } + break; + case EV_REP: + /* FALLTHROUGH. Repeats are handled by evdev subsystem */ + default: + break; + } + + return (0); +} Added: head/usr.sbin/bluetooth/bthidd/btuinput.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/bluetooth/bthidd/btuinput.h Mon Apr 30 12:16:54 2018 (r333113) @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2015-2017 Vladimir Kondratyev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _UINPUT_H_ +#define _UINPUT_H_ + +int32_t uinput_open_mouse(hid_device_p const d, bdaddr_p local); +int32_t uinput_open_keyboard(hid_device_p const d, bdaddr_p local); +int32_t uinput_rep_mouse(int32_t fd, int32_t x, int32_t y, int32_t z, + int32_t t, int32_t buttons, int32_t obuttons); +int32_t uinput_rep_key(int32_t fd, int32_t key, int32_t make); +int32_t uinput_rep_cons(int32_t fd, int32_t key, int32_t make); +int32_t uinput_rep_leds(int32_t fd, int state, int mask); +int32_t uinput_kbd_status_changed(bthid_session_p s, uint8_t *data, + int32_t len); + +#endif /* ndef _UINPUT_H_ */ Modified: head/usr.sbin/bluetooth/bthidd/client.c ============================================================================== --- head/usr.sbin/bluetooth/bthidd/client.c Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/client.c Mon Apr 30 12:16:54 2018 (r333113) @@ -188,14 +188,11 @@ client_connect(bthid_server_p srv, int32_t fd) s->state = OPEN; connect_in_progress = 0; - /* Register session's vkbd descriptor (if any) for read */ - if (s->state == OPEN && d->keyboard) { - assert(s->vkbd != -1); - - FD_SET(s->vkbd, &srv->rfdset); - if (s->vkbd > srv->maxfd) - srv->maxfd = s->vkbd; - } + /* Create kbd/mouse after both channels are established */ + if (session_run(s) < 0) { + session_close(s); + return (-1); + } break; default: Modified: head/usr.sbin/bluetooth/bthidd/hid.c ============================================================================== --- head/usr.sbin/bluetooth/bthidd/hid.c Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/hid.c Mon Apr 30 12:16:54 2018 (r333113) @@ -50,6 +50,7 @@ #include #include "bthid_config.h" #include "bthidd.h" +#include "btuinput.h" #include "kbd.h" /* @@ -280,6 +281,19 @@ hid_interrupt(bthid_session_p s, uint8_t *data, int32_ break; case HUP_CONSUMER: + if (hid_device->keyboard && s->srv->uinput) { + if (h.flags & HIO_VARIABLE) { + uinput_rep_cons(s->ukbd, usage, !!val); + } else { + if (s->consk > 0) + uinput_rep_cons(s->ukbd, + s->consk, 0); + if (uinput_rep_cons(s->ukbd, val, 1) + == 0) + s->consk = val; + } + } + if (!val) break; @@ -551,6 +565,14 @@ check_middle_button: syslog(LOG_ERR, "Could not process mouse events from " \ "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); + + if (hid_device->mouse && s->srv->uinput && + uinput_rep_mouse(s->umouse, mouse_x, mouse_y, mouse_z, + mouse_t, mouse_butt, s->obutt) < 0) + syslog(LOG_ERR, "Could not process mouse events from " \ + "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), + strerror(errno), errno); + s->obutt = mouse_butt; } return (0); Modified: head/usr.sbin/bluetooth/bthidd/kbd.c ============================================================================== --- head/usr.sbin/bluetooth/bthidd/kbd.c Mon Apr 30 10:49:29 2018 (r333112) +++ head/usr.sbin/bluetooth/bthidd/kbd.c Mon Apr 30 12:16:54 2018 (r333113) @@ -56,10 +56,12 @@ #include #include "bthid_config.h" #include "bthidd.h" +#include "btuinput.h" #include "kbd.h" static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob); +static void uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); /* * HID code to PS/2 set 1 code translation table. @@ -354,6 +356,7 @@ kbd_process_keys(bthid_session_p s) if (f2 != -1) { /* release old keys */ kbd_write(s->keys2, f2, 0, s->vkbd); + uinput_kbd_write(s->keys2, f2, 0, s->ukbd); memset(s->keys2, 0, bitstr_size(xsize)); } @@ -366,6 +369,7 @@ kbd_process_keys(bthid_session_p s) memcpy(s->keys2, s->keys1, bitstr_size(xsize)); kbd_write(s->keys1, f1, 1, s->vkbd); + uinput_kbd_write(s->keys1, f1, 1, s->ukbd); memset(s->keys1, 0, bitstr_size(xsize)); return (0); @@ -393,12 +397,15 @@ kbd_process_keys(bthid_session_p s) } bit_ffs(diff, xsize, &f2); - if (f2 > 0) + if (f2 > 0) { kbd_write(diff, f2, 0, s->vkbd); + uinput_kbd_write(diff, f2, 0, s->ukbd); + } bit_ffs(s->keys1, xsize, &f1); if (f1 > 0) { kbd_write(s->keys1, f1, 1, s->vkbd); + uinput_kbd_write(s->keys1, f1, 1, s->ukbd); memset(s->keys1, 0, bitstr_size(xsize)); } @@ -407,6 +414,22 @@ kbd_process_keys(bthid_session_p s) /* * Translate given keymap and write keyscodes + */ +void +uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd) +{ + int32_t i; + + if (fd >= 0) { + for (i = fb; i < xsize; i++) { + if (bit_test(m, i)) + uinput_rep_key(fd, i, make); + } + } +} + +/* + * Translate given keymap and write keyscodes */ static void @@ -520,6 +543,7 @@ kbd_status_changed(bthid_session_p s, uint8_t *data, i hid_device_p hid_device; hid_data_t d; hid_item_t h; + uint8_t leds_mask = 0; assert(s != NULL); assert(len == sizeof(vkbd_status_t)); @@ -553,16 +577,19 @@ kbd_status_changed(bthid_session_p s, uint8_t *data, i case 0x01: /* Num Lock LED */ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***