Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 30 Apr 2018 12:16:54 +0000 (UTC)
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r333113 - in head: etc/defaults etc/rc.d usr.sbin/bluetooth/bthidd
Message-ID:  <201804301216.w3UCGsao060973@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: wulf
Date: Mon Apr 30 12:16:54 2018
New Revision: 333113
URL: https://svnweb.freebsd.org/changeset/base/333113

Log:
  bthidd(8): Add evdev protocol support for bluetooth keyboards and mouses
  
  User-visible changes:
  
  "-u" is added to to list of command line options supported by bthidd.
  Use it to enable evdev support. uinput and evdev modules should be
  kld-loaded or compiled into the kernel in that case.
  
  bthidd_evdev_support rc.conf variable is added to control enabling of
  evdev support in bthidd startup script. Possible values are: "YES", "NO",
  "AUTO"(default). Setting bthidd_evdev_support to "AUTO" inserts "-u" option
  if kernel is compiled with EVDEV_SUPPORT option enabled.
  
  Support for consumer HID usage page keyboard events is implemented. Most of
  them are available only through evdev protocol.
  
  kern.evdev.rcpt_mask sysctl is checked, so "sysctl kern.evdev.rcpt_mask=12"
  should be executed if EVDEV_SUPPORT is compiled into kernel.
  
  It is recommended to regenerate bthidd.conf entries with bthidcontrol(8)
  "Query" command to set user-friendly names of bluetooth devices.
  
  Reviewed by:	emax, gonzo, wblock (docs), bcr (docs, early version)
  Differential Revision:	https://reviews.freebsd.org/D13456

Added:
  head/usr.sbin/bluetooth/bthidd/btuinput.c   (contents, props changed)
  head/usr.sbin/bluetooth/bthidd/btuinput.h   (contents, props changed)
Modified:
  head/etc/defaults/rc.conf
  head/etc/rc.d/bthidd
  head/usr.sbin/bluetooth/bthidd/Makefile
  head/usr.sbin/bluetooth/bthidd/bthid_config.h
  head/usr.sbin/bluetooth/bthidd/bthidd.8
  head/usr.sbin/bluetooth/bthidd/bthidd.c
  head/usr.sbin/bluetooth/bthidd/bthidd.h
  head/usr.sbin/bluetooth/bthidd/client.c
  head/usr.sbin/bluetooth/bthidd/hid.c
  head/usr.sbin/bluetooth/bthidd/kbd.c
  head/usr.sbin/bluetooth/bthidd/parser.y
  head/usr.sbin/bluetooth/bthidd/server.c
  head/usr.sbin/bluetooth/bthidd/session.c

Modified: head/etc/defaults/rc.conf
==============================================================================
--- head/etc/defaults/rc.conf	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/etc/defaults/rc.conf	Mon Apr 30 12:16:54 2018	(r333113)
@@ -433,6 +433,7 @@ sdpd_username="nobody"		# it initializes
 bthidd_enable="NO"		# Enable bthidd(8) (or NO)
 bthidd_config="/etc/bluetooth/bthidd.conf" # bthidd(8) configuration file
 bthidd_hids="/var/db/bthidd.hids" # bthidd(8) known HID devices file
+bthidd_evdev_support="AUTO"	# AUTO depends on EVDEV_SUPPORT kernel option
 
 rfcomm_pppd_server_enable="NO"	# Enable rfcomm_pppd(8) in server mode (or NO)
 rfcomm_pppd_server_profile="one two"	# Profile to use from /etc/ppp/ppp.conf

Modified: head/etc/rc.d/bthidd
==============================================================================
--- head/etc/rc.d/bthidd	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/etc/rc.d/bthidd	Mon Apr 30 12:16:54 2018	(r333113)
@@ -17,8 +17,25 @@ command="/usr/sbin/${name}"
 pidfile="/var/run/${name}.pid"
 start_precmd="bthidd_prestart"
 
