Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153495954
D13456.id40181.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
50 KB
Referenced Files
None
Subscribers
None
D13456.id40181.diff
View Options
Index: etc/defaults/rc.conf
===================================================================
--- etc/defaults/rc.conf
+++ etc/defaults/rc.conf
@@ -433,6 +433,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/libbluetooth/bluetooth.h
===================================================================
--- lib/libbluetooth/bluetooth.h
+++ lib/libbluetooth/bluetooth.h
@@ -182,9 +182,19 @@
int bt_devfilter_evt_tst(struct bt_devfilter const *filter, uint8_t event);
int bt_devinquiry(char const *devname, time_t length, int num_rsp,
struct bt_devinquiry **ii);
+char * bt_devremote_name(char const *devname, const bdaddr_t *remote,
+ time_t to, uint16_t clk_off,
+ uint8_t ps_rep_mode, uint8_t ps_mode);
int bt_devinfo (struct bt_devinfo *di);
int bt_devenum (bt_devenum_cb_t cb, void *arg);
+static __inline char *
+bt_devremote_name_gen(char const *devname, const bdaddr_t *remote)
+{
+ return (bt_devremote_name(devname, remote, 0, 0x0000,
+ NG_HCI_SCAN_REP_MODE0, NG_HCI_MANDATORY_PAGE_SCAN_MODE));
+}
+
/*
* bdaddr utility functions (from NetBSD)
*/
Index: lib/libbluetooth/bluetooth.3
===================================================================
--- lib/libbluetooth/bluetooth.3
+++ lib/libbluetooth/bluetooth.3
@@ -25,7 +25,7 @@
.\" $Id: bluetooth.3,v 1.5 2003/05/20 23:04:30 max Exp $
.\" $FreeBSD$
.\"
-.Dd April 9, 2009
+.Dd January 28, 2018
.Dt BLUETOOTH 3
.Os
.Sh NAME
@@ -58,6 +58,8 @@
.Nm bt_devfilter_evt_clr ,
.Nm bt_devfilter_evt_tst ,
.Nm bt_devinquiry ,
+.Nm bt_devremote_name ,
+.Nm bt_devremote_name_gen ,
.Nm bdaddr_same ,
.Nm bdaddr_any ,
.Nm bdaddr_copy
@@ -126,6 +128,11 @@
.Fn bt_devfilter_evt_tst "struct bt_devfilter const *filter" "uint8_t event"
.Ft int
.Fn bt_devinquiry "char const *devname" "time_t length" "int num_rsp" "struct bt_devinquiry **ii"
+.Ft char *
+.Fn bt_devremote_name "char const *devname" "const bdaddr_t *remote" \
+"time_t to" "uint16_t clk_off" "uint8_t ps_rep_mode" "uint8_t ps_mode"
+.Ft char *
+.Fn bt_devremote_name_gen "char const *devname" "const bdaddr_t *remote"
.Ft int
.Fn bdaddr_same "const bdaddr_t *a" "const bdaddr_t *b"
.Ft int
@@ -589,8 +596,54 @@
.Ed
.Pp
The
+.Fn bt_devremote_name
+function performs Bluetooth Remote Name Request procedure to obtain the
+user-friendly name of another Bluetooth unit.
+The
+.Fa devname
+parameter specifies which local Bluetooth device should perform the request.
+If not specified
+.Dv ( NULL ) ,
+the first available device is used.
+The
+.Fa remote
+parameter specifies the Bluetooth BD_ADDR of the remote device to query.
+The
+.Fa to
+parameter specifies response timeout in seconds.
+If not specified (0), the default value is taken from the
+net.bluetooth.hci.command_timeout
+.Xr sysctl 8
+value.
+The
+.Fa clk_off ,
+.Fa ps_rep_mode ,
+and
+.Fa ps_mode
+parameters specify Clock_Offset, Page_Scan_Repetition_Mode, and Page_Scan_Mode
+fields of HCI_Remote_Name_Request respectively.
+On success, the function returns a pointer to dynamically allocated
+NUL-terminated string or
+.Dv NULL
+if an error occurred.
+It is up to the caller to release returned string using
+.Xr free 3 .
+.Pp
+The
+.Fn bt_devremote_name_gen
+function is a shortcut to
+.Fn bt_devremote_name
+that passes generic defaults for
+.Fa to ,
+.Fa clk_off ,
+.Fa ps_rep_mode ,
+and
+.Fa ps_mode
+parameters.
+.Pp
+The
.Fn bdaddr_same ,
-.Fn bdaddr_any
+.Fn bdaddr_any ,
and
.Fn bdaddr_copy
are handy shorthand Bluetooth address utility functions.
Index: lib/libbluetooth/hci.c
===================================================================
--- lib/libbluetooth/hci.c
+++ lib/libbluetooth/hci.c
@@ -32,6 +32,9 @@
* $FreeBSD$
*/
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
#include <assert.h>
#define L2CAP_SOCKET_CHECKED
#include <bluetooth.h>
@@ -39,6 +42,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#undef MIN
@@ -46,6 +50,7 @@
static int bt_devany_cb(int s, struct bt_devinfo const *di, void *xdevname);
static char * bt_dev2node (char const *devname, char *nodename, int nnlen);
+static time_t bt_get_default_hci_command_timeout(void);
int
bt_devopen(char const *devname)
@@ -534,6 +539,63 @@
return (i - *ii);
}
+char *
+bt_devremote_name(char const *devname, const bdaddr_t *remote, time_t to,
+ uint16_t clk_off, uint8_t ps_rep_mode, uint8_t ps_mode)
+{
+ char _devname[HCI_DEVNAME_SIZE];
+ struct bt_devreq r;
+ ng_hci_remote_name_req_cp cp;
+ ng_hci_remote_name_req_compl_ep ep;
+ int s;
+ char *remote_name = NULL;
+
+ if (remote == NULL || to < 0) {
+ errno = EINVAL;
+ goto out;
+ }
+
+ if (to == 0) {
+ to = bt_get_default_hci_command_timeout();
+ if (to < 0)
+ goto out;
+ }
+ to++;
+
+ if (devname == NULL) {
+ memset(_devname, 0, sizeof(_devname));
+ devname = _devname;
+ if (bt_devenum(bt_devany_cb, _devname) <= 0)
+ goto out;
+ }
+
+ memset(&r, 0, sizeof(r));
+ memset(&cp, 0, sizeof(cp));
+ memset(&ep, 0, sizeof(ep));
+ cp.clock_offset = htole16(clk_off);
+ cp.page_scan_rep_mode = ps_rep_mode;
+ cp.page_scan_mode = ps_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 out;
+
+ if (bt_devreq(s, &r, to) == 0 || ep.status == 0x00)
+ remote_name = strndup((const char *)&ep.name, sizeof(ep.name));
+
+ bt_devclose(s);
+out:
+ return (remote_name);
+}
+
int
bt_devinfo(struct bt_devinfo *di)
{
@@ -735,3 +797,21 @@
return (NULL);
}
+static time_t
+bt_get_default_hci_command_timeout(void)
+{
+ int to;
+ size_t to_size = sizeof(to);
+
+ if (sysctlbyname("net.bluetooth.hci.command_timeout",
+ &to, &to_size, NULL, 0) < 0)
+ return (-1);
+
+ /* Should not happen */
+ if (to <= 0) {
+ errno = ERANGE;
+ return (-1);
+ }
+
+ return ((time_t)to);
+}
Index: lib/libsdp/sdp.h
===================================================================
--- lib/libsdp/sdp.h
+++ lib/libsdp/sdp.h
@@ -532,6 +532,7 @@
void * sdp_open_local (char const *control);
int32_t sdp_close (void *xs);
int32_t sdp_error (void *xs);
+int32_t sdp_get_lcaddr (void *xs, bdaddr_t *l);
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 January 28, 2018
.Dt SDP 3
.Os
.Sh NAME
@@ -45,6 +45,7 @@
.Nm sdp_open_local ,
.Nm sdp_close ,
.Nm sdp_error ,
+.Nm sdp_get_lcaddr ,
.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_lcaddr "void *xs" "bdaddr_t *l"
+.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,22 @@
function.
.Pp
The
+.Fn sdp_get_lcaddr
+function returns the SDP session actual source BD_ADDR.
+The
+.Fa xs
+parameter should point to a valid SDP session object created with
+.Fn sdp_open .
+The
+.Fa l
+parameter should point to a buffer in which the value for the requested BD_ADDR
+is to be returned.
+.Fn sdp_get_lcaddr
+function is useful if the current SDP session has been opened with the
+.Dv NG_HCI_BDADDR_ANY
+value passed as a source address.
+.Pp
+The
.Fn sdp_search
function is used to perform SDP Service Search Attribute Request.
The
@@ -394,9 +413,10 @@
to check if there was connection error.
.Pp
The
+.Fn sdp_get_lcaddr ,
.Fn sdp_search ,
.Fn sdp_register_service ,
-.Fn sdp_unregister_service
+.Fn sdp_unregister_service ,
and
.Fn sdp_change_service
functions return non-zero value on error.
Index: lib/libsdp/session.c
===================================================================
--- lib/libsdp/session.c
+++ lib/libsdp/session.c
@@ -180,3 +180,25 @@
return ((ss != NULL)? ss->error : EINVAL);
}
+
+int32_t
+sdp_get_lcaddr(void *xss, bdaddr_t *l)
+{
+ sdp_session_p ss = (sdp_session_p) xss;
+ struct sockaddr_l2cap sa;
+ socklen_t size;
+
+ if (l == NULL || ss == NULL || ss->flags & SDP_SESSION_LOCAL) {
+ ss->error = EINVAL;
+ goto fail;
+ }
+
+ size = sizeof(sa);
+ if (getsockname(ss->s, (struct sockaddr *)&sa, &size) == 0) {
+ bdaddr_copy(l, &sa.l2cap_bdaddr);
+ ss->error = 0;
+ } else
+ ss->error = errno;
+fail:
+ return ((ss->error == 0) ? 0 : -1);
+}
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,8 @@
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 \
+ -I${SRCTOP}/lib/libbluetooth
LIBADD+= bluetooth sdp usbhid
Index: usr.sbin/bluetooth/bthidcontrol/sdp.c
===================================================================
--- usr.sbin/bluetooth/bthidcontrol/sdp.c
+++ usr.sbin/bluetooth/bthidcontrol/sdp.c
@@ -31,7 +31,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>
@@ -114,13 +116,15 @@
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;
+ bdaddr_t sdp_local;
+ char devname[HCI_DEVNAME_SIZE];
if (local == NULL)
local = NG_HCI_BDADDR_ANY;
@@ -175,6 +179,11 @@
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 */
+ if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||
+ bt_devname(devname, &sdp_local) == 0)
+ hid_sdp_query_exit(ENOATTR);
+
sdp_close(ss);
ss = NULL;
@@ -212,6 +221,7 @@
reconnect_initiate == -1 ||
hid_descriptor == NULL || hid_descriptor_length == -1)
hid_sdp_query_exit(ENOATTR);
+ hd->name = bt_devremote_name_gen(devname, &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
@@ -42,6 +42,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 */
@@ -52,7 +53,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
@@ -48,6 +48,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;
@@ -63,6 +64,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
@@ -87,6 +92,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,11 @@
.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
Index: usr.sbin/bluetooth/bthidd/bthidd.c
===================================================================
--- usr.sbin/bluetooth/bthidd/bthidd.c
+++ usr.sbin/bluetooth/bthidd/bthidd.c
@@ -69,14 +69,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)) {
@@ -111,6 +112,10 @@
usage();
break;
+ case 'u': /* enable evdev support */
+ uinput = 1;
+ break;
+
case 'h':
default:
usage();
@@ -158,6 +163,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);
@@ -263,6 +270,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,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_ */
Index: usr.sbin/bluetooth/bthidd/btuinput.c
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/bthidd/btuinput.c
@@ -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);
+}
Index: usr.sbin/bluetooth/bthidd/client.c
===================================================================
--- usr.sbin/bluetooth/bthidd/client.c
+++ usr.sbin/bluetooth/bthidd/client.c
@@ -188,14 +188,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
@@ -50,6 +50,7 @@
#include <usbhid.h>
#include "bthid_config.h"
#include "bthidd.h"
+#include "btuinput.h"
#include "kbd.h"
/*
@@ -280,6 +281,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;
@@ -551,6 +565,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
@@ -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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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
@@ -56,6 +56,7 @@
device_word device
bdaddr_word bdaddr
+name_word name
vendor_id_word vendor_id
product_id_word product_id
version_word version
@@ -71,6 +72,7 @@
bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}
hexbytestring 0x{hexbyte}
hexwordstring 0x{hexword}
+string \".+\"
%%
@@ -85,6 +87,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);
@@ -118,6 +121,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
@@ -63,6 +63,8 @@
#define EOL "\n"
#endif /* ndef BTHIDCONTROL */
+#define NAMELESS_DEVICE "No Name"
+
#include "bthid_config.h"
int yylex (void);
@@ -85,11 +87,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
@@ -128,6 +133,7 @@
;
option: bdaddr
+ | name
| vendor_id
| product_id
| version
@@ -146,6 +152,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;
@@ -332,6 +356,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" \
@@ -342,6 +367,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",
@@ -367,7 +393,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,
@@ -390,11 +416,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;
@@ -404,6 +442,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;
}
}
@@ -419,6 +479,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
@@ -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 @@
(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 @@
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 @@
} 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
@@ -47,6 +47,7 @@
#include <usbhid.h>
#include "bthid_config.h"
#include "bthidd.h"
+#include "btuinput.h"
#include "kbd.h"
/*
@@ -68,22 +69,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) {
@@ -103,6 +94,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
*/
@@ -135,7 +184,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);
@@ -179,6 +229,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
Details
Attached
Mime Type
text/plain
Expires
Wed, Apr 22, 11:33 AM (6 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31974784
Default Alt Text
D13456.id40181.diff (50 KB)
Attached To
Mode
D13456: bthidd: Add evdev protocol support
Attached
Detach File
Event Timeline
Log In to Comment