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