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 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 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 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 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 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 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 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 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 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 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