+evdev_enabled()
+{
+	case ${bthidd_evdev_support} in
+	[Aa][Uu][Tt][Oo])
+		check_kern_features evdev_support
+		return $?
+		;;
+	*)
+		checkyesno bthidd_evdev_support
+		return $?
+		;;
+	esac
+}
+
 bthidd_prestart()
 {
+	if evdev_enabled; then
+		load_kld -m uinput uinput
+	fi
 	load_kld -m kbdmux kbdmux
 	load_kld -m vkbd vkbd
 	load_kld -m ng_btsocket ng_btsocket
@@ -29,6 +46,9 @@ load_rc_config $name
 config="${bthidd_config:-/etc/bluetooth/${name}.conf}"
 hids="${bthidd_hids:-/var/db/${name}.hids}"
 command_args="-c ${config} -H ${hids} -p ${pidfile}"
+if evdev_enabled; then
+	command_args="$command_args -u"
+fi
 required_files="${config}"
 
 run_rc_command "$1"

Modified: head/usr.sbin/bluetooth/bthidd/Makefile
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/Makefile	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/Makefile	Mon Apr 30 12:16:54 2018	(r333113)
@@ -4,8 +4,8 @@
 PROG=		bthidd
 MAN=		bthidd.8
 #		bthidd.conf.5
-SRCS=		bthidd.c client.c hid.c kbd.c lexer.l parser.y server.c \
-		session.c
+SRCS=		bthidd.c btuinput.c client.c hid.c kbd.c lexer.l parser.y \
+		server.c session.c
 
 CFLAGS+=	-I${.CURDIR}
 

Modified: head/usr.sbin/bluetooth/bthidd/bthid_config.h
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/bthid_config.h	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/bthid_config.h	Mon Apr 30 12:16:54 2018	(r333113)
@@ -53,7 +53,11 @@ struct hid_device
 	unsigned		battery_power        : 1;
 	unsigned		normally_connectable : 1;
 	unsigned		keyboard             : 1;
-	unsigned		reserved             : 11;
+	unsigned		mouse                : 1;
+	unsigned		has_wheel            : 1;
+	unsigned		has_hwheel           : 1;
+	unsigned		has_cons             : 1;
+	unsigned		reserved             : 7;
 	report_desc_t		desc;		/* HID report descriptor */
 	LIST_ENTRY(hid_device)	next;		/* link to the next */
 };

Modified: head/usr.sbin/bluetooth/bthidd/bthidd.8
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/bthidd.8	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/bthidd.8	Mon Apr 30 12:16:54 2018	(r333113)
@@ -25,7 +25,7 @@
 .\" $Id: bthidd.8,v 1.1 2006/09/07 21:36:55 max Exp $
 .\" $FreeBSD$
 .\"
-.Dd September 7, 2006
+.Dd April 30, 2018
 .Dt BTHIDD 8
 .Os
 .Sh NAME
@@ -40,6 +40,7 @@
 .Op Fl H Ar file
 .Op Fl p Ar file
 .Op Fl t Ar val
+.Op Fl u
 .Sh DESCRIPTION
 The
 .Nm
@@ -82,6 +83,11 @@ disconnected
 .Dq passive
 Bluetooth HID devices and will attempt to establish an outgoing connection.
 The default rescan interval is 10 seconds.
+.It Fl u
+Enable support for input event device protocol.
+Requires evdev and uinput drivers to be loaded with
+.Xr kldload 8
+or compiled into the kernel.
 .El
 .Sh KNOWN LIMITATIONS
 The

Modified: head/usr.sbin/bluetooth/bthidd/bthidd.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/bthidd.c	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/bthidd.c	Mon Apr 30 12:16:54 2018	(r333113)
@@ -69,14 +69,15 @@ main(int32_t argc, char *argv[])
 	struct sigaction	 sa;
 	char const		*pid_file = BTHIDD_PIDFILE;
 	char			*ep;
-	int32_t			 opt, detach, tval;
+	int32_t			 opt, detach, tval, uinput;
 
 	memset(&srv, 0, sizeof(srv));
 	memset(&srv.bdaddr, 0, sizeof(srv.bdaddr));
 	detach = 1;
 	tval = 10; /* sec */
+	uinput = 0;
 
