1/* 2 * kbd.c 3 */ 4 5/*- 6 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 7 * 8 * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $ 33 * $FreeBSD$ 34 */ 35 36#include <sys/consio.h> 37#include <sys/ioctl.h> 38#include <sys/kbio.h> 39#include <sys/queue.h> 40#include <sys/wait.h> 41#include <assert.h> 42#define L2CAP_SOCKET_CHECKED 43#include <bluetooth.h> 44#include <dev/usb/usb.h> 45#include <dev/usb/usbhid.h> 46#include <dev/vkbd/vkbd_var.h> 47#include <errno.h> 48#include <fcntl.h> 49#include <limits.h> 50#include <stdarg.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <string.h> 54#include <syslog.h> 55#include <unistd.h> 56#include <usbhid.h> 57#include "bthid_config.h" 58#include "bthidd.h" 59#include "btuinput.h" 60#include "kbd.h" 61 62static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); 63static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob); 64static void uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd); 65 66/* 67 * HID code to PS/2 set 1 code translation table. 68 * 69 * http://www.microsoft.com/whdc/device/input/Scancode.mspx 70 * 71 * The table only contains "make" (key pressed) codes. 72 * The "break" (key released) code is generated as "make" | 0x80 73 */ 74 75#define E0PREFIX (1U << 31) 76#define NOBREAK (1 << 30) 77#define CODEMASK (~(E0PREFIX|NOBREAK)) 78 79static int32_t const x[] = 80{ 81/*==================================================*/ 82/* Name HID code Make Break*/ 83/*==================================================*/ 84/* No Event 00 */ -1, /* None */ 85/* Overrun Error 01 */ NOBREAK|0xFF, /* None */ 86/* POST Fail 02 */ NOBREAK|0xFC, /* None */ 87/* ErrorUndefined 03 */ -1, /* Unassigned */ 88/* a A 04 */ 0x1E, /* 9E */ 89/* b B 05 */ 0x30, /* B0 */ 90/* c C 06 */ 0x2E, /* AE */ 91/* d D 07 */ 0x20, /* A0 */ 92/* e E 08 */ 0x12, /* 92 */ 93/* f F 09 */ 0x21, /* A1 */ 94/* g G 0A */ 0x22, /* A2 */ 95/* h H 0B */ 0x23, /* A3 */ 96/* i I 0C */ 0x17, /* 97 */ 97/* j J 0D */ 0x24, /* A4 */ 98/* k K 0E */ 0x25, /* A5 */ 99/* l L 0F */ 0x26, /* A6 */ 100/* m M 10 */ 0x32, /* B2 */ 101/* n N 11 */ 0x31, /* B1 */ 102/* o O 12 */ 0x18, /* 98 */ 103/* p P 13 */ 0x19, /* 99 */ 104/* q Q 14 */ 0x10, /* 90 */ 105/* r R 15 */ 0x13, /* 93 */ 106/* s S 16 */ 0x1F, /* 9F */ 107/* t T 17 */ 0x14, /* 94 */ 108/* u U 18 */ 0x16, /* 96 */ 109/* v V 19 */ 0x2F, /* AF */ 110/* w W 1A */ 0x11, /* 91 */ 111/* x X 1B */ 0x2D, /* AD */ 112/* y Y 1C */ 0x15, /* 95 */ 113/* z Z 1D */ 0x2C, /* AC */ 114/* 1 ! 1E */ 0x02, /* 82 */ 115/* 2 @ 1F */ 0x03, /* 83 */ 116/* 3 # 20 */ 0x04, /* 84 */ 117/* 4 $ 21 */ 0x05, /* 85 */ 118/* 5 % 22 */ 0x06, /* 86 */ 119/* 6 ^ 23 */ 0x07, /* 87 */ 120/* 7 & 24 */ 0x08, /* 88 */ 121/* 8 * 25 */ 0x09, /* 89 */ 122/* 9 ( 26 */ 0x0A, /* 8A */ 123/* 0 ) 27 */ 0x0B, /* 8B */ 124/* Return 28 */ 0x1C, /* 9C */ 125/* Escape 29 */ 0x01, /* 81 */ 126/* Backspace 2A */ 0x0E, /* 8E */ 127/* Tab 2B */ 0x0F, /* 8F */ 128/* Space 2C */ 0x39, /* B9 */ 129/* - _ 2D */ 0x0C, /* 8C */ 130/* = + 2E */ 0x0D, /* 8D */ 131/* [ { 2F */ 0x1A, /* 9A */ 132/* ] } 30 */ 0x1B, /* 9B */ 133/* \ | 31 */ 0x2B, /* AB */ 134/* Europe 1 32 */ 0x2B, /* AB */ 135/* ; : 33 */ 0x27, /* A7 */ 136/* " ' 34 */ 0x28, /* A8 */ 137/* ` ~ 35 */ 0x29, /* A9 */ 138/* comma < 36 */ 0x33, /* B3 */ 139/* . > 37 */ 0x34, /* B4 */ 140/* / ? 38 */ 0x35, /* B5 */ 141/* Caps Lock 39 */ 0x3A, /* BA */ 142/* F1 3A */ 0x3B, /* BB */ 143/* F2 3B */ 0x3C, /* BC */ 144/* F3 3C */ 0x3D, /* BD */ 145/* F4 3D */ 0x3E, /* BE */ 146/* F5 3E */ 0x3F, /* BF */ 147/* F6 3F */ 0x40, /* C0 */ 148/* F7 40 */ 0x41, /* C1 */ 149/* F8 41 */ 0x42, /* C2 */ 150/* F9 42 */ 0x43, /* C3 */ 151/* F10 43 */ 0x44, /* C4 */ 152/* F11 44 */ 0x57, /* D7 */ 153/* F12 45 */ 0x58, /* D8 */ 154/* Print Screen 46 */ E0PREFIX|0x37, /* E0 B7 */ 155/* Scroll Lock 47 */ 0x46, /* C6 */ 156#if 0 157/* Break (Ctrl-Pause) 48 */ E0 46 E0 C6, /* None */ 158/* Pause 48 */ E1 1D 45 E1 9D C5, /* None */ 159#else 160/* Break (Ctrl-Pause)/Pause 48 */ NOBREAK /* Special case */, /* None */ 161#endif 162/* Insert 49 */ E0PREFIX|0x52, /* E0 D2 */ 163/* Home 4A */ E0PREFIX|0x47, /* E0 C7 */ 164/* Page Up 4B */ E0PREFIX|0x49, /* E0 C9 */ 165/* Delete 4C */ E0PREFIX|0x53, /* E0 D3 */ 166/* End 4D */ E0PREFIX|0x4F, /* E0 CF */ 167/* Page Down 4E */ E0PREFIX|0x51, /* E0 D1 */ 168/* Right Arrow 4F */ E0PREFIX|0x4D, /* E0 CD */ 169/* Left Arrow 50 */ E0PREFIX|0x4B, /* E0 CB */ 170/* Down Arrow 51 */ E0PREFIX|0x50, /* E0 D0 */ 171/* Up Arrow 52 */ E0PREFIX|0x48, /* E0 C8 */ 172/* Num Lock 53 */ 0x45, /* C5 */ 173/* Keypad / 54 */ E0PREFIX|0x35, /* E0 B5 */ 174/* Keypad * 55 */ 0x37, /* B7 */ 175/* Keypad - 56 */ 0x4A, /* CA */ 176/* Keypad + 57 */ 0x4E, /* CE */ 177/* Keypad Enter 58 */ E0PREFIX|0x1C, /* E0 9C */ 178/* Keypad 1 End 59 */ 0x4F, /* CF */ 179/* Keypad 2 Down 5A */ 0x50, /* D0 */ 180/* Keypad 3 PageDn 5B */ 0x51, /* D1 */ 181/* Keypad 4 Left 5C */ 0x4B, /* CB */ 182/* Keypad 5 5D */ 0x4C, /* CC */ 183/* Keypad 6 Right 5E */ 0x4D, /* CD */ 184/* Keypad 7 Home 5F */ 0x47, /* C7 */ 185/* Keypad 8 Up 60 */ 0x48, /* C8 */ 186/* Keypad 9 PageUp 61 */ 0x49, /* C9 */ 187/* Keypad 0 Insert 62 */ 0x52, /* D2 */ 188/* Keypad . Delete 63 */ 0x53, /* D3 */ 189/* Europe 2 64 */ 0x56, /* D6 */ 190/* App 65 */ E0PREFIX|0x5D, /* E0 DD */ 191/* Keyboard Power 66 */ E0PREFIX|0x5E, /* E0 DE */ 192/* Keypad = 67 */ 0x59, /* D9 */ 193/* F13 68 */ 0x64, /* E4 */ 194/* F14 69 */ 0x65, /* E5 */ 195/* F15 6A */ 0x66, /* E6 */ 196/* F16 6B */ 0x67, /* E7 */ 197/* F17 6C */ 0x68, /* E8 */ 198/* F18 6D */ 0x69, /* E9 */ 199/* F19 6E */ 0x6A, /* EA */ 200/* F20 6F */ 0x6B, /* EB */ 201/* F21 70 */ 0x6C, /* EC */ 202/* F22 71 */ 0x6D, /* ED */ 203/* F23 72 */ 0x6E, /* EE */ 204/* F24 73 */ 0x76, /* F6 */ 205/* Keyboard Execute 74 */ -1, /* Unassigned */ 206/* Keyboard Help 75 */ -1, /* Unassigned */ 207/* Keyboard Menu 76 */ -1, /* Unassigned */ 208/* Keyboard Select 77 */ -1, /* Unassigned */ 209/* Keyboard Stop 78 */ -1, /* Unassigned */ 210/* Keyboard Again 79 */ -1, /* Unassigned */ 211/* Keyboard Undo 7A */ -1, /* Unassigned */ 212/* Keyboard Cut 7B */ -1, /* Unassigned */ 213/* Keyboard Copy 7C */ -1, /* Unassigned */ 214/* Keyboard Paste 7D */ -1, /* Unassigned */ 215/* Keyboard Find 7E */ -1, /* Unassigned */ 216/* Keyboard Mute 7F */ -1, /* Unassigned */ 217/* Keyboard Volume Up 80 */ -1, /* Unassigned */ 218/* Keyboard Volume Dn 81 */ -1, /* Unassigned */ 219/* Keyboard Locking Caps Lock 82 */ -1, /* Unassigned */ 220/* Keyboard Locking Num Lock 83 */ -1, /* Unassigned */ 221/* Keyboard Locking Scroll Lock 84 */ -1, /* Unassigned */ 222/* Keypad comma 85 */ 0x7E, /* FE */ 223/* Keyboard Equal Sign 86 */ -1, /* Unassigned */ 224/* Keyboard Int'l 1 87 */ 0x73, /* F3 */ 225/* Keyboard Int'l 2 88 */ 0x70, /* F0 */ 226/* Keyboard Int'l 2 89 */ 0x7D, /* FD */ 227/* Keyboard Int'l 4 8A */ 0x79, /* F9 */ 228/* Keyboard Int'l 5 8B */ 0x7B, /* FB */ 229/* Keyboard Int'l 6 8C */ 0x5C, /* DC */ 230/* Keyboard Int'l 7 8D */ -1, /* Unassigned */ 231/* Keyboard Int'l 8 8E */ -1, /* Unassigned */ 232/* Keyboard Int'l 9 8F */ -1, /* Unassigned */ 233/* Keyboard Lang 1 90 */ 0x71, /* Kana */ 234/* Keyboard Lang 2 91 */ 0x72, /* Eisu */ 235/* Keyboard Lang 3 92 */ 0x78, /* F8 */ 236/* Keyboard Lang 4 93 */ 0x77, /* F7 */ 237/* Keyboard Lang 5 94 */ 0x76, /* F6 */ 238/* Keyboard Lang 6 95 */ -1, /* Unassigned */ 239/* Keyboard Lang 7 96 */ -1, /* Unassigned */ 240/* Keyboard Lang 8 97 */ -1, /* Unassigned */ 241/* Keyboard Lang 9 98 */ -1, /* Unassigned */ 242/* Keyboard Alternate Erase 99 */ -1, /* Unassigned */ 243/* Keyboard SysReq/Attention 9A */ -1, /* Unassigned */ 244/* Keyboard Cancel 9B */ -1, /* Unassigned */ 245/* Keyboard Clear 9C */ -1, /* Unassigned */ 246/* Keyboard Prior 9D */ -1, /* Unassigned */ 247/* Keyboard Return 9E */ -1, /* Unassigned */ 248/* Keyboard Separator 9F */ -1, /* Unassigned */ 249/* Keyboard Out A0 */ -1, /* Unassigned */ 250/* Keyboard Oper A1 */ -1, /* Unassigned */ 251/* Keyboard Clear/Again A2 */ -1, /* Unassigned */ 252/* Keyboard CrSel/Props A3 */ -1, /* Unassigned */ 253/* Keyboard ExSel A4 */ -1, /* Unassigned */ 254/* Reserved A5 */ -1, /* Reserved */ 255/* Reserved A6 */ -1, /* Reserved */ 256/* Reserved A7 */ -1, /* Reserved */ 257/* Reserved A8 */ -1, /* Reserved */ 258/* Reserved A9 */ -1, /* Reserved */ 259/* Reserved AA */ -1, /* Reserved */ 260/* Reserved AB */ -1, /* Reserved */ 261/* Reserved AC */ -1, /* Reserved */ 262/* Reserved AD */ -1, /* Reserved */ 263/* Reserved AE */ -1, /* Reserved */ 264/* Reserved AF */ -1, /* Reserved */ 265/* Reserved B0 */ -1, /* Reserved */ 266/* Reserved B1 */ -1, /* Reserved */ 267/* Reserved B2 */ -1, /* Reserved */ 268/* Reserved B3 */ -1, /* Reserved */ 269/* Reserved B4 */ -1, /* Reserved */ 270/* Reserved B5 */ -1, /* Reserved */ 271/* Reserved B6 */ -1, /* Reserved */ 272/* Reserved B7 */ -1, /* Reserved */ 273/* Reserved B8 */ -1, /* Reserved */ 274/* Reserved B9 */ -1, /* Reserved */ 275/* Reserved BA */ -1, /* Reserved */ 276/* Reserved BB */ -1, /* Reserved */ 277/* Reserved BC */ -1, /* Reserved */ 278/* Reserved BD */ -1, /* Reserved */ 279/* Reserved BE */ -1, /* Reserved */ 280/* Reserved BF */ -1, /* Reserved */ 281/* Reserved C0 */ -1, /* Reserved */ 282/* Reserved C1 */ -1, /* Reserved */ 283/* Reserved C2 */ -1, /* Reserved */ 284/* Reserved C3 */ -1, /* Reserved */ 285/* Reserved C4 */ -1, /* Reserved */ 286/* Reserved C5 */ -1, /* Reserved */ 287/* Reserved C6 */ -1, /* Reserved */ 288/* Reserved C7 */ -1, /* Reserved */ 289/* Reserved C8 */ -1, /* Reserved */ 290/* Reserved C9 */ -1, /* Reserved */ 291/* Reserved CA */ -1, /* Reserved */ 292/* Reserved CB */ -1, /* Reserved */ 293/* Reserved CC */ -1, /* Reserved */ 294/* Reserved CD */ -1, /* Reserved */ 295/* Reserved CE */ -1, /* Reserved */ 296/* Reserved CF */ -1, /* Reserved */ 297/* Reserved D0 */ -1, /* Reserved */ 298/* Reserved D1 */ -1, /* Reserved */ 299/* Reserved D2 */ -1, /* Reserved */ 300/* Reserved D3 */ -1, /* Reserved */ 301/* Reserved D4 */ -1, /* Reserved */ 302/* Reserved D5 */ -1, /* Reserved */ 303/* Reserved D6 */ -1, /* Reserved */ 304/* Reserved D7 */ -1, /* Reserved */ 305/* Reserved D8 */ -1, /* Reserved */ 306/* Reserved D9 */ -1, /* Reserved */ 307/* Reserved DA */ -1, /* Reserved */ 308/* Reserved DB */ -1, /* Reserved */ 309/* Reserved DC */ -1, /* Reserved */ 310/* Reserved DD */ -1, /* Reserved */ 311/* Reserved DE */ -1, /* Reserved */ 312/* Reserved DF */ -1, /* Reserved */ 313/* Left Control E0 */ 0x1D, /* 9D */ 314/* Left Shift E1 */ 0x2A, /* AA */ 315/* Left Alt E2 */ 0x38, /* B8 */ 316/* Left GUI E3 */ E0PREFIX|0x5B, /* E0 DB */ 317/* Right Control E4 */ E0PREFIX|0x1D, /* E0 9D */ 318/* Right Shift E5 */ 0x36, /* B6 */ 319/* Right Alt E6 */ E0PREFIX|0x38, /* E0 B8 */ 320/* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */ 321}; 322 323#define xsize ((int32_t)(sizeof(x)/sizeof(x[0]))) 324 325/* 326 * Get a max HID keycode (aligned) 327 */ 328 329int32_t 330kbd_maxkey(void) 331{ 332 return (xsize); 333} 334 335/* 336 * Process keys 337 */ 338 339int32_t 340kbd_process_keys(bthid_session_p s) 341{ 342 bitstr_t diff[bitstr_size(xsize)]; 343 int32_t f1, f2, i; 344 345 assert(s != NULL); 346 assert(s->srv != NULL); 347 348 /* Check if the new keys have been pressed */ 349 bit_ffs(s->keys1, xsize, &f1); 350 351 /* Check if old keys still pressed */ 352 bit_ffs(s->keys2, xsize, &f2); 353 354 if (f1 == -1) { 355 /* no new key pressed */ 356 if (f2 != -1) { 357 /* release old keys */ 358 kbd_write(s->keys2, f2, 0, s->vkbd); 359 uinput_kbd_write(s->keys2, f2, 0, s->ukbd); 360 memset(s->keys2, 0, bitstr_size(xsize)); 361 } 362 363 return (0); 364 } 365 366 if (f2 == -1) { 367 /* no old keys, but new keys pressed */ 368 assert(f1 != -1); 369 370 memcpy(s->keys2, s->keys1, bitstr_size(xsize)); 371 kbd_write(s->keys1, f1, 1, s->vkbd); 372 uinput_kbd_write(s->keys1, f1, 1, s->ukbd); 373 memset(s->keys1, 0, bitstr_size(xsize)); 374 375 return (0); 376 } 377 378 /* new keys got pressed, old keys got released */ 379 memset(diff, 0, bitstr_size(xsize)); 380 381 for (i = f2; i < xsize; i ++) { 382 if (bit_test(s->keys2, i)) { 383 if (!bit_test(s->keys1, i)) { 384 bit_clear(s->keys2, i); 385 bit_set(diff, i); 386 } 387 } 388 } 389 390 for (i = f1; i < xsize; i++) { 391 if (bit_test(s->keys1, i)) { 392 if (!bit_test(s->keys2, i)) 393 bit_set(s->keys2, i); 394 else 395 bit_clear(s->keys1, i); 396 } 397 } 398 399 bit_ffs(diff, xsize, &f2); 400 if (f2 > 0) { 401 kbd_write(diff, f2, 0, s->vkbd); 402 uinput_kbd_write(diff, f2, 0, s->ukbd); 403 } 404 405 bit_ffs(s->keys1, xsize, &f1); 406 if (f1 > 0) { 407 kbd_write(s->keys1, f1, 1, s->vkbd); 408 uinput_kbd_write(s->keys1, f1, 1, s->ukbd); 409 memset(s->keys1, 0, bitstr_size(xsize)); 410 } 411 412 return (0); 413} 414 415/* 416 * Translate given keymap and write keyscodes 417 */ 418void 419uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd) 420{ 421 int32_t i; 422 423 if (fd >= 0) { 424 for (i = fb; i < xsize; i++) { 425 if (bit_test(m, i)) 426 uinput_rep_key(fd, i, make); 427 } 428 } 429} 430 431/* 432 * Translate given keymap and write keyscodes 433 */ 434 435static void 436kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd) 437{ 438 int32_t i, *b, *eob, n, buf[64]; 439 440 b = buf; 441 eob = b + sizeof(buf)/sizeof(buf[0]); 442 i = fb; 443 444 while (i < xsize) { 445 if (bit_test(m, i)) { 446 n = kbd_xlate(i, make, b, eob); 447 if (n == -1) { 448 write(fd, buf, (b - buf) * sizeof(buf[0])); 449 b = buf; 450 continue; 451 } 452 453 b += n; 454 } 455 456 i ++; 457 } 458 459 if (b != buf) 460 write(fd, buf, (b - buf) * sizeof(buf[0])); 461} 462 463/* 464 * Translate HID code into PS/2 code and put codes into buffer b. 465 * Returns the number of codes put in b. Return -1 if buffer has not 466 * enough space. 467 */ 468 469#undef PUT 470#define PUT(c, n, b, eob) \ 471do { \ 472 if ((b) >= (eob)) \ 473 return (-1); \ 474 *(b) = (c); \ 475 (b) ++; \ 476 (n) ++; \ 477} while (0) 478 479static int32_t 480kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob) 481{ 482 int32_t c, n; 483 484 n = 0; 485 486 if (code >= xsize) 487 return (0); /* HID code is not in the table */ 488 489 /* Handle special case - Pause/Break */ 490 if (code == 0x48) { 491 if (!make) 492 return (0); /* No break code */ 493 494#if 0 495XXX FIXME 496 if (ctrl_is_pressed) { 497 /* Break (Ctrl-Pause) */ 498 PUT(0xe0, n, b, eob); 499 PUT(0x46, n, b, eob); 500 PUT(0xe0, n, b, eob); 501 PUT(0xc6, n, b, eob); 502 } else { 503 /* Pause */ 504 PUT(0xe1, n, b, eob); 505 PUT(0x1d, n, b, eob); 506 PUT(0x45, n, b, eob); 507 PUT(0xe1, n, b, eob); 508 PUT(0x9d, n, b, eob); 509 PUT(0xc5, n, b, eob); 510 } 511#endif 512 513 return (n); 514 } 515 516 if ((c = x[code]) == -1) 517 return (0); /* HID code translation is not defined */ 518 519 if (make) { 520 if (c & E0PREFIX) 521 PUT(0xe0, n, b, eob); 522 523 PUT((c & CODEMASK), n, b, eob); 524 } else if (!(c & NOBREAK)) { 525 if (c & E0PREFIX) 526 PUT(0xe0, n, b, eob); 527 528 PUT((0x80|(c & CODEMASK)), n, b, eob); 529 } 530 531 return (n); 532} 533 534/* 535 * Process status change from vkbd(4) 536 */ 537 538int32_t 539kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len) 540{ 541 vkbd_status_t st; 542 uint8_t found, report_id; 543 hid_device_p hid_device; 544 hid_data_t d; 545 hid_item_t h; 546 uint8_t leds_mask = 0; 547 548 assert(s != NULL); 549 assert(len == sizeof(vkbd_status_t)); 550 551 memcpy(&st, data, sizeof(st)); 552 found = 0; 553 report_id = NO_REPORT_ID; 554 555 hid_device = get_hid_device(&s->bdaddr); 556 assert(hid_device != NULL); 557 558 data[0] = 0xa2; /* DATA output (HID output report) */ 559 data[1] = 0x00; 560 data[2] = 0x00; 561 562 for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1); 563 hid_get_item(d, &h) > 0; ) { 564 if (HID_PAGE(h.usage) == HUP_LEDS) { 565 found++; 566 567 if (report_id == NO_REPORT_ID) 568 report_id = h.report_ID; 569 else if (h.report_ID != report_id) 570 syslog(LOG_WARNING, "Output HID report IDs " \ 571 "for %s do not match: %d vs. %d. " \ 572 "Please report", 573 bt_ntoa(&s->bdaddr, NULL), 574 h.report_ID, report_id); 575 576 switch(HID_USAGE(h.usage)) { 577 case 0x01: /* Num Lock LED */ 578 if (st.leds & LED_NUM) 579 hid_set_data(&data[1], &h, 1); 580 leds_mask |= LED_NUM; 581 break; 582 583 case 0x02: /* Caps Lock LED */ 584 if (st.leds & LED_CAP) 585 hid_set_data(&data[1], &h, 1); 586 leds_mask |= LED_CAP; 587 break; 588 589 case 0x03: /* Scroll Lock LED */ 590 if (st.leds & LED_SCR) 591 hid_set_data(&data[1], &h, 1); 592 leds_mask |= LED_SCR; 593 break; 594 595 /* XXX add other LEDs ? */ 596 } 597 } 598 } 599 hid_end_parse(d); 600 601 if (report_id != NO_REPORT_ID) { 602 data[2] = data[1]; 603 data[1] = report_id; 604 } 605 606 if (found) 607 write(s->intr, data, (report_id != NO_REPORT_ID) ? 3 : 2); 608 609 if (found && s->srv->uinput && hid_device->keyboard) 610 uinput_rep_leds(s->ukbd, st.leds, leds_mask); 611 612 return (0); 613} 614 615