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 * $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 } 619