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