Page MenuHomeFreeBSD

D13456.1775846517.diff
No OneTemporary

Size
45 KB
Referenced Files
None
Subscribers
None

D13456.1775846517.diff

Index: etc/defaults/rc.conf
===================================================================
--- etc/defaults/rc.conf
+++ etc/defaults/rc.conf
@@ -430,6 +430,7 @@
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
Index: etc/rc.d/bthidd
===================================================================
--- etc/rc.d/bthidd
+++ etc/rc.d/bthidd
@@ -17,8 +17,25 @@
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 @@
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"
Index: lib/libsdp/sdp.h
===================================================================
--- lib/libsdp/sdp.h
+++ lib/libsdp/sdp.h
@@ -530,6 +530,7 @@
void * sdp_open_local (char const *control);
int32_t sdp_close (void *xs);
int32_t sdp_error (void *xs);
+int32_t sdp_get_socket (void *xs);
int32_t sdp_search (void *xs,
uint32_t plen, uint16_t const *pp,
Index: lib/libsdp/sdp.3
===================================================================
--- lib/libsdp/sdp.3
+++ lib/libsdp/sdp.3
@@ -25,7 +25,7 @@
.\" $Id: sdp.3,v 1.1 2003/09/07 20:34:19 max Exp $
.\" $FreeBSD$
.\"
-.Dd May 27, 2005
+.Dd December 19, 2017
.Dt SDP 3
.Os
.Sh NAME
@@ -45,6 +45,7 @@
.Nm sdp_open_local ,
.Nm sdp_close ,
.Nm sdp_error ,
+.Nm sdp_get_socket ,
.Nm sdp_search ,
.Nm sdp_attr2desc ,
.Nm sdp_uuid2desc
@@ -75,6 +76,8 @@
.Ft int32_t
.Fn sdp_error "void *xs"
.Ft int32_t
+.Fn sdp_get_socket "void *xs"
+.Ft int32_t
.Fo sdp_search
.Fa "void *xs" "uint32_t plen" "uint16_t const *pp" "uint32_t alen"
.Fa "uint32_t const *ap" "uint32_t vlen" "sdp_attr_t *vp"
@@ -188,6 +191,16 @@
function.
.Pp
The
+.Fn sdp_get_socket
+function returns SDP session socket handle.
+The
+.Fa xs
+parameter should point to a valid SDP session object created with
+.Fn sdp_open
+or
+.Fn sdp_open_local .
+.Pp
+The
.Fn sdp_search
function is used to perform SDP Service Search Attribute Request.
The
Index: lib/libsdp/session.c
===================================================================
--- lib/libsdp/session.c
+++ lib/libsdp/session.c
@@ -178,3 +178,11 @@
return ((ss != NULL)? ss->error : EINVAL);
}
+
+int32_t
+sdp_get_socket(void *xss)
+{
+ sdp_session_p ss = (sdp_session_p) xss;
+
+ return (ss->s);
+}
Index: sys/dev/evdev/uinput.h
===================================================================
--- sys/dev/evdev/uinput.h
+++ sys/dev/evdev/uinput.h
@@ -90,6 +90,13 @@
#define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase)
#define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase)
+/*
+ * FreeBSD specific. Set unique identifier of input device.
+ * Name and magic are chosen to reduce chances of clashing
+ * with possible future Linux extensions.
+ */
+#define UI_SET_BSDUNIQ _IO(UINPUT_IOCTL_BASE, 109)
+
#define EV_UINPUT 0x0101
#define UI_FF_UPLOAD 1
#define UI_FF_ERASE 2
Index: sys/dev/evdev/uinput.c
===================================================================
--- sys/dev/evdev/uinput.c
+++ sys/dev/evdev/uinput.c
@@ -604,6 +604,15 @@
evdev_set_phys(state->ucs_evdev, buf);
return (0);
+ case UI_SET_BSDUNIQ:
+ if (state->ucs_state == UINPUT_RUNNING)
+ return (EINVAL);
+ ret = copyinstr(*(void **)data, buf, sizeof(buf), NULL);
+ if (ret != 0)
+ return (ret);
+ evdev_set_serial(state->ucs_evdev, buf);
+ return (0);
+
case UI_SET_SWBIT:
if (state->ucs_state == UINPUT_RUNNING ||
intdata > SW_MAX || intdata < 0)
Index: usr.sbin/bluetooth/bthidcontrol/Makefile
===================================================================
--- usr.sbin/bluetooth/bthidcontrol/Makefile
+++ usr.sbin/bluetooth/bthidcontrol/Makefile
@@ -7,7 +7,7 @@
MAN= bthidcontrol.8
SRCS= bthidcontrol.c hid.c lexer.l parser.y sdp.c
WARNS?= 1
-CFLAGS+= -DBTHIDCONTROL=1 -I${.CURDIR:H}/bthidd
+CFLAGS+= -DBTHIDCONTROL=1 -I${.CURDIR:H}/bthidd -I${SRCTOP}/lib/libsdp
LIBADD+= bluetooth sdp usbhid
Index: usr.sbin/bluetooth/bthidcontrol/sdp.c
===================================================================
--- usr.sbin/bluetooth/bthidcontrol/sdp.c
+++ usr.sbin/bluetooth/bthidcontrol/sdp.c
@@ -29,7 +29,9 @@
* $FreeBSD$
*/
+#include <sys/types.h>
#include <sys/queue.h>
+#include <sys/sysctl.h>
#define L2CAP_SOCKET_CHECKED
#include <bluetooth.h>
#include <dev/usb/usb.h>
@@ -109,16 +111,78 @@
}
}
+static int
+bt_devany_cb(int s, struct bt_devinfo const *di, void *xdevname)
+{
+ strlcpy((char *) xdevname, di->devname, HCI_DEVNAME_SIZE);
+ return (1);
+}
+
+static char *
+hid_hci_remote_name_query(bdaddr_t *local, bdaddr_t *remote)
+{
+ struct bt_devreq r;
+ ng_hci_remote_name_req_cp cp;
+ ng_hci_remote_name_req_compl_ep ep;
+ int s, to;
+ size_t to_size;
+ char *remote_name = NULL;
+ char devname[HCI_DEVNAME_SIZE];
+
+ to_size = sizeof(to);
+ if (sysctlbyname("net.bluetooth.hci.command_timeout",
+ &to, &to_size, NULL, 0) < 0)
+ goto bail_out;
+ to++;
+
+ memset(devname, 0, sizeof(devname));
+ /* Try to find HCI by local bdaddr */
+ if (local == NULL || local == NG_HCI_BDADDR_ANY ||
+ bt_devname(devname, local) != 0)
+ /* Or scan through all BT devices for first HCI available */
+ if (bt_devenum(bt_devany_cb, devname) <= 0)
+ goto bail_out;
+
+ memset(&r, 0, sizeof(r));
+ memset(&cp, 0, sizeof(cp));
+ memset(&ep, 0, sizeof(ep));
+ cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0;
+ cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE;
+ bdaddr_copy(&cp.bdaddr, remote);
+ r.opcode = NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_REMOTE_NAME_REQ);
+ r.event = NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL;
+ r.cparam = &cp;
+ r.clen = sizeof(cp);
+ r.rparam = &ep;
+ r.rlen = sizeof(ep);
+
+ s = bt_devopen(devname);
+ if (s < 0)
+ goto bail_out;
+
+ /* send request and expect status response */
+ if (bt_devreq(s, &r, to) == 0 || ep.status == 0x00)
+ remote_name = strndup((const char *)&ep.name, sizeof(ep.name));
+
+ bt_devclose(s);
+bail_out:
+ return (remote_name);
+}
+
static int32_t
hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
{
- void *ss = NULL;
- uint8_t *hid_descriptor = NULL, *v;
- int32_t i, control_psm = -1, interrupt_psm = -1,
- reconnect_initiate = -1,
- normally_connectable = 0, battery_power = 0,
- hid_descriptor_length = -1, type;
- int16_t vendor_id = 0, product_id = 0, version = 0;
+ void *ss = NULL;
+ uint8_t *hid_descriptor = NULL, *v;
+ int32_t i, control_psm = -1, interrupt_psm = -1,
+ reconnect_initiate = -1,
+ normally_connectable = 0, battery_power = 0,
+ hid_descriptor_length = -1, type;
+ int16_t vendor_id = 0, product_id = 0, version = 0;
+ struct sockaddr_l2cap salocal;
+ socklen_t salen;
+ bdaddr_t *sdp_local;
if (local == NULL)
local = NG_HCI_BDADDR_ANY;
@@ -173,6 +237,14 @@
if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
hid_sdp_query_exit(sdp_error(ss));
+ /* Try extract HCI bdaddr from opened SDP session */
+ salen = sizeof(salocal);
+ if (getsockname(sdp_get_socket(ss),
+ (struct sockaddr *)&salocal, &salen) == 0)
+ sdp_local = &salocal.l2cap_bdaddr;
+ else
+ sdp_local = NG_HCI_BDADDR_ANY;
+
sdp_close(ss);
ss = NULL;
@@ -210,6 +282,7 @@
reconnect_initiate == -1 ||
hid_descriptor == NULL || hid_descriptor_length == -1)
hid_sdp_query_exit(ENOATTR);
+ hd->name = hid_hci_remote_name_query(sdp_local, &hd->bdaddr);
hd->vendor_id = vendor_id;
hd->product_id = product_id;
hd->version = version;
Index: usr.sbin/bluetooth/bthidd/Makefile
===================================================================
--- usr.sbin/bluetooth/bthidd/Makefile
+++ usr.sbin/bluetooth/bthidd/Makefile
@@ -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}
Index: usr.sbin/bluetooth/bthidd/bthid_config.h
===================================================================
--- usr.sbin/bluetooth/bthidd/bthid_config.h
+++ usr.sbin/bluetooth/bthidd/bthid_config.h
@@ -40,6 +40,7 @@
struct hid_device
{
bdaddr_t bdaddr; /* HID device BDADDR */
+ char * name; /* HID device name */
uint16_t control_psm; /* control PSM */
uint16_t interrupt_psm; /* interrupt PSM */
uint16_t vendor_id; /* primary vendor id */
@@ -50,7 +51,11 @@
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 */
};
Index: usr.sbin/bluetooth/bthidd/bthidd.h
===================================================================
--- usr.sbin/bluetooth/bthidd/bthidd.h
+++ usr.sbin/bluetooth/bthidd/bthidd.h
@@ -46,6 +46,7 @@
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;
@@ -61,6 +62,10 @@
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
@@ -85,6 +90,7 @@
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);
Index: usr.sbin/bluetooth/bthidd/bthidd.8
===================================================================
--- usr.sbin/bluetooth/bthidd/bthidd.8
+++ usr.sbin/bluetooth/bthidd/bthidd.8
@@ -25,7 +25,7 @@
.\" $Id: bthidd.8,v 1.1 2006/09/07 21:36:55 max Exp $
.\" $FreeBSD$
.\"
-.Dd September 7, 2006
+.Dd December 19, 2017
.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,9 @@
.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 kld-loaded or compiled into the kernel.
.El
.Sh KNOWN LIMITATIONS
The
Index: usr.sbin/bluetooth/bthidd/bthidd.c
===================================================================
--- usr.sbin/bluetooth/bthidd/bthidd.c
+++ usr.sbin/bluetooth/bthidd/bthidd.c
@@ -67,14 +67,15 @@
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)) {
@@ -109,6 +110,10 @@
usage();
break;
+ case 'u': /* enable evdev support */
+ uinput = 1;
+ break;
+
case 'h':
default:
usage();
@@ -156,6 +161,8 @@
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);
@@ -261,6 +268,7 @@
" -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);
}
Index: usr.sbin/bluetooth/bthidd/bthidd.conf.sample
===================================================================
--- usr.sbin/bluetooth/bthidd/bthidd.conf.sample
+++ usr.sbin/bluetooth/bthidd/bthidd.conf.sample
@@ -2,6 +2,7 @@
device {
bdaddr 00:50:f2:e5:68:84;
+ name "Bluetooth Mouse";
vendor_id 0x0000;
product_id 0x0000;
version 0x0000;
@@ -27,6 +28,7 @@
device {
bdaddr 00:50:f2:e3:fb:e1;
+ name "Bluetooth Keyboard";
vendor_id 0x0000;
product_id 0x0000;
version 0x0000;
Index: usr.sbin/bluetooth/bthidd/btuinput.h
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/bthidd/btuinput.h
@@ -0,0 +1,42 @@
+/*-
+ * 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_ */
Index: usr.sbin/bluetooth/bthidd/btuinput.c
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/bthidd/btuinput.c
@@ -0,0 +1,623 @@
+/*-
+ * 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];
+ struct hostent *hp;
+ int32_t fd;
+
+ /* Take local and remote bdaddr */
+ bt_ntoa(local, phys);
+ bt_ntoa(&p->bdaddr, uniq);
+
+ /*
+ * Take device name from bthidd.conf than fallback to BT hostname
+ * found in /etc/bluetooth/hosts and after all fallback to generic
+ * name provided by caller.
+ */
+ if (p->name != NULL)
+ name = p->name;
+ else if ((hp = bt_gethostbyaddr((const char *)&p->bdaddr,
+ sizeof(p->bdaddr), AF_BLUETOOTH)) != NULL)
+ name = hp->h_name;
+
+ /* Set device name and bus/vendor information */
+ memset(&uisetup, 0, sizeof(uisetup));
+ snprintf(uisetup.name, UINPUT_MAX_NAME_SIZE, "%s", name);
+ 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:
+ /* Repeats are handled by evdev/uinput subsystem */
+ break;
+ }
+
+ return (0);
+}
Index: usr.sbin/bluetooth/bthidd/client.c
===================================================================
--- usr.sbin/bluetooth/bthidd/client.c
+++ usr.sbin/bluetooth/bthidd/client.c
@@ -186,14 +186,11 @@
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:
Index: usr.sbin/bluetooth/bthidd/hid.c
===================================================================
--- usr.sbin/bluetooth/bthidd/hid.c
+++ usr.sbin/bluetooth/bthidd/hid.c
@@ -48,6 +48,7 @@
#include <usbhid.h>
#include "bthid_config.h"
#include "bthidd.h"
+#include "btuinput.h"
#include "kbd.h"
/*
@@ -278,6 +279,19 @@
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;
@@ -549,6 +563,14 @@
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);
Index: usr.sbin/bluetooth/bthidd/kbd.c
===================================================================
--- usr.sbin/bluetooth/bthidd/kbd.c
+++ usr.sbin/bluetooth/bthidd/kbd.c
@@ -54,10 +54,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.
@@ -352,6 +354,7 @@
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));
}
@@ -364,6 +367,7 @@
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);
@@ -391,18 +395,37 @@
}
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
*/
@@ -518,6 +541,7 @@
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));
@@ -551,16 +575,19 @@
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 ? */
@@ -577,6 +604,9 @@
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);
}
Index: usr.sbin/bluetooth/bthidd/lexer.l
===================================================================
--- usr.sbin/bluetooth/bthidd/lexer.l
+++ usr.sbin/bluetooth/bthidd/lexer.l
@@ -54,6 +54,7 @@
device_word device
bdaddr_word bdaddr
+name_word name
vendor_id_word vendor_id
product_id_word product_id
version_word version
@@ -69,6 +70,7 @@
bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}
hexbytestring 0x{hexbyte}
hexwordstring 0x{hexword}
+string \".+\"
%%
@@ -83,6 +85,7 @@
{device_word} return (T_DEVICE);
{bdaddr_word} return (T_BDADDR);
+{name_word} return (T_NAME);
{vendor_id_word} return (T_VENDOR_ID);
{product_id_word} return (T_PRODUCT_ID);
{version_word} return (T_VERSION);
@@ -116,6 +119,12 @@
return (*ep == '\0'? T_HEXWORD : T_ERROR);
}
+{string} {
+ yytext[strlen(yytext) - 1] = 0;
+ yylval.string = &yytext[1];
+ return (T_STRING);
+ }
+
. return (T_ERROR);
%%
Index: usr.sbin/bluetooth/bthidd/parser.y
===================================================================
--- usr.sbin/bluetooth/bthidd/parser.y
+++ usr.sbin/bluetooth/bthidd/parser.y
@@ -61,6 +61,8 @@
#define EOL "\n"
#endif /* ndef BTHIDCONTROL */
+#define NAMELESS_DEVICE "No Name"
+
#include "bthid_config.h"
int yylex (void);
@@ -83,11 +85,14 @@
%union {
bdaddr_t bdaddr;
int32_t num;
+ char *string;
}
%token <bdaddr> T_BDADDRSTRING
%token <num> T_HEXBYTE
%token <num> T_HEXWORD
+%token <string> T_STRING
+%token T_NAME
%token T_DEVICE T_BDADDR T_VENDOR_ID T_PRODUCT_ID T_VERSION T_CONTROL_PSM
%token T_INTERRUPT_PSM T_RECONNECT_INITIATE T_BATTERY_POWER
%token T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
@@ -126,6 +131,7 @@
;
option: bdaddr
+ | name
| vendor_id
| product_id
| version
@@ -144,6 +150,24 @@
}
;
+name: T_NAME T_STRING
+ {
+ if (hid_device->name != NULL) {
+ free(hid_device->name);
+ hid_device->name = NULL;
+ }
+
+ if (strcmp($2, NAMELESS_DEVICE)) {
+ hid_device->name = strdup($2);
+ if (hid_device->name == NULL) {
+ SYSLOG(LOGCRIT, "Could not allocate new " \
+ "device name" EOL);
+ YYABORT;
+ }
+ }
+ }
+ ;
+
vendor_id: T_VENDOR_ID T_HEXWORD
{
hid_device->vendor_id = $2;
@@ -330,6 +354,7 @@
fprintf(f,
"device {\n" \
" bdaddr %s;\n" \
+" name \"%s\";\n" \
" vendor_id 0x%04x;\n" \
" product_id 0x%04x;\n" \
" version 0x%04x;\n" \
@@ -340,6 +365,7 @@
" normally_connectable %s;\n" \
" hid_descriptor {",
bt_ntoa(&d->bdaddr, NULL),
+ (d->name != NULL)? d->name : NAMELESS_DEVICE,
d->vendor_id, d->product_id, d->version,
d->control_psm, d->interrupt_psm,
d->reconnect_initiate? "true" : "false",
@@ -365,7 +391,7 @@
{
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,
@@ -388,11 +414,23 @@
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;
@@ -402,6 +440,28 @@
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;
}
}
@@ -417,6 +477,7 @@
if (d->desc != NULL)
hid_dispose_report_desc(d->desc);
+ free(d->name);
memset(d, 0, sizeof(*d));
free(d);
}
Index: usr.sbin/bluetooth/bthidd/server.c
===================================================================
--- usr.sbin/bluetooth/bthidd/server.c
+++ usr.sbin/bluetooth/bthidd/server.c
@@ -35,6 +35,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>
@@ -46,6 +47,7 @@
#include <usbhid.h>
#include "bthid_config.h"
#include "bthidd.h"
+#include "btuinput.h"
#include "kbd.h"
#undef max
@@ -280,19 +282,12 @@
(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);
}
@@ -307,9 +302,10 @@
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 */
@@ -321,6 +317,9 @@
} 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);
Index: usr.sbin/bluetooth/bthidd/session.c
===================================================================
--- usr.sbin/bluetooth/bthidd/session.c
+++ usr.sbin/bluetooth/bthidd/session.c
@@ -45,6 +45,7 @@
#include <usbhid.h>
#include "bthid_config.h"
#include "bthidd.h"
+#include "btuinput.h"
#include "kbd.h"
/*
@@ -66,22 +67,12 @@
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) {
@@ -101,6 +92,64 @@
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
*/
@@ -133,7 +182,8 @@
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);
@@ -177,6 +227,17 @@
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);

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 10, 6:41 PM (9 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28318527
Default Alt Text
D13456.1775846517.diff (45 KB)

Event Timeline