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
55 static 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
66 static 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
74 static 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 */
145 static 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
299 static int32_t
uinput_open_common(hid_device_p const p,bdaddr_p local,const uint8_t * name)300 uinput_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 */
338 int32_t
uinput_open_mouse(hid_device_p const p,bdaddr_p local)339 uinput_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
368 bail_out:
369 if (fd >= 0)
370 close(fd);
371 return (-1);
372 }
373
374 /*
375 * Setup uinput keyboard
376 */
377 int32_t
uinput_open_keyboard(hid_device_p const p,bdaddr_p local)378 uinput_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
416 bail_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
429 static int32_t
uinput_get_rcpt_mask(void)430 uinput_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
459 static int32_t
uinput_write_event(int32_t fd,uint16_t type,uint16_t code,int32_t value)460 uinput_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
473 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)474 uinput_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 */
510 int32_t
uinput_rep_key(int32_t fd,int32_t key,int32_t make)511 uinput_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 */
533 int32_t
uinput_rep_cons(int32_t fd,int32_t key,int32_t make)534 uinput_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 */
556 int32_t
uinput_rep_leds(int32_t fd,int state,int mask)557 uinput_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 */
581 int32_t
uinput_kbd_status_changed(bthid_session_p s,uint8_t * data,int32_t len)582 uinput_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