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