xref: /freebsd/usr.sbin/bluetooth/bthidd/kbd.c (revision 6bd85498ec2df62aeb8cf9c476daac8a4d9a7a4b)
13adfd74aSMaksim Yevmenkin /*
23adfd74aSMaksim Yevmenkin  * kbd.c
37aebfa93SMaksim Yevmenkin  */
47aebfa93SMaksim Yevmenkin 
57aebfa93SMaksim Yevmenkin /*-
64d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
71de7b4b8SPedro F. Giffuni  *
87aebfa93SMaksim Yevmenkin  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
93adfd74aSMaksim Yevmenkin  * All rights reserved.
103adfd74aSMaksim Yevmenkin  *
113adfd74aSMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
123adfd74aSMaksim Yevmenkin  * modification, are permitted provided that the following conditions
133adfd74aSMaksim Yevmenkin  * are met:
143adfd74aSMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
153adfd74aSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
163adfd74aSMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
173adfd74aSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
183adfd74aSMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
193adfd74aSMaksim Yevmenkin  *
203adfd74aSMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
213adfd74aSMaksim Yevmenkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
223adfd74aSMaksim Yevmenkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
233adfd74aSMaksim Yevmenkin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
243adfd74aSMaksim Yevmenkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
253adfd74aSMaksim Yevmenkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
263adfd74aSMaksim Yevmenkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
273adfd74aSMaksim Yevmenkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
283adfd74aSMaksim Yevmenkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
293adfd74aSMaksim Yevmenkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
303adfd74aSMaksim Yevmenkin  * SUCH DAMAGE.
313adfd74aSMaksim Yevmenkin  *
327aebfa93SMaksim Yevmenkin  * $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $
333adfd74aSMaksim Yevmenkin  */
343adfd74aSMaksim Yevmenkin 
353adfd74aSMaksim Yevmenkin #include <sys/consio.h>
363adfd74aSMaksim Yevmenkin #include <sys/ioctl.h>
373adfd74aSMaksim Yevmenkin #include <sys/kbio.h>
38fdbf7cabSElyes Haouas #include <sys/param.h>
393adfd74aSMaksim Yevmenkin #include <sys/queue.h>
403adfd74aSMaksim Yevmenkin #include <sys/wait.h>
413adfd74aSMaksim Yevmenkin #include <assert.h>
428d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
433adfd74aSMaksim Yevmenkin #include <bluetooth.h>
447aebfa93SMaksim Yevmenkin #include <dev/usb/usb.h>
457aebfa93SMaksim Yevmenkin #include <dev/usb/usbhid.h>
467aebfa93SMaksim Yevmenkin #include <dev/vkbd/vkbd_var.h>
473adfd74aSMaksim Yevmenkin #include <errno.h>
483adfd74aSMaksim Yevmenkin #include <fcntl.h>
493adfd74aSMaksim Yevmenkin #include <limits.h>
503adfd74aSMaksim Yevmenkin #include <stdarg.h>
513adfd74aSMaksim Yevmenkin #include <stdio.h>
523adfd74aSMaksim Yevmenkin #include <stdlib.h>
533adfd74aSMaksim Yevmenkin #include <string.h>
543adfd74aSMaksim Yevmenkin #include <syslog.h>
553adfd74aSMaksim Yevmenkin #include <unistd.h>
567aebfa93SMaksim Yevmenkin #include <usbhid.h>
577aebfa93SMaksim Yevmenkin #include "bthid_config.h"
583adfd74aSMaksim Yevmenkin #include "bthidd.h"
5944af5666SVladimir Kondratyev #include "btuinput.h"
603adfd74aSMaksim Yevmenkin #include "kbd.h"
613adfd74aSMaksim Yevmenkin 
627aebfa93SMaksim Yevmenkin static void	kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);
637aebfa93SMaksim Yevmenkin static int32_t	kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob);
6444af5666SVladimir Kondratyev static void	uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);
653adfd74aSMaksim Yevmenkin 
663adfd74aSMaksim Yevmenkin /*
673adfd74aSMaksim Yevmenkin  * HID code to PS/2 set 1 code translation table.
683adfd74aSMaksim Yevmenkin  *
693adfd74aSMaksim Yevmenkin  * http://www.microsoft.com/whdc/device/input/Scancode.mspx
703adfd74aSMaksim Yevmenkin  *
713adfd74aSMaksim Yevmenkin  * The table only contains "make" (key pressed) codes.
723adfd74aSMaksim Yevmenkin  * The "break" (key released) code is generated as "make" | 0x80
733adfd74aSMaksim Yevmenkin  */
743adfd74aSMaksim Yevmenkin 
757a22215cSEitan Adler #define E0PREFIX	(1U << 31)
763adfd74aSMaksim Yevmenkin #define NOBREAK		(1 << 30)
773adfd74aSMaksim Yevmenkin #define CODEMASK	(~(E0PREFIX|NOBREAK))
783adfd74aSMaksim Yevmenkin 
797aebfa93SMaksim Yevmenkin static int32_t const	x[] =
803adfd74aSMaksim Yevmenkin {
813adfd74aSMaksim Yevmenkin /*==================================================*/
823adfd74aSMaksim Yevmenkin /* Name                   HID code    Make     Break*/
833adfd74aSMaksim Yevmenkin /*==================================================*/
843adfd74aSMaksim Yevmenkin /* No Event                     00 */ -1,   /* None */
853adfd74aSMaksim Yevmenkin /* Overrun Error                01 */ NOBREAK|0xFF, /* None */
863adfd74aSMaksim Yevmenkin /* POST Fail                    02 */ NOBREAK|0xFC, /* None */
873adfd74aSMaksim Yevmenkin /* ErrorUndefined               03 */ -1,   /* Unassigned */
883adfd74aSMaksim Yevmenkin /* a A                          04 */ 0x1E, /* 9E */
893adfd74aSMaksim Yevmenkin /* b B                          05 */ 0x30, /* B0 */
903adfd74aSMaksim Yevmenkin /* c C                          06 */ 0x2E, /* AE */
913adfd74aSMaksim Yevmenkin /* d D                          07 */ 0x20, /* A0 */
923adfd74aSMaksim Yevmenkin /* e E                          08 */ 0x12, /* 92 */
933adfd74aSMaksim Yevmenkin /* f F                          09 */ 0x21, /* A1 */
943adfd74aSMaksim Yevmenkin /* g G                          0A */ 0x22, /* A2 */
953adfd74aSMaksim Yevmenkin /* h H                          0B */ 0x23, /* A3 */
963adfd74aSMaksim Yevmenkin /* i I                          0C */ 0x17, /* 97 */
973adfd74aSMaksim Yevmenkin /* j J                          0D */ 0x24, /* A4 */
983adfd74aSMaksim Yevmenkin /* k K                          0E */ 0x25, /* A5 */
993adfd74aSMaksim Yevmenkin /* l L                          0F */ 0x26, /* A6 */
1003adfd74aSMaksim Yevmenkin /* m M                          10 */ 0x32, /* B2 */
1013adfd74aSMaksim Yevmenkin /* n N                          11 */ 0x31, /* B1 */
1023adfd74aSMaksim Yevmenkin /* o O                          12 */ 0x18, /* 98 */
1033adfd74aSMaksim Yevmenkin /* p P                          13 */ 0x19, /* 99 */
1043adfd74aSMaksim Yevmenkin /* q Q                          14 */ 0x10, /* 90 */
1053adfd74aSMaksim Yevmenkin /* r R                          15 */ 0x13, /* 93 */
1063adfd74aSMaksim Yevmenkin /* s S                          16 */ 0x1F, /* 9F */
1073adfd74aSMaksim Yevmenkin /* t T                          17 */ 0x14, /* 94 */
1083adfd74aSMaksim Yevmenkin /* u U                          18 */ 0x16, /* 96 */
1093adfd74aSMaksim Yevmenkin /* v V                          19 */ 0x2F, /* AF */
1103adfd74aSMaksim Yevmenkin /* w W                          1A */ 0x11, /* 91 */
1113adfd74aSMaksim Yevmenkin /* x X                          1B */ 0x2D, /* AD */
1123adfd74aSMaksim Yevmenkin /* y Y                          1C */ 0x15, /* 95 */
1133adfd74aSMaksim Yevmenkin /* z Z                          1D */ 0x2C, /* AC */
1143adfd74aSMaksim Yevmenkin /* 1 !                          1E */ 0x02, /* 82 */
1153adfd74aSMaksim Yevmenkin /* 2 @                          1F */ 0x03, /* 83 */
1163adfd74aSMaksim Yevmenkin /* 3 #                          20 */ 0x04, /* 84 */
1173adfd74aSMaksim Yevmenkin /* 4 $                          21 */ 0x05, /* 85 */
1183adfd74aSMaksim Yevmenkin /* 5 %                          22 */ 0x06, /* 86 */
1193adfd74aSMaksim Yevmenkin /* 6 ^                          23 */ 0x07, /* 87 */
1203adfd74aSMaksim Yevmenkin /* 7 &                          24 */ 0x08, /* 88 */
1213adfd74aSMaksim Yevmenkin /* 8 *                          25 */ 0x09, /* 89 */
1223adfd74aSMaksim Yevmenkin /* 9 (                          26 */ 0x0A, /* 8A */
1233adfd74aSMaksim Yevmenkin /* 0 )                          27 */ 0x0B, /* 8B */
1243adfd74aSMaksim Yevmenkin /* Return                       28 */ 0x1C, /* 9C */
1253adfd74aSMaksim Yevmenkin /* Escape                       29 */ 0x01, /* 81 */
1263adfd74aSMaksim Yevmenkin /* Backspace                    2A */ 0x0E, /* 8E */
1273adfd74aSMaksim Yevmenkin /* Tab                          2B */ 0x0F, /* 8F */
1283adfd74aSMaksim Yevmenkin /* Space                        2C */ 0x39, /* B9 */
1293adfd74aSMaksim Yevmenkin /* - _                          2D */ 0x0C, /* 8C */
1303adfd74aSMaksim Yevmenkin /* = +                          2E */ 0x0D, /* 8D */
1313adfd74aSMaksim Yevmenkin /* [ {                          2F */ 0x1A, /* 9A */
1323adfd74aSMaksim Yevmenkin /* ] }                          30 */ 0x1B, /* 9B */
1333adfd74aSMaksim Yevmenkin /* \ |                          31 */ 0x2B, /* AB */
1343adfd74aSMaksim Yevmenkin /* Europe 1                     32 */ 0x2B, /* AB */
1353adfd74aSMaksim Yevmenkin /* ; :                          33 */ 0x27, /* A7 */
1363adfd74aSMaksim Yevmenkin /* " '                          34 */ 0x28, /* A8 */
1373adfd74aSMaksim Yevmenkin /* ` ~                          35 */ 0x29, /* A9 */
1383adfd74aSMaksim Yevmenkin /* comma <                      36 */ 0x33, /* B3 */
1393adfd74aSMaksim Yevmenkin /* . >                          37 */ 0x34, /* B4 */
1403adfd74aSMaksim Yevmenkin /* / ?                          38 */ 0x35, /* B5 */
1413adfd74aSMaksim Yevmenkin /* Caps Lock                    39 */ 0x3A, /* BA */
1423adfd74aSMaksim Yevmenkin /* F1                           3A */ 0x3B, /* BB */
1433adfd74aSMaksim Yevmenkin /* F2                           3B */ 0x3C, /* BC */
1443adfd74aSMaksim Yevmenkin /* F3                           3C */ 0x3D, /* BD */
1453adfd74aSMaksim Yevmenkin /* F4                           3D */ 0x3E, /* BE */
1463adfd74aSMaksim Yevmenkin /* F5                           3E */ 0x3F, /* BF */
1473adfd74aSMaksim Yevmenkin /* F6                           3F */ 0x40, /* C0 */
1483adfd74aSMaksim Yevmenkin /* F7                           40 */ 0x41, /* C1 */
1493adfd74aSMaksim Yevmenkin /* F8                           41 */ 0x42, /* C2 */
1503adfd74aSMaksim Yevmenkin /* F9                           42 */ 0x43, /* C3 */
1513adfd74aSMaksim Yevmenkin /* F10                          43 */ 0x44, /* C4 */
1523adfd74aSMaksim Yevmenkin /* F11                          44 */ 0x57, /* D7 */
1533adfd74aSMaksim Yevmenkin /* F12                          45 */ 0x58, /* D8 */
154610e662eSMarkus Brueffer /* Print Screen                 46 */ E0PREFIX|0x37, /* E0 B7 */
1553adfd74aSMaksim Yevmenkin /* Scroll Lock                  47 */ 0x46, /* C6 */
1563adfd74aSMaksim Yevmenkin #if 0
1573adfd74aSMaksim Yevmenkin /* Break (Ctrl-Pause)           48 */ E0 46 E0 C6, /* None */
1583adfd74aSMaksim Yevmenkin /* Pause                        48 */ E1 1D 45 E1 9D C5, /* None */
1593adfd74aSMaksim Yevmenkin #else
1603adfd74aSMaksim Yevmenkin /* Break (Ctrl-Pause)/Pause     48 */ NOBREAK /* Special case */, /* None */
1613adfd74aSMaksim Yevmenkin #endif
1623adfd74aSMaksim Yevmenkin /* Insert                       49 */ E0PREFIX|0x52, /* E0 D2 */
1633adfd74aSMaksim Yevmenkin /* Home                         4A */ E0PREFIX|0x47, /* E0 C7 */
1643adfd74aSMaksim Yevmenkin /* Page Up                      4B */ E0PREFIX|0x49, /* E0 C9 */
1653adfd74aSMaksim Yevmenkin /* Delete                       4C */ E0PREFIX|0x53, /* E0 D3 */
1663adfd74aSMaksim Yevmenkin /* End                          4D */ E0PREFIX|0x4F, /* E0 CF */
1673adfd74aSMaksim Yevmenkin /* Page Down                    4E */ E0PREFIX|0x51, /* E0 D1 */
1683adfd74aSMaksim Yevmenkin /* Right Arrow                  4F */ E0PREFIX|0x4D, /* E0 CD */
1693adfd74aSMaksim Yevmenkin /* Left Arrow                   50 */ E0PREFIX|0x4B, /* E0 CB */
1703adfd74aSMaksim Yevmenkin /* Down Arrow                   51 */ E0PREFIX|0x50, /* E0 D0 */
1713adfd74aSMaksim Yevmenkin /* Up Arrow                     52 */ E0PREFIX|0x48, /* E0 C8 */
1723adfd74aSMaksim Yevmenkin /* Num Lock                     53 */ 0x45, /* C5 */
1733adfd74aSMaksim Yevmenkin /* Keypad /                     54 */ E0PREFIX|0x35, /* E0 B5 */
1743adfd74aSMaksim Yevmenkin /* Keypad *                     55 */ 0x37, /* B7 */
1753adfd74aSMaksim Yevmenkin /* Keypad -                     56 */ 0x4A, /* CA */
1763adfd74aSMaksim Yevmenkin /* Keypad +                     57 */ 0x4E, /* CE */
1773adfd74aSMaksim Yevmenkin /* Keypad Enter                 58 */ E0PREFIX|0x1C, /* E0 9C */
1783adfd74aSMaksim Yevmenkin /* Keypad 1 End                 59 */ 0x4F, /* CF */
1793adfd74aSMaksim Yevmenkin /* Keypad 2 Down                5A */ 0x50, /* D0 */
1803adfd74aSMaksim Yevmenkin /* Keypad 3 PageDn              5B */ 0x51, /* D1 */
1813adfd74aSMaksim Yevmenkin /* Keypad 4 Left                5C */ 0x4B, /* CB */
1823adfd74aSMaksim Yevmenkin /* Keypad 5                     5D */ 0x4C, /* CC */
1833adfd74aSMaksim Yevmenkin /* Keypad 6 Right               5E */ 0x4D, /* CD */
1843adfd74aSMaksim Yevmenkin /* Keypad 7 Home                5F */ 0x47, /* C7 */
1853adfd74aSMaksim Yevmenkin /* Keypad 8 Up                  60 */ 0x48, /* C8 */
1863adfd74aSMaksim Yevmenkin /* Keypad 9 PageUp              61 */ 0x49, /* C9 */
1873adfd74aSMaksim Yevmenkin /* Keypad 0 Insert              62 */ 0x52, /* D2 */
1883adfd74aSMaksim Yevmenkin /* Keypad . Delete              63 */ 0x53, /* D3 */
1893adfd74aSMaksim Yevmenkin /* Europe 2                     64 */ 0x56, /* D6 */
1903adfd74aSMaksim Yevmenkin /* App                          65 */ E0PREFIX|0x5D, /* E0 DD */
1913adfd74aSMaksim Yevmenkin /* Keyboard Power               66 */ E0PREFIX|0x5E, /* E0 DE */
1923adfd74aSMaksim Yevmenkin /* Keypad =                     67 */ 0x59, /* D9 */
1933adfd74aSMaksim Yevmenkin /* F13                          68 */ 0x64, /* E4 */
1943adfd74aSMaksim Yevmenkin /* F14                          69 */ 0x65, /* E5 */
1953adfd74aSMaksim Yevmenkin /* F15                          6A */ 0x66, /* E6 */
1963adfd74aSMaksim Yevmenkin /* F16                          6B */ 0x67, /* E7 */
1973adfd74aSMaksim Yevmenkin /* F17                          6C */ 0x68, /* E8 */
1983adfd74aSMaksim Yevmenkin /* F18                          6D */ 0x69, /* E9 */
1993adfd74aSMaksim Yevmenkin /* F19                          6E */ 0x6A, /* EA */
2003adfd74aSMaksim Yevmenkin /* F20                          6F */ 0x6B, /* EB */
2013adfd74aSMaksim Yevmenkin /* F21                          70 */ 0x6C, /* EC */
2023adfd74aSMaksim Yevmenkin /* F22                          71 */ 0x6D, /* ED */
2033adfd74aSMaksim Yevmenkin /* F23                          72 */ 0x6E, /* EE */
2043adfd74aSMaksim Yevmenkin /* F24                          73 */ 0x76, /* F6 */
2053adfd74aSMaksim Yevmenkin /* Keyboard Execute             74 */ -1,   /* Unassigned */
2063adfd74aSMaksim Yevmenkin /* Keyboard Help                75 */ -1,   /* Unassigned */
2073adfd74aSMaksim Yevmenkin /* Keyboard Menu                76 */ -1,   /* Unassigned */
2083adfd74aSMaksim Yevmenkin /* Keyboard Select              77 */ -1,   /* Unassigned */
2093adfd74aSMaksim Yevmenkin /* Keyboard Stop                78 */ -1,   /* Unassigned */
2103adfd74aSMaksim Yevmenkin /* Keyboard Again               79 */ -1,   /* Unassigned */
2113adfd74aSMaksim Yevmenkin /* Keyboard Undo                7A */ -1,   /* Unassigned */
2123adfd74aSMaksim Yevmenkin /* Keyboard Cut                 7B */ -1,   /* Unassigned */
2133adfd74aSMaksim Yevmenkin /* Keyboard Copy                7C */ -1,   /* Unassigned */
2143adfd74aSMaksim Yevmenkin /* Keyboard Paste               7D */ -1,   /* Unassigned */
2153adfd74aSMaksim Yevmenkin /* Keyboard Find                7E */ -1,   /* Unassigned */
2163adfd74aSMaksim Yevmenkin /* Keyboard Mute                7F */ -1,   /* Unassigned */
2173adfd74aSMaksim Yevmenkin /* Keyboard Volume Up           80 */ -1,   /* Unassigned */
2183adfd74aSMaksim Yevmenkin /* Keyboard Volume Dn           81 */ -1,   /* Unassigned */
2193adfd74aSMaksim Yevmenkin /* Keyboard Locking Caps Lock   82 */ -1,   /* Unassigned */
2203adfd74aSMaksim Yevmenkin /* Keyboard Locking Num Lock    83 */ -1,   /* Unassigned */
2213adfd74aSMaksim Yevmenkin /* Keyboard Locking Scroll Lock 84 */ -1,   /* Unassigned */
2223adfd74aSMaksim Yevmenkin /* Keypad comma                 85 */ 0x7E, /* FE */
2233adfd74aSMaksim Yevmenkin /* Keyboard Equal Sign          86 */ -1,   /* Unassigned */
2243adfd74aSMaksim Yevmenkin /* Keyboard Int'l 1             87 */ 0x73, /* F3 */
2253adfd74aSMaksim Yevmenkin /* Keyboard Int'l 2             88 */ 0x70, /* F0 */
2263adfd74aSMaksim Yevmenkin /* Keyboard Int'l 2             89 */ 0x7D, /* FD */
2273adfd74aSMaksim Yevmenkin /* Keyboard Int'l 4             8A */ 0x79, /* F9 */
2283adfd74aSMaksim Yevmenkin /* Keyboard Int'l 5             8B */ 0x7B, /* FB */
2293adfd74aSMaksim Yevmenkin /* Keyboard Int'l 6             8C */ 0x5C, /* DC */
2303adfd74aSMaksim Yevmenkin /* Keyboard Int'l 7             8D */ -1,   /* Unassigned */
2313adfd74aSMaksim Yevmenkin /* Keyboard Int'l 8             8E */ -1,   /* Unassigned */
2323adfd74aSMaksim Yevmenkin /* Keyboard Int'l 9             8F */ -1,   /* Unassigned */
233d6c53633SHans Petter Selasky /* Keyboard Lang 1              90 */ 0x71, /* Kana */
234d6c53633SHans Petter Selasky /* Keyboard Lang 2              91 */ 0x72, /* Eisu */
2353adfd74aSMaksim Yevmenkin /* Keyboard Lang 3              92 */ 0x78, /* F8 */
2363adfd74aSMaksim Yevmenkin /* Keyboard Lang 4              93 */ 0x77, /* F7 */
2373adfd74aSMaksim Yevmenkin /* Keyboard Lang 5              94 */ 0x76, /* F6 */
2383adfd74aSMaksim Yevmenkin /* Keyboard Lang 6              95 */ -1,   /* Unassigned */
2393adfd74aSMaksim Yevmenkin /* Keyboard Lang 7              96 */ -1,   /* Unassigned */
2403adfd74aSMaksim Yevmenkin /* Keyboard Lang 8              97 */ -1,   /* Unassigned */
2413adfd74aSMaksim Yevmenkin /* Keyboard Lang 9              98 */ -1,   /* Unassigned */
2423adfd74aSMaksim Yevmenkin /* Keyboard Alternate Erase     99 */ -1,   /* Unassigned */
2433adfd74aSMaksim Yevmenkin /* Keyboard SysReq/Attention    9A */ -1,   /* Unassigned */
2443adfd74aSMaksim Yevmenkin /* Keyboard Cancel              9B */ -1,   /* Unassigned */
2453adfd74aSMaksim Yevmenkin /* Keyboard Clear               9C */ -1,   /* Unassigned */
2463adfd74aSMaksim Yevmenkin /* Keyboard Prior               9D */ -1,   /* Unassigned */
2473adfd74aSMaksim Yevmenkin /* Keyboard Return              9E */ -1,   /* Unassigned */
2483adfd74aSMaksim Yevmenkin /* Keyboard Separator           9F */ -1,   /* Unassigned */
2493adfd74aSMaksim Yevmenkin /* Keyboard Out                 A0 */ -1,   /* Unassigned */
2503adfd74aSMaksim Yevmenkin /* Keyboard Oper                A1 */ -1,   /* Unassigned */
2513adfd74aSMaksim Yevmenkin /* Keyboard Clear/Again         A2 */ -1,   /* Unassigned */
2523adfd74aSMaksim Yevmenkin /* Keyboard CrSel/Props         A3 */ -1,   /* Unassigned */
2533adfd74aSMaksim Yevmenkin /* Keyboard ExSel               A4 */ -1,   /* Unassigned */
2543adfd74aSMaksim Yevmenkin /* Reserved                     A5 */ -1,   /* Reserved */
2553adfd74aSMaksim Yevmenkin /* Reserved                     A6 */ -1,   /* Reserved */
2563adfd74aSMaksim Yevmenkin /* Reserved                     A7 */ -1,   /* Reserved */
2573adfd74aSMaksim Yevmenkin /* Reserved                     A8 */ -1,   /* Reserved */
2583adfd74aSMaksim Yevmenkin /* Reserved                     A9 */ -1,   /* Reserved */
2593adfd74aSMaksim Yevmenkin /* Reserved                     AA */ -1,   /* Reserved */
2603adfd74aSMaksim Yevmenkin /* Reserved                     AB */ -1,   /* Reserved */
2613adfd74aSMaksim Yevmenkin /* Reserved                     AC */ -1,   /* Reserved */
2623adfd74aSMaksim Yevmenkin /* Reserved                     AD */ -1,   /* Reserved */
2633adfd74aSMaksim Yevmenkin /* Reserved                     AE */ -1,   /* Reserved */
2643adfd74aSMaksim Yevmenkin /* Reserved                     AF */ -1,   /* Reserved */
2653adfd74aSMaksim Yevmenkin /* Reserved                     B0 */ -1,   /* Reserved */
2663adfd74aSMaksim Yevmenkin /* Reserved                     B1 */ -1,   /* Reserved */
2673adfd74aSMaksim Yevmenkin /* Reserved                     B2 */ -1,   /* Reserved */
2683adfd74aSMaksim Yevmenkin /* Reserved                     B3 */ -1,   /* Reserved */
2693adfd74aSMaksim Yevmenkin /* Reserved                     B4 */ -1,   /* Reserved */
2703adfd74aSMaksim Yevmenkin /* Reserved                     B5 */ -1,   /* Reserved */
2713adfd74aSMaksim Yevmenkin /* Reserved                     B6 */ -1,   /* Reserved */
2723adfd74aSMaksim Yevmenkin /* Reserved                     B7 */ -1,   /* Reserved */
2733adfd74aSMaksim Yevmenkin /* Reserved                     B8 */ -1,   /* Reserved */
2743adfd74aSMaksim Yevmenkin /* Reserved                     B9 */ -1,   /* Reserved */
2753adfd74aSMaksim Yevmenkin /* Reserved                     BA */ -1,   /* Reserved */
2763adfd74aSMaksim Yevmenkin /* Reserved                     BB */ -1,   /* Reserved */
2773adfd74aSMaksim Yevmenkin /* Reserved                     BC */ -1,   /* Reserved */
2783adfd74aSMaksim Yevmenkin /* Reserved                     BD */ -1,   /* Reserved */
2793adfd74aSMaksim Yevmenkin /* Reserved                     BE */ -1,   /* Reserved */
2803adfd74aSMaksim Yevmenkin /* Reserved                     BF */ -1,   /* Reserved */
2813adfd74aSMaksim Yevmenkin /* Reserved                     C0 */ -1,   /* Reserved */
2823adfd74aSMaksim Yevmenkin /* Reserved                     C1 */ -1,   /* Reserved */
2833adfd74aSMaksim Yevmenkin /* Reserved                     C2 */ -1,   /* Reserved */
2843adfd74aSMaksim Yevmenkin /* Reserved                     C3 */ -1,   /* Reserved */
2853adfd74aSMaksim Yevmenkin /* Reserved                     C4 */ -1,   /* Reserved */
2863adfd74aSMaksim Yevmenkin /* Reserved                     C5 */ -1,   /* Reserved */
2873adfd74aSMaksim Yevmenkin /* Reserved                     C6 */ -1,   /* Reserved */
288610e662eSMarkus Brueffer /* Reserved                     C7 */ -1,   /* Reserved */
2893adfd74aSMaksim Yevmenkin /* Reserved                     C8 */ -1,   /* Reserved */
2903adfd74aSMaksim Yevmenkin /* Reserved                     C9 */ -1,   /* Reserved */
2913adfd74aSMaksim Yevmenkin /* Reserved                     CA */ -1,   /* Reserved */
2923adfd74aSMaksim Yevmenkin /* Reserved                     CB */ -1,   /* Reserved */
2933adfd74aSMaksim Yevmenkin /* Reserved                     CC */ -1,   /* Reserved */
2943adfd74aSMaksim Yevmenkin /* Reserved                     CD */ -1,   /* Reserved */
2953adfd74aSMaksim Yevmenkin /* Reserved                     CE */ -1,   /* Reserved */
2963adfd74aSMaksim Yevmenkin /* Reserved                     CF */ -1,   /* Reserved */
2973adfd74aSMaksim Yevmenkin /* Reserved                     D0 */ -1,   /* Reserved */
2983adfd74aSMaksim Yevmenkin /* Reserved                     D1 */ -1,   /* Reserved */
2993adfd74aSMaksim Yevmenkin /* Reserved                     D2 */ -1,   /* Reserved */
3003adfd74aSMaksim Yevmenkin /* Reserved                     D3 */ -1,   /* Reserved */
3013adfd74aSMaksim Yevmenkin /* Reserved                     D4 */ -1,   /* Reserved */
3023adfd74aSMaksim Yevmenkin /* Reserved                     D5 */ -1,   /* Reserved */
3033adfd74aSMaksim Yevmenkin /* Reserved                     D6 */ -1,   /* Reserved */
304610e662eSMarkus Brueffer /* Reserved                     D7 */ -1,   /* Reserved */
3053adfd74aSMaksim Yevmenkin /* Reserved                     D8 */ -1,   /* Reserved */
3063adfd74aSMaksim Yevmenkin /* Reserved                     D9 */ -1,   /* Reserved */
3073adfd74aSMaksim Yevmenkin /* Reserved                     DA */ -1,   /* Reserved */
3083adfd74aSMaksim Yevmenkin /* Reserved                     DB */ -1,   /* Reserved */
3093adfd74aSMaksim Yevmenkin /* Reserved                     DC */ -1,   /* Reserved */
3103adfd74aSMaksim Yevmenkin /* Reserved                     DD */ -1,   /* Reserved */
3113adfd74aSMaksim Yevmenkin /* Reserved                     DE */ -1,   /* Reserved */
3123adfd74aSMaksim Yevmenkin /* Reserved                     DF */ -1,   /* Reserved */
3133adfd74aSMaksim Yevmenkin /* Left Control                 E0 */ 0x1D, /* 9D */
3143adfd74aSMaksim Yevmenkin /* Left Shift                   E1 */ 0x2A, /* AA */
3153adfd74aSMaksim Yevmenkin /* Left Alt                     E2 */ 0x38, /* B8 */
3163adfd74aSMaksim Yevmenkin /* Left GUI                     E3 */ E0PREFIX|0x5B, /* E0 DB */
3173adfd74aSMaksim Yevmenkin /* Right Control                E4 */ E0PREFIX|0x1D, /* E0 9D */
3183adfd74aSMaksim Yevmenkin /* Right Shift                  E5 */ 0x36, /* B6 */
3193adfd74aSMaksim Yevmenkin /* Right Alt                    E6 */ E0PREFIX|0x38, /* E0 B8 */
3203adfd74aSMaksim Yevmenkin /* Right GUI                    E7 */ E0PREFIX|0x5C  /* E0 DC */
3213adfd74aSMaksim Yevmenkin };
3223adfd74aSMaksim Yevmenkin 
323fdbf7cabSElyes Haouas #define xsize	(int32_t)nitems(x)
3243adfd74aSMaksim Yevmenkin 
3253adfd74aSMaksim Yevmenkin /*
3263adfd74aSMaksim Yevmenkin  * Get a max HID keycode (aligned)
3273adfd74aSMaksim Yevmenkin  */
3283adfd74aSMaksim Yevmenkin 
3297aebfa93SMaksim Yevmenkin int32_t
3303adfd74aSMaksim Yevmenkin kbd_maxkey(void)
3313adfd74aSMaksim Yevmenkin {
3323adfd74aSMaksim Yevmenkin 	return (xsize);
3333adfd74aSMaksim Yevmenkin }
3343adfd74aSMaksim Yevmenkin 
3353adfd74aSMaksim Yevmenkin /*
3363adfd74aSMaksim Yevmenkin  * Process keys
3373adfd74aSMaksim Yevmenkin  */
3383adfd74aSMaksim Yevmenkin 
3397aebfa93SMaksim Yevmenkin int32_t
3403adfd74aSMaksim Yevmenkin kbd_process_keys(bthid_session_p s)
3413adfd74aSMaksim Yevmenkin {
342*6bd85498SRyan Libby 	bitstr_t	bit_decl(diff, xsize);
3437aebfa93SMaksim Yevmenkin 	int32_t		f1, f2, i;
3443adfd74aSMaksim Yevmenkin 
3453adfd74aSMaksim Yevmenkin 	assert(s != NULL);
3463adfd74aSMaksim Yevmenkin 	assert(s->srv != NULL);
3473adfd74aSMaksim Yevmenkin 
3487aebfa93SMaksim Yevmenkin 	/* Check if the new keys have been pressed */
3497aebfa93SMaksim Yevmenkin 	bit_ffs(s->keys1, xsize, &f1);
3503adfd74aSMaksim Yevmenkin 
3517aebfa93SMaksim Yevmenkin 	/* Check if old keys still pressed */
3527aebfa93SMaksim Yevmenkin 	bit_ffs(s->keys2, xsize, &f2);
3533adfd74aSMaksim Yevmenkin 
3543adfd74aSMaksim Yevmenkin 	if (f1 == -1) {
3557aebfa93SMaksim Yevmenkin 		/* no new key pressed */
3567aebfa93SMaksim Yevmenkin 		if (f2 != -1) {
3577aebfa93SMaksim Yevmenkin 			/* release old keys */
3587aebfa93SMaksim Yevmenkin 			kbd_write(s->keys2, f2, 0, s->vkbd);
35944af5666SVladimir Kondratyev 			uinput_kbd_write(s->keys2, f2, 0, s->ukbd);
3607aebfa93SMaksim Yevmenkin 			memset(s->keys2, 0, bitstr_size(xsize));
3613adfd74aSMaksim Yevmenkin 		}
3623adfd74aSMaksim Yevmenkin 
3633adfd74aSMaksim Yevmenkin 		return (0);
3643adfd74aSMaksim Yevmenkin 	}
3653adfd74aSMaksim Yevmenkin 
3667aebfa93SMaksim Yevmenkin 	if (f2 == -1) {
3677aebfa93SMaksim Yevmenkin 		/* no old keys, but new keys pressed */
3687aebfa93SMaksim Yevmenkin 		assert(f1 != -1);
3697aebfa93SMaksim Yevmenkin 
3707aebfa93SMaksim Yevmenkin 		memcpy(s->keys2, s->keys1, bitstr_size(xsize));
3717aebfa93SMaksim Yevmenkin 		kbd_write(s->keys1, f1, 1, s->vkbd);
37244af5666SVladimir Kondratyev 		uinput_kbd_write(s->keys1, f1, 1, s->ukbd);
3737aebfa93SMaksim Yevmenkin 		memset(s->keys1, 0, bitstr_size(xsize));
3747aebfa93SMaksim Yevmenkin 
3757aebfa93SMaksim Yevmenkin 		return (0);
3767aebfa93SMaksim Yevmenkin 	}
3777aebfa93SMaksim Yevmenkin 
3787aebfa93SMaksim Yevmenkin 	/* new keys got pressed, old keys got released */
3797aebfa93SMaksim Yevmenkin 	memset(diff, 0, bitstr_size(xsize));
3807aebfa93SMaksim Yevmenkin 
3817aebfa93SMaksim Yevmenkin 	for (i = f2; i < xsize; i ++) {
3827aebfa93SMaksim Yevmenkin 		if (bit_test(s->keys2, i)) {
3837aebfa93SMaksim Yevmenkin 			if (!bit_test(s->keys1, i)) {
3847aebfa93SMaksim Yevmenkin 				bit_clear(s->keys2, i);
3857aebfa93SMaksim Yevmenkin 				bit_set(diff, i);
3867aebfa93SMaksim Yevmenkin 			}
3877aebfa93SMaksim Yevmenkin 		}
3887aebfa93SMaksim Yevmenkin 	}
3893adfd74aSMaksim Yevmenkin 
3903adfd74aSMaksim Yevmenkin 	for (i = f1; i < xsize; i++) {
3917aebfa93SMaksim Yevmenkin 		if (bit_test(s->keys1, i)) {
3927aebfa93SMaksim Yevmenkin 			if (!bit_test(s->keys2, i))
3937aebfa93SMaksim Yevmenkin 				bit_set(s->keys2, i);
3943adfd74aSMaksim Yevmenkin 			else
3957aebfa93SMaksim Yevmenkin 				bit_clear(s->keys1, i);
3963adfd74aSMaksim Yevmenkin 		}
3973adfd74aSMaksim Yevmenkin 	}
3983adfd74aSMaksim Yevmenkin 
3997aebfa93SMaksim Yevmenkin 	bit_ffs(diff, xsize, &f2);
40044af5666SVladimir Kondratyev 	if (f2 > 0) {
4017aebfa93SMaksim Yevmenkin 		kbd_write(diff, f2, 0, s->vkbd);
40244af5666SVladimir Kondratyev 		uinput_kbd_write(diff, f2, 0, s->ukbd);
40344af5666SVladimir Kondratyev 	}
4043adfd74aSMaksim Yevmenkin 
4057aebfa93SMaksim Yevmenkin 	bit_ffs(s->keys1, xsize, &f1);
4063adfd74aSMaksim Yevmenkin 	if (f1 > 0) {
4077aebfa93SMaksim Yevmenkin 		kbd_write(s->keys1, f1, 1, s->vkbd);
40844af5666SVladimir Kondratyev 		uinput_kbd_write(s->keys1, f1, 1, s->ukbd);
4097aebfa93SMaksim Yevmenkin 		memset(s->keys1, 0, bitstr_size(xsize));
4103adfd74aSMaksim Yevmenkin 	}
4113adfd74aSMaksim Yevmenkin 
4123adfd74aSMaksim Yevmenkin 	return (0);
4133adfd74aSMaksim Yevmenkin }
4143adfd74aSMaksim Yevmenkin 
4153adfd74aSMaksim Yevmenkin /*
4163adfd74aSMaksim Yevmenkin  * Translate given keymap and write keyscodes
4173adfd74aSMaksim Yevmenkin  */
41844af5666SVladimir Kondratyev void
41944af5666SVladimir Kondratyev uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)
42044af5666SVladimir Kondratyev {
42144af5666SVladimir Kondratyev 	int32_t i;
42244af5666SVladimir Kondratyev 
42344af5666SVladimir Kondratyev 	if (fd >= 0) {
42444af5666SVladimir Kondratyev 		for (i = fb; i < xsize; i++) {
42544af5666SVladimir Kondratyev 			if (bit_test(m, i))
42644af5666SVladimir Kondratyev 				uinput_rep_key(fd, i, make);
42744af5666SVladimir Kondratyev 		}
42844af5666SVladimir Kondratyev 	}
42944af5666SVladimir Kondratyev }
43044af5666SVladimir Kondratyev 
43144af5666SVladimir Kondratyev /*
43244af5666SVladimir Kondratyev  * Translate given keymap and write keyscodes
43344af5666SVladimir Kondratyev  */
4343adfd74aSMaksim Yevmenkin 
4353adfd74aSMaksim Yevmenkin static void
4367aebfa93SMaksim Yevmenkin kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)
4373adfd74aSMaksim Yevmenkin {
4387aebfa93SMaksim Yevmenkin 	int32_t	i, *b, *eob, n, buf[64];
4393adfd74aSMaksim Yevmenkin 
4403adfd74aSMaksim Yevmenkin 	b = buf;
441fdbf7cabSElyes Haouas 	eob = b + nitems(buf);
4423adfd74aSMaksim Yevmenkin 	i = fb;
4433adfd74aSMaksim Yevmenkin 
4443adfd74aSMaksim Yevmenkin 	while (i < xsize) {
4453adfd74aSMaksim Yevmenkin 		if (bit_test(m, i)) {
4463adfd74aSMaksim Yevmenkin 			n = kbd_xlate(i, make, b, eob);
4473adfd74aSMaksim Yevmenkin 			if (n == -1) {
4483adfd74aSMaksim Yevmenkin 				write(fd, buf, (b - buf) * sizeof(buf[0]));
4493adfd74aSMaksim Yevmenkin 				b = buf;
4503adfd74aSMaksim Yevmenkin 				continue;
4513adfd74aSMaksim Yevmenkin 			}
4523adfd74aSMaksim Yevmenkin 
4533adfd74aSMaksim Yevmenkin 			b += n;
4543adfd74aSMaksim Yevmenkin 		}
4553adfd74aSMaksim Yevmenkin 
4563adfd74aSMaksim Yevmenkin 		i ++;
4573adfd74aSMaksim Yevmenkin 	}
4583adfd74aSMaksim Yevmenkin 
4593adfd74aSMaksim Yevmenkin 	if (b != buf)
4603adfd74aSMaksim Yevmenkin 		write(fd, buf, (b - buf) * sizeof(buf[0]));
4613adfd74aSMaksim Yevmenkin }
4623adfd74aSMaksim Yevmenkin 
4633adfd74aSMaksim Yevmenkin /*
4643adfd74aSMaksim Yevmenkin  * Translate HID code into PS/2 code and put codes into buffer b.
4653adfd74aSMaksim Yevmenkin  * Returns the number of codes put in b. Return -1 if buffer has not
4663adfd74aSMaksim Yevmenkin  * enough space.
4673adfd74aSMaksim Yevmenkin  */
4683adfd74aSMaksim Yevmenkin 
4693adfd74aSMaksim Yevmenkin #undef  PUT
4703adfd74aSMaksim Yevmenkin #define PUT(c, n, b, eob)	\
4713adfd74aSMaksim Yevmenkin do {				\
4723adfd74aSMaksim Yevmenkin 	if ((b) >= (eob))	\
4733adfd74aSMaksim Yevmenkin 		return (-1);	\
4743adfd74aSMaksim Yevmenkin 	*(b) = (c);		\
4753adfd74aSMaksim Yevmenkin 	(b) ++;			\
4763adfd74aSMaksim Yevmenkin 	(n) ++;			\
4773adfd74aSMaksim Yevmenkin } while (0)
4783adfd74aSMaksim Yevmenkin 
4797aebfa93SMaksim Yevmenkin static int32_t
4807aebfa93SMaksim Yevmenkin kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob)
4813adfd74aSMaksim Yevmenkin {
4827aebfa93SMaksim Yevmenkin 	int32_t	c, n;
4833adfd74aSMaksim Yevmenkin 
4843adfd74aSMaksim Yevmenkin 	n = 0;
4853adfd74aSMaksim Yevmenkin 
4863adfd74aSMaksim Yevmenkin 	if (code >= xsize)
4873adfd74aSMaksim Yevmenkin 		return (0); /* HID code is not in the table */
4883adfd74aSMaksim Yevmenkin 
4893adfd74aSMaksim Yevmenkin 	/* Handle special case - Pause/Break */
4903adfd74aSMaksim Yevmenkin 	if (code == 0x48) {
4913adfd74aSMaksim Yevmenkin 		if (!make)
4923adfd74aSMaksim Yevmenkin 			return (0); /* No break code */
4933adfd74aSMaksim Yevmenkin 
4943adfd74aSMaksim Yevmenkin #if 0
4953adfd74aSMaksim Yevmenkin XXX FIXME
4963adfd74aSMaksim Yevmenkin 		if (ctrl_is_pressed) {
4973adfd74aSMaksim Yevmenkin 			/* Break (Ctrl-Pause) */
4983adfd74aSMaksim Yevmenkin 			PUT(0xe0, n, b, eob);
4993adfd74aSMaksim Yevmenkin 			PUT(0x46, n, b, eob);
5003adfd74aSMaksim Yevmenkin 			PUT(0xe0, n, b, eob);
5013adfd74aSMaksim Yevmenkin 			PUT(0xc6, n, b, eob);
5023adfd74aSMaksim Yevmenkin 		} else {
5033adfd74aSMaksim Yevmenkin 			/* Pause */
5043adfd74aSMaksim Yevmenkin 			PUT(0xe1, n, b, eob);
5053adfd74aSMaksim Yevmenkin 			PUT(0x1d, n, b, eob);
5063adfd74aSMaksim Yevmenkin 			PUT(0x45, n, b, eob);
5073adfd74aSMaksim Yevmenkin 			PUT(0xe1, n, b, eob);
5083adfd74aSMaksim Yevmenkin 			PUT(0x9d, n, b, eob);
5093adfd74aSMaksim Yevmenkin 			PUT(0xc5, n, b, eob);
5103adfd74aSMaksim Yevmenkin 		}
5113adfd74aSMaksim Yevmenkin #endif
5123adfd74aSMaksim Yevmenkin 
5133adfd74aSMaksim Yevmenkin 		return (n);
5143adfd74aSMaksim Yevmenkin 	}
5153adfd74aSMaksim Yevmenkin 
5163adfd74aSMaksim Yevmenkin 	if ((c = x[code]) == -1)
5173adfd74aSMaksim Yevmenkin 		return (0); /* HID code translation is not defined */
5183adfd74aSMaksim Yevmenkin 
5193adfd74aSMaksim Yevmenkin 	if (make) {
5203adfd74aSMaksim Yevmenkin 		if (c & E0PREFIX)
5213adfd74aSMaksim Yevmenkin 			PUT(0xe0, n, b, eob);
5223adfd74aSMaksim Yevmenkin 
5233adfd74aSMaksim Yevmenkin 		PUT((c & CODEMASK), n, b, eob);
5243adfd74aSMaksim Yevmenkin 	} else if (!(c & NOBREAK)) {
5253adfd74aSMaksim Yevmenkin 		if (c & E0PREFIX)
5263adfd74aSMaksim Yevmenkin 			PUT(0xe0, n, b, eob);
5273adfd74aSMaksim Yevmenkin 
5283adfd74aSMaksim Yevmenkin 		PUT((0x80|(c & CODEMASK)), n, b, eob);
5293adfd74aSMaksim Yevmenkin 	}
5303adfd74aSMaksim Yevmenkin 
5313adfd74aSMaksim Yevmenkin 	return (n);
5323adfd74aSMaksim Yevmenkin }
5333adfd74aSMaksim Yevmenkin 
5347aebfa93SMaksim Yevmenkin /*
5357aebfa93SMaksim Yevmenkin  * Process status change from vkbd(4)
5367aebfa93SMaksim Yevmenkin  */
5377aebfa93SMaksim Yevmenkin 
5387aebfa93SMaksim Yevmenkin int32_t
5397aebfa93SMaksim Yevmenkin kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)
5407aebfa93SMaksim Yevmenkin {
5412d7f9912SMaksim Yevmenkin 	vkbd_status_t	st;
54252a4455bSMaksim Yevmenkin 	uint8_t		found, report_id;
5437aebfa93SMaksim Yevmenkin 	hid_device_p	hid_device;
5447aebfa93SMaksim Yevmenkin 	hid_data_t	d;
5457aebfa93SMaksim Yevmenkin 	hid_item_t	h;
54644af5666SVladimir Kondratyev 	uint8_t		leds_mask = 0;
5477aebfa93SMaksim Yevmenkin 
5487aebfa93SMaksim Yevmenkin 	assert(s != NULL);
5497aebfa93SMaksim Yevmenkin 	assert(len == sizeof(vkbd_status_t));
5507aebfa93SMaksim Yevmenkin 
5512d7f9912SMaksim Yevmenkin 	memcpy(&st, data, sizeof(st));
55252a4455bSMaksim Yevmenkin 	found = 0;
5537aebfa93SMaksim Yevmenkin 	report_id = NO_REPORT_ID;
5547aebfa93SMaksim Yevmenkin 
5557aebfa93SMaksim Yevmenkin 	hid_device = get_hid_device(&s->bdaddr);
5567aebfa93SMaksim Yevmenkin 	assert(hid_device != NULL);
5577aebfa93SMaksim Yevmenkin 
55852a4455bSMaksim Yevmenkin 	data[0] = 0xa2; /* DATA output (HID output report) */
55952a4455bSMaksim Yevmenkin 	data[1] = 0x00;
56052a4455bSMaksim Yevmenkin 	data[2] = 0x00;
56152a4455bSMaksim Yevmenkin 
5627aebfa93SMaksim Yevmenkin 	for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1);
5637aebfa93SMaksim Yevmenkin 	     hid_get_item(d, &h) > 0; ) {
5647aebfa93SMaksim Yevmenkin 		if (HID_PAGE(h.usage) == HUP_LEDS) {
56552a4455bSMaksim Yevmenkin 			found++;
56652a4455bSMaksim Yevmenkin 
5677aebfa93SMaksim Yevmenkin 			if (report_id == NO_REPORT_ID)
5687aebfa93SMaksim Yevmenkin 				report_id = h.report_ID;
5697aebfa93SMaksim Yevmenkin 			else if (h.report_ID != report_id)
5707aebfa93SMaksim Yevmenkin 				syslog(LOG_WARNING, "Output HID report IDs " \
5717aebfa93SMaksim Yevmenkin 					"for %s do not match: %d vs. %d. " \
5727aebfa93SMaksim Yevmenkin 					"Please report",
5737aebfa93SMaksim Yevmenkin 					bt_ntoa(&s->bdaddr, NULL),
5747aebfa93SMaksim Yevmenkin 					h.report_ID, report_id);
5757aebfa93SMaksim Yevmenkin 
5767aebfa93SMaksim Yevmenkin 			switch(HID_USAGE(h.usage)) {
5777aebfa93SMaksim Yevmenkin 			case 0x01: /* Num Lock LED */
5782d7f9912SMaksim Yevmenkin 				if (st.leds & LED_NUM)
57952a4455bSMaksim Yevmenkin 					hid_set_data(&data[1], &h, 1);
58044af5666SVladimir Kondratyev 				leds_mask |= LED_NUM;
5817aebfa93SMaksim Yevmenkin 				break;
5827aebfa93SMaksim Yevmenkin 
5837aebfa93SMaksim Yevmenkin 			case 0x02: /* Caps Lock LED */
5842d7f9912SMaksim Yevmenkin 				if (st.leds & LED_CAP)
58552a4455bSMaksim Yevmenkin 					hid_set_data(&data[1], &h, 1);
58644af5666SVladimir Kondratyev 				leds_mask |= LED_CAP;
5877aebfa93SMaksim Yevmenkin 				break;
5887aebfa93SMaksim Yevmenkin 
5897aebfa93SMaksim Yevmenkin 			case 0x03: /* Scroll Lock LED */
5902d7f9912SMaksim Yevmenkin 				if (st.leds & LED_SCR)
59152a4455bSMaksim Yevmenkin 					hid_set_data(&data[1], &h, 1);
59244af5666SVladimir Kondratyev 				leds_mask |= LED_SCR;
5937aebfa93SMaksim Yevmenkin 				break;
5947aebfa93SMaksim Yevmenkin 
5957aebfa93SMaksim Yevmenkin 			/* XXX add other LEDs ? */
5967aebfa93SMaksim Yevmenkin 			}
5977aebfa93SMaksim Yevmenkin 		}
5987aebfa93SMaksim Yevmenkin 	}
5997aebfa93SMaksim Yevmenkin 	hid_end_parse(d);
6007aebfa93SMaksim Yevmenkin 
60112df5213SVladimir Kondratyev 	if (report_id != NO_REPORT_ID) {
60212df5213SVladimir Kondratyev 		data[2] = data[1];
60312df5213SVladimir Kondratyev 		data[1] = report_id;
60412df5213SVladimir Kondratyev 	}
60512df5213SVladimir Kondratyev 
60652a4455bSMaksim Yevmenkin 	if (found)
60752a4455bSMaksim Yevmenkin 		write(s->intr, data, (report_id != NO_REPORT_ID) ? 3 : 2);
6087aebfa93SMaksim Yevmenkin 
60944af5666SVladimir Kondratyev 	if (found && s->srv->uinput && hid_device->keyboard)
61044af5666SVladimir Kondratyev 		uinput_rep_leds(s->ukbd, st.leds, leds_mask);
61144af5666SVladimir Kondratyev 
6127aebfa93SMaksim Yevmenkin 	return (0);
6137aebfa93SMaksim Yevmenkin }
6147aebfa93SMaksim Yevmenkin 
615