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