1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2015-2017 Vladimir Kondratyev <wulf@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/ioctl.h>
31#include <sys/kbio.h>
32#include <sys/sysctl.h>
33
34#include <dev/evdev/input.h>
35#include <dev/evdev/uinput.h>
36#include <dev/usb/usb.h>
37#include <dev/usb/usbhid.h>
38
39#include <assert.h>
40#define L2CAP_SOCKET_CHECKED
41#include <bluetooth.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <stdio.h>
45#include <string.h>
46#include <syslog.h>
47#include <time.h>
48#include <unistd.h>
49#include <usbhid.h>
50
51#include "bthid_config.h"
52#include "bthidd.h"
53#include "btuinput.h"
54
55static int16_t const mbuttons[8] = {
56	BTN_LEFT,
57	BTN_MIDDLE,
58	BTN_RIGHT,
59	BTN_SIDE,
60	BTN_EXTRA,
61	BTN_FORWARD,
62	BTN_BACK,
63	BTN_TASK
64};
65
66static uint16_t const led_codes[3] = {
67	LED_CAPSL,	/* CLKED */
68	LED_NUML,	/* NLKED */
69	LED_SCROLLL,	/* SLKED */
70};
71
72#define	NONE	KEY_RESERVED
73
74static uint16_t const keymap[0x100] = {
75	/* 0x00 - 0x27 */
76	NONE,	NONE,	NONE,	NONE,	KEY_A,	KEY_B,	KEY_C,	KEY_D,
77	KEY_E,	KEY_F,	KEY_G,	KEY_H,	KEY_I,	KEY_J,	KEY_K,	KEY_L,
78	KEY_M,	KEY_N,	KEY_O,	KEY_P,	KEY_Q,	KEY_R,	KEY_S,	KEY_T,
79	KEY_U,	KEY_V,	KEY_W,	KEY_X,	KEY_Y,	KEY_Z,	KEY_1,	KEY_2,
80	KEY_3,	KEY_4,	KEY_5,	KEY_6,	KEY_7,	KEY_8,	KEY_9,	KEY_0,
81	/* 0x28 - 0x3f */
82	KEY_ENTER,	KEY_ESC,	KEY_BACKSPACE,	KEY_TAB,
83	KEY_SPACE,	KEY_MINUS,	KEY_EQUAL,	KEY_LEFTBRACE,
84	KEY_RIGHTBRACE,	KEY_BACKSLASH,	KEY_BACKSLASH,	KEY_SEMICOLON,
85	KEY_APOSTROPHE,	KEY_GRAVE,	KEY_COMMA,	KEY_DOT,
86	KEY_SLASH,	KEY_CAPSLOCK,	KEY_F1,		KEY_F2,
87	KEY_F3,		KEY_F4,		KEY_F5,		KEY_F6,
88	/* 0x40 - 0x5f */
89	KEY_F7,		KEY_F8,		KEY_F9,		KEY_F10,
90	KEY_F11,	KEY_F12,	KEY_SYSRQ,	KEY_SCROLLLOCK,
91	KEY_PAUSE,	KEY_INSERT,	KEY_HOME,	KEY_PAGEUP,
92	KEY_DELETE,	KEY_END,	KEY_PAGEDOWN,	KEY_RIGHT,
93	KEY_LEFT,	KEY_DOWN,	KEY_UP,		KEY_NUMLOCK,
94	KEY_KPSLASH,	KEY_KPASTERISK,	KEY_KPMINUS,	KEY_KPPLUS,
95	KEY_KPENTER,	KEY_KP1,	KEY_KP2,	KEY_KP3,
96	KEY_KP4,	KEY_KP5,	KEY_KP6,	KEY_KP7,
97	/* 0x60 - 0x7f */
98	KEY_KP8,	KEY_KP9,	KEY_KP0,	KEY_KPDOT,
99	KEY_102ND,	KEY_COMPOSE,	KEY_POWER,	KEY_KPEQUAL,
100	KEY_F13,	KEY_F14,	KEY_F15,	KEY_F16,
101	KEY_F17,	KEY_F18,	KEY_F19,	KEY_F20,
102	KEY_F21,	KEY_F22,	KEY_F23,	KEY_F24,
103	KEY_OPEN,	KEY_HELP,	KEY_PROPS,	KEY_FRONT,
104	KEY_STOP,	KEY_AGAIN,	KEY_UNDO,	KEY_CUT,
105	KEY_COPY,	KEY_PASTE,	KEY_FIND,	KEY_MUTE,
106	/* 0x80 - 0x9f */
107	KEY_VOLUMEUP,	KEY_VOLUMEDOWN,	NONE,		NONE,
108	NONE,		KEY_KPCOMMA,	NONE,		KEY_RO,
109	KEY_KATAKANAHIRAGANA,	KEY_YEN,KEY_HENKAN,	KEY_MUHENKAN,
110	KEY_KPJPCOMMA,	NONE,		NONE,		NONE,
111	KEY_HANGEUL,	KEY_HANJA,	KEY_KATAKANA,	KEY_HIRAGANA,
112	KEY_ZENKAKUHANKAKU,	NONE,	NONE,		NONE,
113	NONE,		NONE,		NONE,		NONE,
114	NONE,		NONE,		NONE,		NONE,
115	/* 0xa0 - 0xbf */
116	NONE,		NONE,		NONE,		NONE,
117	NONE,		NONE,		NONE,		NONE,
118	NONE,		NONE,		NONE,		NONE,
119	NONE,		NONE,		NONE,		NONE,
120	NONE,		NONE,		NONE,		NONE,
121	NONE,		NONE,		NONE,		NONE,
122	NONE,		NONE,		NONE,		NONE,
123	NONE,		NONE,		NONE,		NONE,
124	/* 0xc0 - 0xdf */
125	NONE,		NONE,           NONE,		NONE,
126	NONE,		NONE,           NONE,		NONE,
127	NONE,		NONE,           NONE,		NONE,
128	NONE,		NONE,           NONE,		NONE,
129	NONE,		NONE,           NONE,		NONE,
130	NONE,		NONE,           NONE,		NONE,
131	NONE,		NONE,           NONE,		NONE,
132	NONE,		NONE,           NONE,		NONE,
133	/* 0xe0 - 0xff */
134	KEY_LEFTCTRL,	KEY_LEFTSHIFT,	KEY_LEFTALT,	KEY_LEFTMETA,
135	KEY_RIGHTCTRL,	KEY_RIGHTSHIFT,	KEY_RIGHTALT,	KEY_RIGHTMETA,
136	KEY_PLAYPAUSE,	KEY_STOPCD,	KEY_PREVIOUSSONG,KEY_NEXTSONG,
137	KEY_EJECTCD,	KEY_VOLUMEUP,	KEY_VOLUMEDOWN, KEY_MUTE,
138	KEY_WWW,	KEY_BACK,	KEY_FORWARD,	KEY_STOP,
139	KEY_FIND,	KEY_SCROLLUP,	KEY_SCROLLDOWN,	KEY_EDIT,
140	KEY_SLEEP,	KEY_COFFEE,	KEY_REFRESH,	KEY_CALC,
141	NONE,		NONE,		NONE,		NONE,
142};
143
144/* Consumer page usage mapping */
145static uint16_t const consmap[0x300] = {
146	[0x030] = KEY_POWER,
147	[0x031] = KEY_RESTART,
148	[0x032] = KEY_SLEEP,
149	[0x034] = KEY_SLEEP,
150	[0x035] = KEY_KBDILLUMTOGGLE,
151	[0x036] = BTN_MISC,
152	[0x040] = KEY_MENU,
153	[0x041] = KEY_SELECT,
154	[0x042] = KEY_UP,
155	[0x043] = KEY_DOWN,
156	[0x044] = KEY_LEFT,
157	[0x045] = KEY_RIGHT,
158	[0x046] = KEY_ESC,
159	[0x047] = KEY_KPPLUS,
160	[0x048] = KEY_KPMINUS,
161	[0x060] = KEY_INFO,
162	[0x061] = KEY_SUBTITLE,
163	[0x063] = KEY_VCR,
164	[0x065] = KEY_CAMERA,
165	[0x069] = KEY_RED,
166	[0x06a] = KEY_GREEN,
167	[0x06b] = KEY_BLUE,
168	[0x06c] = KEY_YELLOW,
169	[0x06d] = KEY_ZOOM,
170	[0x06f] = KEY_BRIGHTNESSUP,
171	[0x070] = KEY_BRIGHTNESSDOWN,
172	[0x072] = KEY_BRIGHTNESS_TOGGLE,
173	[0x073] = KEY_BRIGHTNESS_MIN,
174	[0x074] = KEY_BRIGHTNESS_MAX,
175	[0x075] = KEY_BRIGHTNESS_AUTO,
176	[0x082] = KEY_VIDEO_NEXT,
177	[0x083] = KEY_LAST,
178	[0x084] = KEY_ENTER,
179	[0x088] = KEY_PC,
180	[0x089] = KEY_TV,
181	[0x08a] = KEY_WWW,
182	[0x08b] = KEY_DVD,
183	[0x08c] = KEY_PHONE,
184	[0x08d] = KEY_PROGRAM,
185	[0x08e] = KEY_VIDEOPHONE,
186	[0x08f] = KEY_GAMES,
187	[0x090] = KEY_MEMO,
188	[0x091] = KEY_CD,
189	[0x092] = KEY_VCR,
190	[0x093] = KEY_TUNER,
191	[0x094] = KEY_EXIT,
192	[0x095] = KEY_HELP,
193	[0x096] = KEY_TAPE,
194	[0x097] = KEY_TV2,
195	[0x098] = KEY_SAT,
196	[0x09a] = KEY_PVR,
197	[0x09c] = KEY_CHANNELUP,
198	[0x09d] = KEY_CHANNELDOWN,
199	[0x0a0] = KEY_VCR2,
200	[0x0b0] = KEY_PLAY,
201	[0x0b1] = KEY_PAUSE,
202	[0x0b2] = KEY_RECORD,
203	[0x0b3] = KEY_FASTFORWARD,
204	[0x0b4] = KEY_REWIND,
205	[0x0b5] = KEY_NEXTSONG,
206	[0x0b6] = KEY_PREVIOUSSONG,
207	[0x0b7] = KEY_STOPCD,
208	[0x0b8] = KEY_EJECTCD,
209	[0x0bc] = KEY_MEDIA_REPEAT,
210	[0x0b9] = KEY_SHUFFLE,
211	[0x0bf] = KEY_SLOW,
212	[0x0cd] = KEY_PLAYPAUSE,
213	[0x0cf] = KEY_VOICECOMMAND,
214	[0x0e2] = KEY_MUTE,
215	[0x0e5] = KEY_BASSBOOST,
216	[0x0e9] = KEY_VOLUMEUP,
217	[0x0ea] = KEY_VOLUMEDOWN,
218	[0x0f5] = KEY_SLOW,
219	[0x181] = KEY_BUTTONCONFIG,
220	[0x182] = KEY_BOOKMARKS,
221	[0x183] = KEY_CONFIG,
222	[0x184] = KEY_WORDPROCESSOR,
223	[0x185] = KEY_EDITOR,
224	[0x186] = KEY_SPREADSHEET,
225	[0x187] = KEY_GRAPHICSEDITOR,
226	[0x188] = KEY_PRESENTATION,
227	[0x189] = KEY_DATABASE,
228	[0x18a] = KEY_MAIL,
229	[0x18b] = KEY_NEWS,
230	[0x18c] = KEY_VOICEMAIL,
231	[0x18d] = KEY_ADDRESSBOOK,
232	[0x18e] = KEY_CALENDAR,
233	[0x18f] = KEY_TASKMANAGER,
234	[0x190] = KEY_JOURNAL,
235	[0x191] = KEY_FINANCE,
236	[0x192] = KEY_CALC,
237	[0x193] = KEY_PLAYER,
238	[0x194] = KEY_FILE,
239	[0x196] = KEY_WWW,
240	[0x199] = KEY_CHAT,
241	[0x19c] = KEY_LOGOFF,
242	[0x19e] = KEY_COFFEE,
243	[0x19f] = KEY_CONTROLPANEL,
244	[0x1a2] = KEY_APPSELECT,
245	[0x1a3] = KEY_NEXT,
246	[0x1a4] = KEY_PREVIOUS,
247	[0x1a6] = KEY_HELP,
248	[0x1a7] = KEY_DOCUMENTS,
249	[0x1ab] = KEY_SPELLCHECK,
250	[0x1ae] = KEY_KEYBOARD,
251	[0x1b1] = KEY_SCREENSAVER,
252	[0x1b4] = KEY_FILE,
253	[0x1b6] = KEY_IMAGES,
254	[0x1b7] = KEY_AUDIO,
255	[0x1b8] = KEY_VIDEO,
256	[0x1bc] = KEY_MESSENGER,
257	[0x1bd] = KEY_INFO,
258	[0x201] = KEY_NEW,
259	[0x202] = KEY_OPEN,
260	[0x203] = KEY_CLOSE,
261	[0x204] = KEY_EXIT,
262	[0x207] = KEY_SAVE,
263	[0x208] = KEY_PRINT,
264	[0x209] = KEY_PROPS,
265	[0x21a] = KEY_UNDO,
266	[0x21b] = KEY_COPY,
267	[0x21c] = KEY_CUT,
268	[0x21d] = KEY_PASTE,
269	[0x21f] = KEY_FIND,
270	[0x221] = KEY_SEARCH,
271	[0x222] = KEY_GOTO,
272	[0x223] = KEY_HOMEPAGE,
273	[0x224] = KEY_BACK,
274	[0x225] = KEY_FORWARD,
275	[0x226] = KEY_STOP,
276	[0x227] = KEY_REFRESH,
277	[0x22a] = KEY_BOOKMARKS,
278	[0x22d] = KEY_ZOOMIN,
279	[0x22e] = KEY_ZOOMOUT,
280	[0x22f] = KEY_ZOOMRESET,
281	[0x233] = KEY_SCROLLUP,
282	[0x234] = KEY_SCROLLDOWN,
283	[0x23d] = KEY_EDIT,
284	[0x25f] = KEY_CANCEL,
285	[0x269] = KEY_INSERT,
286	[0x26a] = KEY_DELETE,
287	[0x279] = KEY_REDO,
288	[0x289] = KEY_REPLY,
289	[0x28b] = KEY_FORWARDMAIL,
290	[0x28c] = KEY_SEND,
291	[0x2c7] = KEY_KBDINPUTASSIST_PREV,
292	[0x2c8] = KEY_KBDINPUTASSIST_NEXT,
293	[0x2c9] = KEY_KBDINPUTASSIST_PREVGROUP,
294	[0x2ca] = KEY_KBDINPUTASSIST_NEXTGROUP,
295	[0x2cb] = KEY_KBDINPUTASSIST_ACCEPT,
296	[0x2cc] = KEY_KBDINPUTASSIST_CANCEL,
297};
298
299static int32_t
300uinput_open_common(hid_device_p const p, bdaddr_p local, const uint8_t *name)
301{
302	struct uinput_setup	uisetup;
303	uint8_t			phys[UINPUT_MAX_NAME_SIZE];
304	uint8_t			uniq[UINPUT_MAX_NAME_SIZE];
305	int32_t			fd;
306
307	/* Take local and remote bdaddr */
308	bt_ntoa(local, phys);
309	bt_ntoa(&p->bdaddr, uniq);
310
311	/* Take device name from bthidd.conf. Fallback to generic name. */
312	if (p->name != NULL)
313		name = p->name;
314
315	/* Set device name and bus/vendor information */
316	memset(&uisetup, 0, sizeof(uisetup));
317	snprintf(uisetup.name, UINPUT_MAX_NAME_SIZE,
318	    "%s, bdaddr %s", name, uniq);
319	uisetup.id.bustype = BUS_BLUETOOTH;
320	uisetup.id.vendor  = p->vendor_id;
321	uisetup.id.product = p->product_id;
322	uisetup.id.version = p->version;
323
324	fd = open("/dev/uinput", O_RDWR | O_NONBLOCK);
325
326	if (ioctl(fd, UI_SET_PHYS, phys) < 0 ||
327	    ioctl(fd, UI_SET_BSDUNIQ, uniq) < 0 ||
328	    ioctl(fd, UI_DEV_SETUP, &uisetup) < 0)
329		return (-1);
330
331	return (fd);
332}
333
334/*
335 * Setup uinput device as 8button mouse with wheel(s)
336 * TODO: bring in more feature detection code from ums
337 */
338int32_t
339uinput_open_mouse(hid_device_p const p, bdaddr_p local)
340{
341	size_t	i;
342	int32_t	fd;
343
344	assert(p != NULL);
345
346	if ((fd = uinput_open_common(p, local, "Bluetooth Mouse")) < 0)
347		goto bail_out;
348
349	/* Advertise events and axes */
350	if (ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 ||
351	    ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 ||
352	    ioctl(fd, UI_SET_EVBIT, EV_REL) < 0 ||
353	    ioctl(fd, UI_SET_RELBIT, REL_X) < 0 ||
354	    ioctl(fd, UI_SET_RELBIT, REL_Y) < 0 ||
355	    (p->has_wheel && ioctl(fd, UI_SET_RELBIT, REL_WHEEL) < 0) ||
356	    (p->has_hwheel && ioctl(fd, UI_SET_RELBIT, REL_HWHEEL) < 0) ||
357	    ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_POINTER) < 0)
358		goto bail_out;
359
360	/* Advertise mouse buttons */
361	for (i = 0; i < nitems(mbuttons); i++)
362		if (ioctl(fd, UI_SET_KEYBIT, mbuttons[i]) < 0)
363			goto bail_out;
364
365	if (ioctl(fd, UI_DEV_CREATE) >= 0)
366		return (fd); /* SUCCESS */
367
368bail_out:
369	if (fd >= 0)
370		close(fd);
371	return (-1);
372}
373
374/*
375 * Setup uinput keyboard
376 */
377int32_t
378uinput_open_keyboard(hid_device_p const p, bdaddr_p local)
379{
380	size_t	i;
381	int32_t	fd;
382
383	assert(p != NULL);
384
385	if ((fd = uinput_open_common(p, local, "Bluetooth Keyboard")) < 0)
386		goto bail_out;
387
388	/* Advertise key events and LEDs */
389	if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 ||
390	    ioctl(fd, UI_SET_EVBIT, EV_LED) < 0 ||
391	    ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 ||
392	    ioctl(fd, UI_SET_EVBIT, EV_REP) < 0 ||
393	    ioctl(fd, UI_SET_LEDBIT, LED_CAPSL) < 0 ||
394	    ioctl(fd, UI_SET_LEDBIT, LED_NUML) < 0 ||
395	    ioctl(fd, UI_SET_LEDBIT, LED_SCROLLL))
396		goto bail_out;
397
398	/* Advertise keycodes */
399	for (i = 0; i < nitems(keymap); i++)
400		if (keymap[i] != NONE &&
401		    ioctl(fd, UI_SET_KEYBIT, keymap[i]) < 0)
402			goto bail_out;
403
404	/* Advertise consumer page keys if any */
405	if (p->has_cons) {
406		for (i = 0; i < nitems(consmap); i++) {
407			if (consmap[i] != NONE &&
408			    ioctl(fd, UI_SET_KEYBIT, consmap[i]) < 0)
409				goto bail_out;
410		}
411	}
412
413	if (ioctl(fd, UI_DEV_CREATE) >= 0)
414		return (fd); /* SUCCESS */
415
416bail_out:
417	if (fd >= 0)
418		close(fd);
419	return (-1);
420}
421
422/* from sys/dev/evdev/evdev.h */
423#define	EVDEV_RCPT_HW_MOUSE	(1<<2)
424#define	EVDEV_RCPT_HW_KBD	(1<<3)
425
426#define	MASK_POLL_INTERVAL	5 /* seconds */
427#define	MASK_SYSCTL		"kern.evdev.rcpt_mask"
428
429static int32_t
430uinput_get_rcpt_mask(void)
431{
432	static struct timespec last = { 0, 0 };
433	struct timespec now;
434	static int32_t mask = 0;
435	size_t len;
436	time_t elapsed;
437
438	if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == -1)
439		return mask;
440
441	elapsed = now.tv_sec - last.tv_sec;
442	if (now.tv_nsec < last.tv_nsec)
443		elapsed--;
444
445	if (elapsed >= MASK_POLL_INTERVAL) {
446		len = sizeof(mask);
447		if (sysctlbyname(MASK_SYSCTL, &mask, &len, NULL, 0) < 0) {
448			if (errno == ENOENT)
449				/* kernel is compiled w/o EVDEV_SUPPORT */
450				mask = EVDEV_RCPT_HW_MOUSE | EVDEV_RCPT_HW_KBD;
451			else
452				mask = 0;
453		}
454		last = now;
455	}
456	return mask;
457}
458
459static int32_t
460uinput_write_event(int32_t fd, uint16_t type, uint16_t code, int32_t value)
461{
462	struct input_event ie;
463
464	assert(fd >= 0);
465
466	memset(&ie, 0, sizeof(ie));
467	ie.type = type;
468	ie.code = code;
469	ie.value = value;
470	return (write(fd, &ie, sizeof(ie)));
471}
472
473int32_t
474uinput_rep_mouse(int32_t fd, int32_t x, int32_t y, int32_t z, int32_t t,
475    int32_t buttons, int32_t obuttons)
476{
477	size_t i;
478	int32_t rcpt_mask, mask;
479
480	assert(fd >= 0);
481
482	rcpt_mask = uinput_get_rcpt_mask();
483	if (!(rcpt_mask & EVDEV_RCPT_HW_MOUSE))
484		return (0);
485
486	if ((x != 0 && uinput_write_event(fd, EV_REL, REL_X, x) < 0) ||
487	    (y != 0 && uinput_write_event(fd, EV_REL, REL_Y, y) < 0) ||
488	    (z != 0 && uinput_write_event(fd, EV_REL, REL_WHEEL, -z) < 0) ||
489	    (t != 0 && uinput_write_event(fd, EV_REL, REL_HWHEEL, t) < 0))
490		return (-1);
491
492	for (i = 0; i < nitems(mbuttons); i++) {
493		mask = 1 << i;
494		if ((buttons & mask) == (obuttons & mask))
495			continue;
496		if (uinput_write_event(fd, EV_KEY, mbuttons[i],
497		    (buttons & mask) != 0) < 0)
498			return (-1);
499	}
500
501	if (uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) < 0)
502		return (-1);
503
504	return (0);
505}
506
507/*
508 * Translate and report keyboard page key events
509 */
510int32_t
511uinput_rep_key(int32_t fd, int32_t key, int32_t make)
512{
513	int32_t rcpt_mask;
514
515	assert(fd >= 0);
516
517	rcpt_mask = uinput_get_rcpt_mask();
518	if (!(rcpt_mask & EVDEV_RCPT_HW_KBD))
519		return (0);
520
521	if (key >= 0 && key < (int32_t)nitems(keymap) &&
522	    keymap[key] != NONE) {
523		if (uinput_write_event(fd, EV_KEY, keymap[key], make) > 0 &&
524		    uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0)
525			return (0);
526	}
527	return (-1);
528}
529
530/*
531 * Translate and report consumer page key events
532 */
533int32_t
534uinput_rep_cons(int32_t fd, int32_t key, int32_t make)
535{
536	int32_t rcpt_mask;
537
538	assert(fd >= 0);
539
540	rcpt_mask = uinput_get_rcpt_mask();
541	if (!(rcpt_mask & EVDEV_RCPT_HW_KBD))
542		return (0);
543
544	if (key >= 0 && key < (int32_t)nitems(consmap) &&
545	    consmap[key] != NONE) {
546		if (uinput_write_event(fd, EV_KEY, consmap[key], make) > 0 &&
547		    uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0)
548			return (0);
549	}
550	return (-1);
551}
552
553/*
554 * Translate and report LED events
555 */
556int32_t
557uinput_rep_leds(int32_t fd, int state, int mask)
558{
559	size_t i;
560	int32_t rcpt_mask;
561
562	assert(fd >= 0);
563
564	rcpt_mask = uinput_get_rcpt_mask();
565	if (!(rcpt_mask & EVDEV_RCPT_HW_KBD))
566		return (0);
567
568	for (i = 0; i < nitems(led_codes); i++) {
569		if (mask & (1 << i) &&
570		    uinput_write_event(fd, EV_LED, led_codes[i],
571					state & (1 << i) ? 1 : 0) < 0)
572			return (-1);
573	}
574
575	return (0);
576}
577
578/*
579 * Process status change from evdev
580 */
581int32_t
582uinput_kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)
583{
584	struct input_event ie;
585	int32_t leds, oleds;
586	size_t i;
587
588	assert(s != NULL);
589	assert(s->vkbd >= 0);
590	assert(len == sizeof(struct input_event));
591
592	memcpy(&ie, data, sizeof(ie));
593	switch (ie.type) {
594	case EV_LED:
595		ioctl(s->vkbd, KDGETLED, &oleds);
596		leds = oleds;
597		for (i = 0; i < nitems(led_codes); i++) {
598			if (led_codes[i] == ie.code) {
599				if (ie.value)
600					leds |= 1 << i;
601				else
602					leds &= ~(1 << i);
603				if (leds != oleds)
604					ioctl(s->vkbd, KDSETLED, leds);
605				break;
606			}
607		}
608		break;
609	case EV_REP:
610		/* FALLTHROUGH. Repeats are handled by evdev subsystem */
611	default:
612		break;
613	}
614
615	return (0);
616}
617