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
This commit is contained in:
parent
e650806966
commit
44af5666d9
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
618
usr.sbin/bluetooth/bthidd/btuinput.c
Normal file
618
usr.sbin/bluetooth/bthidd/btuinput.c
Normal file
@ -0,0 +1,618 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2015-2017 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
* 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 <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <dev/evdev/input.h>
|
||||
#include <dev/evdev/uinput.h>
|
||||
#include <dev/usb/usb.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
|
||||
#include <assert.h>
|
||||
#define L2CAP_SOCKET_CHECKED
|
||||
#include <bluetooth.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <usbhid.h>
|
||||
|
||||
#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);
|
||||
}
|
44
usr.sbin/bluetooth/bthidd/btuinput.h
Normal file
44
usr.sbin/bluetooth/bthidd/btuinput.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2015-2017 Vladimir Kondratyev <wulf@FreeBSD.org>
|
||||
* 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_ */
|
@ -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:
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include <usbhid.h>
|
||||
#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_t len)
|
||||
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 @@ hid_interrupt(bthid_session_p s, uint8_t *data, int32_t len)
|
||||
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);
|
||||
|
@ -56,10 +56,12 @@
|
||||
#include <usbhid.h>
|
||||
#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,18 +397,37 @@ 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));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
@ -520,6 +543,7 @@ kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)
|
||||
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, int32_t len)
|
||||
case 0x01: /* Num Lock LED */
|
||||
if (st.leds & LED_NUM)
|
||||
hid_set_data(&data[1], &h, 1);
|
||||
leds_mask |= LED_NUM;
|
||||
break;
|
||||
|
||||
case 0x02: /* Caps Lock LED */
|
||||
if (st.leds & LED_CAP)
|
||||
hid_set_data(&data[1], &h, 1);
|
||||
leds_mask |= LED_CAP;
|
||||
break;
|
||||
|
||||
case 0x03: /* Scroll Lock LED */
|
||||
if (st.leds & LED_SCR)
|
||||
hid_set_data(&data[1], &h, 1);
|
||||
leds_mask |= LED_SCR;
|
||||
break;
|
||||
|
||||
/* XXX add other LEDs ? */
|
||||
@ -579,6 +606,9 @@ kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)
|
||||
if (found)
|
||||
write(s->intr, data, (report_id != NO_REPORT_ID) ? 3 : 2);
|
||||
|
||||
if (found && s->srv->uinput && hid_device->keyboard)
|
||||
uinput_rep_leds(s->ukbd, st.leds, leds_mask);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -393,7 +393,7 @@ check_hid_device(hid_device_p d)
|
||||
{
|
||||
hid_data_t hd;
|
||||
hid_item_t hi;
|
||||
int32_t page;
|
||||
int32_t page, mdepth;
|
||||
|
||||
if (get_hid_device(&d->bdaddr) != NULL) {
|
||||
SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
|
||||
@ -416,11 +416,23 @@ check_hid_device(hid_device_p d)
|
||||
return (0);
|
||||
}
|
||||
|
||||
mdepth = 0;
|
||||
|
||||
/* XXX somehow need to make sure descriptor is valid */
|
||||
for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
|
||||
switch (hi.kind) {
|
||||
case hid_collection:
|
||||
if (mdepth != 0)
|
||||
mdepth++;
|
||||
else if (hi.collection == 1 &&
|
||||
hi.usage ==
|
||||
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))
|
||||
mdepth++;
|
||||
break;
|
||||
case hid_endcollection:
|
||||
if (mdepth != 0)
|
||||
mdepth--;
|
||||
break;
|
||||
case hid_output:
|
||||
case hid_feature:
|
||||
break;
|
||||
@ -430,6 +442,28 @@ check_hid_device(hid_device_p d)
|
||||
page = HID_PAGE(hi.usage);
|
||||
if (page == HUP_KEYBOARD)
|
||||
d->keyboard = 1;
|
||||
if (page == HUP_CONSUMER &&
|
||||
(hi.flags & (HIO_CONST|HIO_RELATIVE)) == 0)
|
||||
d->has_cons = 1;
|
||||
/* Check if the device may send relative motion events */
|
||||
if (mdepth == 0)
|
||||
break;
|
||||
if (hi.usage ==
|
||||
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) &&
|
||||
(hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
|
||||
d->mouse = 1;
|
||||
if (hi.usage ==
|
||||
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) &&
|
||||
(hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
|
||||
d->mouse = 1;
|
||||
if (hi.usage ==
|
||||
HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL) &&
|
||||
(hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
|
||||
d->has_wheel = 1;
|
||||
if (hi.usage ==
|
||||
HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN) &&
|
||||
(hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE)
|
||||
d->has_hwheel = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <assert.h>
|
||||
#define L2CAP_SOCKET_CHECKED
|
||||
#include <bluetooth.h>
|
||||
#include <dev/evdev/input.h>
|
||||
#include <dev/vkbd/vkbd_var.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@ -48,6 +49,7 @@
|
||||
#include <usbhid.h>
|
||||
#include "bthid_config.h"
|
||||
#include "bthidd.h"
|
||||
#include "btuinput.h"
|
||||
#include "kbd.h"
|
||||
|
||||
#undef max
|
||||
@ -282,19 +284,12 @@ server_accept(bthid_server_p srv, int32_t fd)
|
||||
(fd == srv->ctrl)? "control" : "interrupt",
|
||||
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
|
||||
|
||||
/* Register session's vkbd descriptor (if needed) 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 virtual kbd/mouse after both channels are established */
|
||||
if (s->state == OPEN && session_run(s) < 0) {
|
||||
session_close(s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Pass device for probing after both channels are established */
|
||||
if (s->state == OPEN)
|
||||
hid_initialise(s);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -309,9 +304,10 @@ server_process(bthid_server_p srv, int32_t fd)
|
||||
int32_t len, to_read;
|
||||
int32_t (*cb)(bthid_session_p, uint8_t *, int32_t);
|
||||
union {
|
||||
uint8_t b[1024];
|
||||
vkbd_status_t s;
|
||||
} data;
|
||||
uint8_t b[1024];
|
||||
vkbd_status_t s;
|
||||
struct input_event ie;
|
||||
} data;
|
||||
|
||||
if (s == NULL)
|
||||
return (0); /* can happen on device disconnect */
|
||||
@ -323,6 +319,9 @@ server_process(bthid_server_p srv, int32_t fd)
|
||||
} else if (fd == s->intr) {
|
||||
cb = hid_interrupt;
|
||||
to_read = sizeof(data.b);
|
||||
} else if (fd == s->ukbd) {
|
||||
cb = uinput_kbd_status_changed;
|
||||
to_read = sizeof(data.ie);
|
||||
} else {
|
||||
assert(fd == s->vkbd);
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <usbhid.h>
|
||||
#include "bthid_config.h"
|
||||
#include "bthidd.h"
|
||||
#include "btuinput.h"
|
||||
#include "kbd.h"
|
||||
|
||||
/*
|
||||
@ -68,22 +69,12 @@ session_open(bthid_server_p srv, hid_device_p const d)
|
||||
memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr));
|
||||
s->ctrl = -1;
|
||||
s->intr = -1;
|
||||
s->vkbd = -1;
|
||||
s->ctx = NULL;
|
||||
|
||||
if (d->keyboard) {
|
||||
/* Open /dev/vkbdctl */
|
||||
s->vkbd = open("/dev/vkbdctl", O_RDWR);
|
||||
if (s->vkbd < 0) {
|
||||
syslog(LOG_ERR, "Could not open /dev/vkbdctl " \
|
||||
"for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
|
||||
strerror(errno), errno);
|
||||
free(s);
|
||||
return (NULL);
|
||||
}
|
||||
} else
|
||||
s->vkbd = -1;
|
||||
|
||||
s->state = CLOSED;
|
||||
s->ukbd = -1;
|
||||
s->umouse = -1;
|
||||
s->obutt = 0;
|
||||
|
||||
s->keys1 = bit_alloc(kbd_maxkey());
|
||||
if (s->keys1 == NULL) {
|
||||
@ -103,6 +94,64 @@ session_open(bthid_server_p srv, hid_device_p const d)
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize virtual keyboard and mouse after both channels are established
|
||||
*/
|
||||
|
||||
int32_t
|
||||
session_run(bthid_session_p s)
|
||||
{
|
||||
hid_device_p d = get_hid_device(&s->bdaddr);
|
||||
struct sockaddr_l2cap local;
|
||||
socklen_t len;
|
||||
|
||||
if (d->keyboard) {
|
||||
/* Open /dev/vkbdctl */
|
||||
s->vkbd = open("/dev/vkbdctl", O_RDWR);
|
||||
if (s->vkbd < 0) {
|
||||
syslog(LOG_ERR, "Could not open /dev/vkbdctl " \
|
||||
"for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
|
||||
strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
/* Register session's vkbd descriptor (if needed) for read */
|
||||
FD_SET(s->vkbd, &s->srv->rfdset);
|
||||
if (s->vkbd > s->srv->maxfd)
|
||||
s->srv->maxfd = s->vkbd;
|
||||
}
|
||||
|
||||
/* Pass device for probing */
|
||||
hid_initialise(s);
|
||||
|
||||
/* Take local bdaddr */
|
||||
len = sizeof(local);
|
||||
getsockname(s->ctrl, (struct sockaddr *) &local, &len);
|
||||
|
||||
if (d->mouse && s->srv->uinput) {
|
||||
s->umouse = uinput_open_mouse(d, &local.l2cap_bdaddr);
|
||||
if (s->umouse < 0) {
|
||||
syslog(LOG_ERR, "Could not open /dev/uinput " \
|
||||
"for %s. %s (%d)", bt_ntoa(&s->bdaddr,
|
||||
NULL), strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
if (d->keyboard && s->srv->uinput) {
|
||||
s->ukbd = uinput_open_keyboard(d, &local.l2cap_bdaddr);
|
||||
if (s->ukbd < 0) {
|
||||
syslog(LOG_ERR, "Could not open /dev/uinput " \
|
||||
"for %s. %s (%d)", bt_ntoa(&s->bdaddr,
|
||||
NULL), strerror(errno), errno);
|
||||
return (-1);
|
||||
}
|
||||
/* Register session's ukbd descriptor (if needed) for read */
|
||||
FD_SET(s->ukbd, &s->srv->rfdset);
|
||||
if (s->ukbd > s->srv->maxfd)
|
||||
s->srv->maxfd = s->ukbd;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup session by bdaddr
|
||||
*/
|
||||
@ -135,7 +184,8 @@ session_by_fd(bthid_server_p srv, int32_t fd)
|
||||
assert(fd >= 0);
|
||||
|
||||
LIST_FOREACH(s, &srv->sessions, next)
|
||||
if (s->ctrl == fd || s->intr == fd || s->vkbd == fd)
|
||||
if (s->ctrl == fd || s->intr == fd ||
|
||||
s->vkbd == fd || s->ukbd == fd)
|
||||
break;
|
||||
|
||||
return (s);
|
||||
@ -179,6 +229,17 @@ session_close(bthid_session_p s)
|
||||
s->srv->maxfd --;
|
||||
}
|
||||
|
||||
if (s->umouse != -1)
|
||||
close(s->umouse);
|
||||
|
||||
if (s->ukbd != -1) {
|
||||
FD_CLR(s->ukbd, &s->srv->rfdset);
|
||||
close(s->ukbd);
|
||||
|
||||
if (s->srv->maxfd == s->ukbd)
|
||||
s->srv->maxfd --;
|
||||
}
|
||||
|
||||
free(s->ctx);
|
||||
free(s->keys1);
|
||||
free(s->keys2);
|
||||
|
Loading…
Reference in New Issue
Block a user