-	while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) {
+	while ((opt = getopt(argc, argv, "a:c:dH:hp:t:u")) != -1) {
 		switch (opt) {
 		case 'a': /* BDADDR */
 			if (!bt_aton(optarg, &srv.bdaddr)) {
@@ -111,6 +112,10 @@ main(int32_t argc, char *argv[])
 				usage();
 			break;
 
+		case 'u': /* enable evdev support */
+			uinput = 1;
+			break;
+
 		case 'h':
 		default:
 			usage();
@@ -158,6 +163,8 @@ main(int32_t argc, char *argv[])
 	    server_init(&srv) < 0 || write_pid_file(pid_file) < 0)
 		exit(1);
 
+	srv.uinput = uinput;
+
 	for (done = 0; !done; ) {
 		if (elapsed(tval))
 			client_rescan(&srv);
@@ -263,6 +270,7 @@ usage(void)
 "	-h		display this message\n" \
 "	-p file		specify PID file name\n" \
 "	-t tval		specify client rescan interval (sec)\n" \
+"	-u		enable evdev protocol support\n" \
 "", BTHIDD_IDENT);
 	exit(255);
 }

Modified: head/usr.sbin/bluetooth/bthidd/bthidd.h
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/bthidd.h	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/bthidd.h	Mon Apr 30 12:16:54 2018	(r333113)
@@ -48,6 +48,7 @@ struct bthid_server
 	int32_t				 ctrl;   /* control channel (listen) */
 	int32_t				 intr;   /* intr. channel (listen) */
 	int32_t				 maxfd;	 /* max fd in sets */
+	int32_t				 uinput; /* enable evdev support */
 	fd_set				 rfdset; /* read descriptor set */
 	fd_set				 wfdset; /* write descriptor set */
 	LIST_HEAD(, bthid_session)	 sessions;
@@ -63,6 +64,10 @@ struct bthid_session
 	int32_t				 intr;	/* interrupt channel */
 	int32_t				 vkbd;	/* virual keyboard */
 	void				*ctx;   /* product specific dev state */
+	int32_t				 ukbd;  /* evdev user input */
+	int32_t				 umouse;/* evdev user input */
+	int32_t				 obutt; /* previous mouse buttons */
+	int32_t				 consk; /* last consumer page key */
 	bdaddr_t			 bdaddr;/* remote bdaddr */
 	uint16_t			 state;	/* session state */
 #define CLOSED	0
@@ -87,6 +92,7 @@ int32_t		client_connect   (bthid_server_p srv, int fd)
 bthid_session_p	session_open     (bthid_server_p srv, hid_device_p const d);
 bthid_session_p	session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
 bthid_session_p	session_by_fd    (bthid_server_p srv, int32_t fd);
+int32_t		session_run      (bthid_session_p s);
 void		session_close    (bthid_session_p s);
 
 void		hid_initialise	 (bthid_session_p s);

Added: head/usr.sbin/bluetooth/bthidd/btuinput.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/bluetooth/bthidd/btuinput.c	Mon Apr 30 12:16:54 2018	(r333113)
@@ -0,0 +1,618 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2015-2017 Vladimir Kondratyev <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);
+}

Added: head/usr.sbin/bluetooth/bthidd/btuinput.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/usr.sbin/bluetooth/bthidd/btuinput.h	Mon Apr 30 12:16:54 2018	(r333113)
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2015-2017 Vladimir Kondratyev <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_ */

Modified: head/usr.sbin/bluetooth/bthidd/client.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/client.c	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/client.c	Mon Apr 30 12:16:54 2018	(r333113)
@@ -188,14 +188,11 @@ client_connect(bthid_server_p srv, int32_t fd)
 		s->state = OPEN;
 		connect_in_progress = 0;
 
-		/* Register session's vkbd descriptor (if any) for read */
-		if (s->state == OPEN && d->keyboard) {
-			assert(s->vkbd != -1);
-
-			FD_SET(s->vkbd, &srv->rfdset);
-			if (s->vkbd > srv->maxfd)
-				srv->maxfd = s->vkbd;
-	        }
+		/* Create kbd/mouse after both channels are established */
+		if (session_run(s) < 0) {
+			session_close(s);
+			return (-1);
+		}
 		break;
 
 	default:

Modified: head/usr.sbin/bluetooth/bthidd/hid.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/hid.c	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/hid.c	Mon Apr 30 12:16:54 2018	(r333113)
@@ -50,6 +50,7 @@
 #include <usbhid.h>
 #include "bthid_config.h"
 #include "bthidd.h"
