]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidd/btuinput.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / bthidd / btuinput.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  * $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
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
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
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
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
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
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
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
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
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
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 }