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/param.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)nitems(x)
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 + nitems(buf);
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