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