+#include "btuinput.h"
 #include "kbd.h"
 
 /*
@@ -280,6 +281,19 @@ hid_interrupt(bthid_session_p s, uint8_t *data, int32_
 			break;
 
 		case HUP_CONSUMER:
+			if (hid_device->keyboard && s->srv->uinput) {
+				if (h.flags & HIO_VARIABLE) {
+					uinput_rep_cons(s->ukbd, usage, !!val);
+				} else {
+					if (s->consk > 0)
+						uinput_rep_cons(s->ukbd,
+						    s->consk, 0);
+					if (uinput_rep_cons(s->ukbd, val, 1)
+					    == 0)
+						s->consk = val;
+				}
+			}
+
 			if (!val)
 				break;
 
@@ -551,6 +565,14 @@ check_middle_button:
 			syslog(LOG_ERR, "Could not process mouse events from " \
 				"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
 				strerror(errno), errno);
+
+		if (hid_device->mouse && s->srv->uinput &&
+		    uinput_rep_mouse(s->umouse, mouse_x, mouse_y, mouse_z,
+					mouse_t, mouse_butt, s->obutt) < 0)
+			syslog(LOG_ERR, "Could not process mouse events from " \
+				"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
+				strerror(errno), errno);
+		s->obutt = mouse_butt;
 	}
 
 	return (0);

Modified: head/usr.sbin/bluetooth/bthidd/kbd.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/kbd.c	Mon Apr 30 10:49:29 2018	(r333112)
+++ head/usr.sbin/bluetooth/bthidd/kbd.c	Mon Apr 30 12:16:54 2018	(r333113)
@@ -56,10 +56,12 @@
 #include <usbhid.h>
 #include "bthid_config.h"
 #include "bthidd.h"
+#include "btuinput.h"
 #include "kbd.h"
 
 static void	kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);
 static int32_t	kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob);
+static void	uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);
 
 /*
  * HID code to PS/2 set 1 code translation table.
@@ -354,6 +356,7 @@ kbd_process_keys(bthid_session_p s)
 		if (f2 != -1) {
 			/* release old keys */
 			kbd_write(s->keys2, f2, 0, s->vkbd);
+			uinput_kbd_write(s->keys2, f2, 0, s->ukbd);
 			memset(s->keys2, 0, bitstr_size(xsize));
 		}
 
@@ -366,6 +369,7 @@ kbd_process_keys(bthid_session_p s)
 		
 		memcpy(s->keys2, s->keys1, bitstr_size(xsize));
 		kbd_write(s->keys1, f1, 1, s->vkbd);
+		uinput_kbd_write(s->keys1, f1, 1, s->ukbd);
 		memset(s->keys1, 0, bitstr_size(xsize));
 
 		return (0);
@@ -393,12 +397,15 @@ kbd_process_keys(bthid_session_p s)
 	}
 
 	bit_ffs(diff, xsize, &f2);
-	if (f2 > 0)
+	if (f2 > 0) {
 		kbd_write(diff, f2, 0, s->vkbd);
+		uinput_kbd_write(diff, f2, 0, s->ukbd);
+	}
 
 	bit_ffs(s->keys1, xsize, &f1);
 	if (f1 > 0) {
 		kbd_write(s->keys1, f1, 1, s->vkbd);
+		uinput_kbd_write(s->keys1, f1, 1, s->ukbd);
 		memset(s->keys1, 0, bitstr_size(xsize));
 	}
 
@@ -407,6 +414,22 @@ kbd_process_keys(bthid_session_p s)
 
 /*
  * Translate given keymap and write keyscodes
+ */
+void
+uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)
+{
+	int32_t i;
+
+	if (fd >= 0) {
+		for (i = fb; i < xsize; i++) {
+			if (bit_test(m, i))
+				uinput_rep_key(fd, i, make);
+		}
+	}
+}
+
+/*
+ * Translate given keymap and write keyscodes
  */ 
 
 static void
@@ -520,6 +543,7 @@ kbd_status_changed(bthid_session_p s, uint8_t *data, i
 	hid_device_p	hid_device;
 	hid_data_t	d;
 	hid_item_t	h;
+	uint8_t		leds_mask = 0;
 
 	assert(s != NULL);
 	assert(len == sizeof(vkbd_status_t));
@@ -553,16 +577,19 @@ kbd_status_changed(bthid_session_p s, uint8_t *data, i
 			case 0x01: /* Num Lock LED */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201804301216.w3UCGsao060973>