xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/KeyboardProtocolHandler.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
1 /*
2  * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch>
3  * Distributed under the terms of the MIT license.
4  */
5 
6 
7 #include <new>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <usb/USB_hid.h>
12 #include <util/AutoLock.h>
13 
14 #include <debug.h>
15 #include <kernel.h>
16 
17 #include "Driver.h"
18 #include "KeyboardProtocolHandler.h"
19 
20 #include "HIDCollection.h"
21 #include "HIDDevice.h"
22 #include "HIDReport.h"
23 #include "HIDReportItem.h"
24 
25 #include <keyboard_mouse_driver.h>
26 
27 
28 #define LEFT_ALT_KEY	0x04
29 #define RIGHT_ALT_KEY	0x40
30 #define ALT_KEYS		(LEFT_ALT_KEY | RIGHT_ALT_KEY)
31 
32 #define KEYBOARD_HANDLER_COOKIE_FLAG_READER		0x01
33 #define KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER	0x02
34 
35 
36 #ifdef KEYBOARD_SUPPORTS_KDL
37 static bool sDebugKeyboardFound = false;
38 static size_t sDebugKeyboardReportSize = 0;
39 static int32 sDebuggerCommandAdded = 0;
40 
41 #ifdef USB_KDL
42 static usb_id sDebugKeyboardPipe = 0;
43 
44 static int
45 debug_get_keyboard_config(int argc, char **argv)
46 {
47 	set_debug_variable("_usbPipeID", (uint64)sDebugKeyboardPipe);
48 	set_debug_variable("_usbReportSize", (uint64)sDebugKeyboardReportSize);
49 	return 0;
50 }
51 #endif
52 #endif
53 
54 
55 //	#pragma mark -
56 
57 
58 KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport,
59 	HIDReport *outputReport)
60 	:
61 	ProtocolHandler(inputReport.Device(), "input/keyboard/" DEVICE_PATH_SUFFIX
62 		"/", 512),
63 	fInputReport(inputReport),
64 	fOutputReport(outputReport),
65 	fRepeatDelay(300000),
66 	fRepeatRate(35000),
67 	fCurrentRepeatDelay(B_INFINITE_TIMEOUT),
68 	fCurrentRepeatKey(0),
69 	fKeyCount(0),
70 	fModifierCount(0),
71 	fLastModifiers(0),
72 	fCurrentKeys(NULL),
73 	fLastKeys(NULL),
74 	fHasReader(0),
75 	fHasDebugReader(false)
76 {
77 	mutex_init(&fLock, DEVICE_PATH_SUFFIX " keyboard");
78 
79 	// find modifiers and keys
80 	bool debugUsable = false;
81 
82 	for (uint32 i = 0; i < inputReport.CountItems(); i++) {
83 		HIDReportItem *item = inputReport.ItemAt(i);
84 		if (!item->HasData())
85 			continue;
86 
87 		if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD
88 			|| item->UsagePage() == B_HID_USAGE_PAGE_CONSUMER
89 			|| item->UsagePage() == B_HID_USAGE_PAGE_BUTTON) {
90 			TRACE("keyboard item with usage %" B_PRIx32 "\n",
91 				item->Usage());
92 
93 			debugUsable = true;
94 
95 			if (item->UsageID() >= B_HID_UID_KB_LEFT_CONTROL
96 				&& item->UsageID() <= B_HID_UID_KB_RIGHT_GUI) {
97 				if (fModifierCount < MAX_MODIFIERS)
98 					fModifiers[fModifierCount++] = item;
99 			} else if (fKeyCount < MAX_KEYS)
100 						fKeys[fKeyCount++] = item;
101 		}
102 	}
103 
104 #ifdef KEYBOARD_SUPPORTS_KDL
105 	if (!sDebugKeyboardFound && debugUsable) {
106 		// It's a keyboard, not just some additional buttons, set up the kernel
107 		// debugger info here so that it is ready on panics or crashes that
108 		// don't go through the emergency keys. If we also found LEDs we assume
109 		// it is a full sized keyboard and discourage further setting the info.
110 #ifdef USB_KDL
111 		sDebugKeyboardPipe = fInputReport.Device()->InterruptPipe();
112 #endif
113 		sDebugKeyboardReportSize = fInputReport.Parser()->MaxReportSize();
114 		if (outputReport != NULL)
115 			sDebugKeyboardFound = true;
116 	}
117 #endif
118 
119 	TRACE("keyboard device with %" B_PRIu32 " keys and %" B_PRIu32
120 		" modifiers\n", fKeyCount, fModifierCount);
121 	TRACE("input report: %u; output report: %u\n", inputReport.ID(),
122 		outputReport != NULL ? outputReport->ID() : 255);
123 
124 	fLastKeys = (uint16 *)malloc(fKeyCount * 2 * sizeof(uint16));
125 	fCurrentKeys = &fLastKeys[fKeyCount];
126 	if (fLastKeys == NULL) {
127 		fStatus = B_NO_MEMORY;
128 		return;
129 	}
130 
131 	memset(fLastKeys, 0, fKeyCount * 2 * sizeof(uint16));
132 
133 	// find leds if we have an output report
134 	for (uint32 i = 0; i < MAX_LEDS; i++)
135 		fLEDs[i] = NULL;
136 
137 	if (outputReport != NULL) {
138 		for (uint32 i = 0; i < outputReport->CountItems(); i++) {
139 			HIDReportItem *item = outputReport->ItemAt(i);
140 			if (!item->HasData())
141 				continue;
142 
143 			// the led item array is identity mapped with what we get from
144 			// the input_server for the set-leds command
145 			if (item->UsagePage() == B_HID_USAGE_PAGE_LED) {
146 				switch (item->UsageID()) {
147 					case B_HID_UID_LED_NUM_LOCK:
148 						fLEDs[0] = item;
149 						break;
150 					case B_HID_UID_LED_CAPS_LOCK:
151 						fLEDs[1] = item;
152 						break;
153 					case B_HID_UID_LED_SCROLL_LOCK:
154 						fLEDs[2] = item;
155 						break;
156 				}
157 			}
158 		}
159 	}
160 
161 #ifdef KEYBOARD_SUPPORTS_KDL
162 	if (atomic_add(&sDebuggerCommandAdded, 1) == 0) {
163 #ifdef USB_KDL
164 		add_debugger_command("get_usb_keyboard_config",
165 			&debug_get_keyboard_config,
166 			"Gets the required config of the USB keyboard");
167 #endif
168 	}
169 #endif
170 }
171 
172 
173 KeyboardProtocolHandler::~KeyboardProtocolHandler()
174 {
175 	free(fLastKeys);
176 
177 #ifdef KEYBOARD_SUPPORTS_KDL
178 	if (atomic_add(&sDebuggerCommandAdded, -1) == 1) {
179 #ifdef USB_KDL
180 		remove_debugger_command("get_usb_keyboard_config",
181 			&debug_get_keyboard_config);
182 #endif
183 	}
184 #endif
185 
186 	mutex_destroy(&fLock);
187 }
188 
189 
190 void
191 KeyboardProtocolHandler::AddHandlers(HIDDevice &device,
192 	HIDCollection &collection, ProtocolHandler *&handlerList)
193 {
194 	bool handled = false;
195 	switch (collection.UsagePage()) {
196 		case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
197 		{
198 			switch (collection.UsageID()) {
199 				case B_HID_UID_GD_KEYBOARD:
200 				case B_HID_UID_GD_KEYPAD:
201 #if 0
202 				// This is not specific enough to deserve a keyboard device on
203 				// its own (some mice have one such descriptor, for example).
204 				// If your keyboard uses this, do a more extensive check of
205 				// the descriptor to make sure there actually are keys in it.
206 				case B_HID_UID_GD_SYSTEM_CONTROL:
207 #endif
208 					handled = true;
209 			}
210 
211 			break;
212 		}
213 
214 		case B_HID_USAGE_PAGE_CONSUMER:
215 		{
216 			switch (collection.UsageID()) {
217 				case B_HID_UID_CON_CONSUMER_CONTROL:
218 					handled = true;
219 			}
220 
221 			break;
222 		}
223 	}
224 
225 	if (!handled) {
226 		TRACE("collection not a supported keyboard subset\n");
227 		return;
228 	}
229 
230 	HIDParser &parser = device.Parser();
231 	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
232 	if (maxReportCount == 0)
233 		return;
234 
235 	uint32 inputReportCount = 0;
236 	HIDReport *inputReports[maxReportCount];
237 	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
238 		inputReportCount);
239 
240 	TRACE("input report count: %" B_PRIu32 "\n", inputReportCount);
241 
242 	for (uint32 i = 0; i < inputReportCount; i++) {
243 		HIDReport *inputReport = inputReports[i];
244 
245 //		bool mayHaveOutput = false;
246 		bool foundKeyboardUsage = false;
247 		for (uint32 j = 0; j < inputReport->CountItems(); j++) {
248 			HIDReportItem *item = inputReport->ItemAt(j);
249 			if (!item->HasData())
250 				continue;
251 
252 			if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD
253 				|| (item->UsagePage() == B_HID_USAGE_PAGE_CONSUMER
254 					&& item->Array())
255 				|| (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON
256 					&& item->Array())) {
257 				// found at least one item with a keyboard usage or with
258 				// a consumer/button usage that is handled like a key
259 //				mayHaveOutput = item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD;
260 				foundKeyboardUsage = true;
261 				break;
262 			}
263 		}
264 
265 		if (!foundKeyboardUsage)
266 			continue;
267 
268 		bool foundOutputReport = false;
269 		HIDReport *outputReport = NULL;
270 		do {
271 			// try to find the led output report
272 			maxReportCount =  parser.CountReports(HID_REPORT_TYPE_OUTPUT);
273 			if (maxReportCount == 0)
274 				break;
275 
276 			uint32 outputReportCount = 0;
277 			HIDReport *outputReports[maxReportCount];
278 			collection.BuildReportList(HID_REPORT_TYPE_OUTPUT,
279 				outputReports, outputReportCount);
280 
281 			for (uint32  j = 0; j < outputReportCount; j++) {
282 				outputReport = outputReports[j];
283 
284 				for (uint32 k = 0; k < outputReport->CountItems(); k++) {
285 					HIDReportItem *item = outputReport->ItemAt(k);
286 					if (item->UsagePage() == B_HID_USAGE_PAGE_LED) {
287 						foundOutputReport = true;
288 						break;
289 					}
290 				}
291 
292 				if (foundOutputReport)
293 					break;
294 			}
295 		} while (false);
296 
297 		ProtocolHandler *newHandler = new(std::nothrow) KeyboardProtocolHandler(
298 			*inputReport, foundOutputReport ? outputReport : NULL);
299 		if (newHandler == NULL) {
300 			TRACE("failed to allocated keyboard protocol handler\n");
301 			continue;
302 		}
303 
304 		newHandler->SetNextHandler(handlerList);
305 		handlerList = newHandler;
306 	}
307 }
308 
309 
310 status_t
311 KeyboardProtocolHandler::Open(uint32 flags, uint32 *cookie)
312 {
313 	status_t status = ProtocolHandler::Open(flags, cookie);
314 	if (status != B_OK) {
315 		TRACE_ALWAYS("keyboard device failed to open: %s\n",
316 			strerror(status));
317 		return status;
318 	}
319 
320 	if (Device()->OpenCount() == 1) {
321 		fCurrentRepeatDelay = B_INFINITE_TIMEOUT;
322 		fCurrentRepeatKey = 0;
323 	}
324 
325 	return B_OK;
326 }
327 
328 
329 status_t
330 KeyboardProtocolHandler::Close(uint32 *cookie)
331 {
332 	if ((*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER) != 0)
333 		fHasDebugReader = false;
334 	if ((*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_READER) != 0)
335 		atomic_and(&fHasReader, 0);
336 
337 	return ProtocolHandler::Close(cookie);
338 }
339 
340 
341 status_t
342 KeyboardProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
343 	size_t length)
344 {
345 	switch (op) {
346 		case B_GET_DEVICE_NAME:
347 		{
348 			const char name[] = DEVICE_NAME" Keyboard";
349 			return IOGetDeviceName(name,buffer,length);
350 		}
351 
352 		case KB_READ:
353 		{
354 			if (*cookie == 0) {
355 				if (atomic_or(&fHasReader, 1) != 0)
356 					return B_BUSY;
357 
358 				// We're the first, so we become the only reader
359 				*cookie = KEYBOARD_HANDLER_COOKIE_FLAG_READER;
360 			}
361 
362 			while (true) {
363 				MutexLocker locker(fLock);
364 
365 				bigtime_t enterTime = system_time();
366 				while (RingBufferReadable() == 0) {
367 					status_t result = _ReadReport(fCurrentRepeatDelay, cookie);
368 					if (result != B_OK && result != B_TIMED_OUT)
369 						return result;
370 
371 					if (!Device()->IsOpen())
372 						return B_ERROR;
373 
374 					if (RingBufferReadable() == 0 && fCurrentRepeatKey != 0
375 						&& system_time() - enterTime > fCurrentRepeatDelay) {
376 						// this case is for handling key repeats, it means no
377 						// interrupt transfer has happened or it didn't produce
378 						// any new key events, but a repeated key down is due
379 						_WriteKey(fCurrentRepeatKey, true);
380 
381 						// the next timeout is reduced to the repeat_rate
382 						fCurrentRepeatDelay = fRepeatRate;
383 						break;
384 					}
385 				}
386 
387 				if (fHasDebugReader
388 					&& (*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER)
389 						== 0) {
390 					// Handover buffer to the debugger instead
391 					locker.Unlock();
392 					snooze(25000);
393 					continue;
394 				}
395 
396 				if (!IS_USER_ADDRESS(buffer))
397 					return B_BAD_ADDRESS;
398 
399 				// process what is in the ring_buffer, it could be written
400 				// there because we handled an interrupt transfer or because
401 				// we wrote the current repeat key
402 				return RingBufferRead(buffer, sizeof(raw_key_info));
403 			}
404 		}
405 
406 		case KB_SET_LEDS:
407 		{
408 			uint8 ledData[4];
409 			if (!IS_USER_ADDRESS(buffer)
410 				|| user_memcpy(ledData, buffer, sizeof(ledData)) != B_OK) {
411 				return B_BAD_ADDRESS;
412 			}
413 			return _SetLEDs(ledData);
414 		}
415 
416 		case KB_SET_KEY_REPEAT_RATE:
417 		{
418 			int32 repeatRate;
419 			if (!IS_USER_ADDRESS(buffer)
420 				|| user_memcpy(&repeatRate, buffer, sizeof(repeatRate))
421 					!= B_OK) {
422 				return B_BAD_ADDRESS;
423 			}
424 
425 			if (repeatRate == 0 || repeatRate > 1000000)
426 				return B_BAD_VALUE;
427 
428 			fRepeatRate = 10000000 / repeatRate;
429 			return B_OK;
430 		}
431 
432 		case KB_GET_KEY_REPEAT_RATE:
433 		{
434 			int32 repeatRate = 10000000 / fRepeatRate;
435 			if (!IS_USER_ADDRESS(buffer)
436 				|| user_memcpy(buffer, &repeatRate, sizeof(repeatRate))
437 					!= B_OK) {
438 				return B_BAD_ADDRESS;
439 			}
440 			return B_OK;
441 		}
442 
443 		case KB_SET_KEY_REPEAT_DELAY:
444 			if (!IS_USER_ADDRESS(buffer)
445 				|| user_memcpy(&fRepeatDelay, buffer, sizeof(fRepeatDelay))
446 					!= B_OK) {
447 				return B_BAD_ADDRESS;
448 			}
449 			return B_OK;
450 
451 		case KB_GET_KEY_REPEAT_DELAY:
452 			if (!IS_USER_ADDRESS(buffer)
453 				|| user_memcpy(buffer, &fRepeatDelay, sizeof(fRepeatDelay))
454 					!= B_OK) {
455 				return B_BAD_ADDRESS;
456 			}
457 			return B_OK;
458 
459 		case KB_SET_DEBUG_READER:
460 #ifdef KEYBOARD_SUPPORTS_KDL
461 			if (fHasDebugReader)
462 				return B_BUSY;
463 
464 			*cookie |= KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER;
465 			fHasDebugReader = true;
466 			return B_OK;
467 #else
468 			return B_NOT_SUPPORTED;
469 #endif
470 	}
471 
472 	TRACE_ALWAYS("keyboard device unhandled control 0x%08" B_PRIx32 "\n", op);
473 	return B_ERROR;
474 }
475 
476 
477 void
478 KeyboardProtocolHandler::_WriteKey(uint32 key, bool down)
479 {
480 	raw_key_info info;
481 	info.keycode = key;
482 	info.is_keydown = down;
483 	info.timestamp = system_time();
484 	RingBufferWrite(&info, sizeof(raw_key_info));
485 }
486 
487 
488 status_t
489 KeyboardProtocolHandler::_SetLEDs(uint8 *data)
490 {
491 	if (fOutputReport == NULL || fOutputReport->Device()->IsRemoved())
492 		return B_ERROR;
493 
494 	for (uint32 i = 0; i < MAX_LEDS; i++) {
495 		if (fLEDs[i] == NULL)
496 			continue;
497 
498 		fLEDs[i]->SetData(data[i]);
499 	}
500 
501 	return fOutputReport->SendReport();
502 }
503 
504 
505 status_t
506 KeyboardProtocolHandler::_ReadReport(bigtime_t timeout, uint32 *cookie)
507 {
508 	status_t result = fInputReport.WaitForReport(timeout);
509 	if (result != B_OK) {
510 		if (fInputReport.Device()->IsRemoved()) {
511 			TRACE("device has been removed\n");
512 			return B_ERROR;
513 		}
514 
515 		if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0)
516 			return B_CANCELED;
517 
518 		if (result != B_TIMED_OUT && result != B_INTERRUPTED) {
519 			// we expect timeouts as we do repeat key handling this way,
520 			// interrupts happen when other reports come in on the same
521 			// endpoint
522 			TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
523 		}
524 
525 		// signal that we simply want to try again
526 		return B_OK;
527 	}
528 
529 	TRACE("got keyboard input report\n");
530 
531 	uint8 modifiers = 0;
532 	for (uint32 i = 0; i < fModifierCount; i++) {
533 		HIDReportItem *modifier = fModifiers[i];
534 		if (modifier == NULL)
535 			break;
536 
537 		if (modifier->Extract() == B_OK && modifier->Valid()) {
538 			modifiers |= (modifier->Data() & 1)
539 				<< (modifier->UsageID() - B_HID_UID_KB_LEFT_CONTROL);
540 		}
541 	}
542 
543 	for (uint32 i = 0; i < fKeyCount; i++) {
544 		HIDReportItem *key = fKeys[i];
545 		if (key == NULL)
546 			break;
547 
548 		if (key->Extract() == B_OK && key->Valid()) {
549 			// handle both array and bitmap based keyboard reports
550 			if (key->Array()) {
551 				fCurrentKeys[i] = key->Data();
552 			} else {
553 				if (key->Data() == 1)
554 					fCurrentKeys[i] = key->UsageID();
555 				else
556 					fCurrentKeys[i] = 0;
557 			}
558 		}
559 		else
560 			fCurrentKeys[i] = 0;
561 	}
562 
563 	fInputReport.DoneProcessing();
564 
565 	static const uint32 kModifierTable[] = {
566 		KEY_ControlL,
567 		KEY_ShiftL,
568 		KEY_AltL,
569 		KEY_WinL,
570 		KEY_ControlR,
571 		KEY_ShiftR,
572 		KEY_AltR,
573 		KEY_WinR
574 	};
575 
576 	// find modifier changes and push them into the buffer
577 	uint8 modifierChange = fLastModifiers ^ modifiers;
578 	for (uint8 i = 0; modifierChange; i++, modifierChange >>= 1) {
579 		if (modifierChange & 1)
580 			_WriteKey(kModifierTable[i], (modifiers >> i) & 1);
581 	}
582 
583 	fLastModifiers = modifiers;
584 
585 	static const uint32 kKeyTable[] = {
586 		0x00,	// ERROR
587 		0x00,	// ERROR
588 		0x00,	// ERROR
589 		0x00,	// ERROR
590 		0x3c,	// A
591 		0x50,	// B
592 		0x4e,	// C
593 		0x3e,	// D
594 		0x29,	// E
595 		0x3f,	// F
596 		0x40,	// G
597 		0x41,	// H
598 		0x2e,	// I
599 		0x42,	// J
600 		0x43,	// K
601 		0x44,	// L
602 		0x52,	// M
603 		0x51,	// N
604 		0x2f,	// O
605 		0x30,	// P
606 		0x27,	// Q
607 		0x2a,	// R
608 		0x3d,	// S
609 		0x2b,	// T
610 		0x2d,	// U
611 		0x4f,	// V
612 		0x28,	// W
613 		0x4d,	// X
614 		0x2c,	// Y
615 		0x4c,	// Z
616 		0x12,	// 1
617 		0x13,	// 2
618 		0x14,	// 3
619 		0x15,	// 4
620 		0x16,	// 5
621 		0x17,	// 6
622 		0x18,	// 7
623 		0x19,	// 8
624 		0x1a,	// 9
625 		0x1b,	// 0
626 		0x47,	// enter
627 		0x01,	// Esc
628 		0x1e,	// Backspace
629 		0x26,	// Tab
630 		0x5e,	// Space
631 		0x1c,	// -
632 		0x1d,	// =
633 		0x31,	// [
634 		0x32,	// ]
635 		0x33,	// backslash
636 		0x33,	// backslash
637 		0x45,	// ;
638 		0x46,	// '
639 		0x11,	// `
640 		0x53,	// ,
641 		0x54,	// .
642 		0x55,	// /
643 		KEY_CapsLock,	// Caps
644 		0x02,	// F1
645 		0x03,	// F2
646 		0x04,	// F3
647 		0x05,	// F4
648 		0x06,	// F5
649 		0x07,	// F6
650 		0x08,	// F7
651 		0x09,	// F8
652 		0x0a,	// F9
653 		0x0b,	// F10
654 		0x0c,	// F11
655 		0x0d,	// F12
656 		0x0e,	// PrintScreen
657 		KEY_Scroll,	// Scroll Lock
658 		KEY_Pause,	// Pause (0x7f with Ctrl)
659 		0x1f,	// Insert
660 		0x20,	// Home
661 		0x21,	// Page up
662 		0x34,	// Delete
663 		0x35,	// End
664 		0x36,	// Page down
665 		0x63,	// Right arrow
666 		0x61,	// Left arrow
667 		0x62,	// Down arrow
668 		0x57,	// Up arrow
669 		0x22,	// Num Lock
670 		0x23,	// Pad /
671 		0x24,	// Pad *
672 		0x25,	// Pad -
673 		0x3a,	// Pad +
674 		0x5b,	// Pad Enter
675 		0x58,	// Pad 1
676 		0x59,	// Pad 2
677 		0x5a,	// Pad 3
678 		0x48,	// Pad 4
679 		0x49,	// Pad 5
680 		0x4a,	// Pad 6
681 		0x37,	// Pad 7
682 		0x38,	// Pad 8
683 		0x39,	// Pad 9
684 		0x64,	// Pad 0
685 		0x65,	// Pad .
686 		0x69,	// <
687 		KEY_Menu,	// Menu
688 		KEY_Power,	// Power
689 		KEY_NumEqual,	// Pad =
690 		0x00,	// F13 unmapped
691 		0x00,	// F14 unmapped
692 		0x00,	// F15 unmapped
693 		0x00,	// F16 unmapped
694 		0x00,	// F17 unmapped
695 		0x00,	// F18 unmapped
696 		0x00,	// F19 unmapped
697 		0x00,	// F20 unmapped
698 		0x00,	// F21 unmapped
699 		0x00,	// F22 unmapped
700 		0x00,	// F23 unmapped
701 		0x00,	// F24 unmapped
702 		0x00,	// Execute unmapped
703 		0x00,	// Help unmapped
704 		0x00,	// Menu unmapped
705 		0x00,	// Select unmapped
706 		0x00,	// Stop unmapped
707 		0x00,	// Again unmapped
708 		0x00,	// Undo unmapped
709 		0x00,	// Cut unmapped
710 		0x00,	// Copy unmapped
711 		0x00,	// Paste unmapped
712 		0x00,	// Find unmapped
713 		0x00,	// Mute unmapped
714 		0x00,	// Volume up unmapped
715 		0x00,	// Volume down unmapped
716 		0x00,	// CapsLock unmapped
717 		0x00,	// NumLock unmapped
718 		0x00,	// Scroll lock unmapped
719 		0x70,	// Keypad . on Brazilian ABNT2
720 		0x00,	// = sign
721 		0x6b,	// Ro (\\ key, japanese)
722 		0x6e,	// Katakana/Hiragana, second key right to spacebar, japanese
723 		0x6a,	// Yen (macron key, japanese)
724 		0x6d,	// Henkan, first key right to spacebar, japanese
725 		0x6c,	// Muhenkan, key left to spacebar, japanese
726 		0x00,	// Keyboard International6 unmapped
727 		0x00,	// Keyboard International7 unmapped
728 		0x00,	// Keyboard International8 unmapped
729 		0x00,	// Keyboard International9 unmapped
730 		0xf0,	// Hangul, korean, Kana, Mac japanese USB
731 		0xf1,	// Hangul_Hanja, korean, Eisu, Mac japanese USB
732 	};
733 
734 	static const size_t kKeyTableSize
735 		= sizeof(kKeyTable) / sizeof(kKeyTable[0]);
736 
737 	bool phantomState = true;
738 	for (size_t i = 0; i < fKeyCount; i++) {
739 		if (fCurrentKeys[i] != 1
740 			|| fKeys[i]->UsagePage() != B_HID_USAGE_PAGE_KEYBOARD) {
741 			phantomState = false;
742 			break;
743 		}
744 	}
745 
746 	if (phantomState) {
747 		// no valid key information is present in this state and we don't
748 		// want to overwrite our last buffer as otherwise we generate
749 		// spurious key ups now and spurious key downs when leaving the
750 		// phantom state again
751 		return B_OK;
752 	}
753 
754 	static bool sysReqPressed = false;
755 
756 	bool keyDown = false;
757 	uint16 *current = fLastKeys;
758 	uint16 *compare = fCurrentKeys;
759 	for (int32 twice = 0; twice < 2; twice++) {
760 		for (size_t i = 0; i < fKeyCount; i++) {
761 			if (current[i] == 0 || (current[i] == 1
762 				&& fKeys[i]->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD))
763 				continue;
764 
765 			bool found = false;
766 			for (size_t j = 0; j < fKeyCount; j++) {
767 				if (compare[j] == current[i]) {
768 					found = true;
769 					break;
770 				}
771 			}
772 
773 			if (found)
774 				continue;
775 
776 			// a change occured
777 			uint32 key = 0;
778 			if (fKeys[i]->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD) {
779 				if (current[i] < kKeyTableSize)
780 					key = kKeyTable[current[i]];
781 
782 				if (key == KEY_Pause && (modifiers & ALT_KEYS) != 0)
783 					key = KEY_Break;
784 				else if (key == 0xe && (modifiers & ALT_KEYS) != 0) {
785 					key = KEY_SysRq;
786 					sysReqPressed = keyDown;
787 				} else if (sysReqPressed && keyDown
788 					&& current[i] >= 4 && current[i] <= 29
789 					&& (fLastModifiers & ALT_KEYS) != 0) {
790 					// Alt-SysReq+letter was pressed
791 #ifdef KEYBOARD_SUPPORTS_KDL
792 #ifdef USB_KDL
793 					sDebugKeyboardPipe
794 						= fInputReport.Device()->InterruptPipe();
795 #endif
796 					sDebugKeyboardReportSize
797 						= fInputReport.Parser()->MaxReportSize();
798 #endif
799 
800 					char letter = current[i] - 4 + 'a';
801 
802 					if (debug_emergency_key_pressed(letter)) {
803 						// we probably have lost some keys, so reset our key
804 						// state
805 						sysReqPressed = false;
806 						continue;
807 					}
808 				}
809 			}
810 
811 			if (key == 0) {
812 				// unmapped normal key or consumer/button key
813 				key = fInputReport.Usages()[0] + current[i];
814 			}
815 
816 			_WriteKey(key, keyDown);
817 
818 			if (keyDown) {
819 				// repeat handling
820 				fCurrentRepeatKey = key;
821 				fCurrentRepeatDelay = fRepeatDelay;
822 			} else {
823 				// cancel the repeats if they are for this key
824 				if (fCurrentRepeatKey == key) {
825 					fCurrentRepeatDelay = B_INFINITE_TIMEOUT;
826 					fCurrentRepeatKey = 0;
827 				}
828 			}
829 		}
830 
831 		current = fCurrentKeys;
832 		compare = fLastKeys;
833 		keyDown = true;
834 	}
835 
836 	memcpy(fLastKeys, fCurrentKeys, fKeyCount * sizeof(uint16));
837 	return B_OK;
838 }
839