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