⛏️ index : haiku.git

/*
 * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch>
 * Distributed under the terms of the MIT license.
 */


#include <new>
#include <stdlib.h>
#include <string.h>

#include <interface/InterfaceDefs.h>
#include <usb/USB_hid.h>
#include <util/AutoLock.h>

#include <debug.h>
#include <kernel.h>

#include "Driver.h"
#include "KeyboardProtocolHandler.h"

#include "HIDCollection.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "HIDReportItem.h"

#include <keyboard_mouse_driver.h>


#define LEFT_ALT_KEY	0x04
#define RIGHT_ALT_KEY	0x40
#define ALT_KEYS		(LEFT_ALT_KEY | RIGHT_ALT_KEY)

#define KEYBOARD_HANDLER_COOKIE_FLAG_READER		0x01
#define KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER	0x02


#ifdef KEYBOARD_SUPPORTS_KDL
static bool sDebugKeyboardFound = false;
static size_t sDebugKeyboardReportSize = 0;
static int32 sDebuggerCommandAdded = 0;

#ifdef USB_KDL
static usb_id sDebugKeyboardPipe = 0;

static int
debug_get_keyboard_config(int argc, char **argv)
{
	set_debug_variable("_usbPipeID", (uint64)sDebugKeyboardPipe);
	set_debug_variable("_usbReportSize", (uint64)sDebugKeyboardReportSize);
	return 0;
}
#endif
#endif


//	#pragma mark -


KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport,
	HIDReport *outputReport)
	:
	ProtocolHandler(inputReport.Device(), "input/keyboard/" DEVICE_PATH_SUFFIX
		"/", 512),
	fInputReport(inputReport),
	fOutputReport(outputReport),
	fRepeatDelay(300000),
	fRepeatRate(35000),
	fCurrentRepeatDelay(B_INFINITE_TIMEOUT),
	fCurrentRepeatKey(0),
	fKeyCount(0),
	fModifierCount(0),
	fLastModifiers(0),
	fCurrentKeys(NULL),
	fLastKeys(NULL),
	fHasReader(0),
	fHasDebugReader(false)
{
	mutex_init(&fLock, DEVICE_PATH_SUFFIX " keyboard");

	// find modifiers and keys
	bool debugUsable = false;

	for (uint32 i = 0; i < inputReport.CountItems(); i++) {
		HIDReportItem *item = inputReport.ItemAt(i);
		if (!item->HasData())
			continue;

		if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD
			|| item->UsagePage() == B_HID_USAGE_PAGE_CONSUMER
			|| item->UsagePage() == B_HID_USAGE_PAGE_BUTTON) {
			TRACE("keyboard item with usage %" B_PRIx32 "\n",
				item->Usage());

			debugUsable = true;

			if (item->UsageID() >= B_HID_UID_KB_LEFT_CONTROL
				&& item->UsageID() <= B_HID_UID_KB_RIGHT_GUI) {
				if (fModifierCount < MAX_MODIFIERS)
					fModifiers[fModifierCount++] = item;
			} else if (fKeyCount < MAX_KEYS)
						fKeys[fKeyCount++] = item;
		}
	}

#ifdef KEYBOARD_SUPPORTS_KDL
	if (!sDebugKeyboardFound && debugUsable) {
		// It's a keyboard, not just some additional buttons, set up the kernel
		// debugger info here so that it is ready on panics or crashes that
		// don't go through the emergency keys. If we also found LEDs we assume
		// it is a full sized keyboard and discourage further setting the info.
#ifdef USB_KDL
		sDebugKeyboardPipe = fInputReport.Device()->InterruptPipe();
#endif
		sDebugKeyboardReportSize =
			fInputReport.Parser()->MaxReportSize(HID_REPORT_TYPE_INPUT);
		if (outputReport != NULL)
			sDebugKeyboardFound = true;
	}
#endif

	TRACE("keyboard device with %" B_PRIu32 " keys and %" B_PRIu32
		" modifiers\n", fKeyCount, fModifierCount);
	TRACE("input report: %u; output report: %u\n", inputReport.ID(),
		outputReport != NULL ? outputReport->ID() : 255);

	fLastKeys = (uint16 *)malloc(fKeyCount * 2 * sizeof(uint16));
	fCurrentKeys = &fLastKeys[fKeyCount];
	if (fLastKeys == NULL) {
		fStatus = B_NO_MEMORY;
		return;
	}

	memset(fLastKeys, 0, fKeyCount * 2 * sizeof(uint16));

	// find leds if we have an output report
	for (uint32 i = 0; i < MAX_LEDS; i++)
		fLEDs[i] = NULL;

	if (outputReport != NULL) {
		for (uint32 i = 0; i < outputReport->CountItems(); i++) {
			HIDReportItem *item = outputReport->ItemAt(i);
			if (!item->HasData())
				continue;

			// the led item array is identity mapped with what we get from
			// the input_server for the set-leds command
			if (item->UsagePage() == B_HID_USAGE_PAGE_LED) {
				switch (item->UsageID()) {
					case B_HID_UID_LED_NUM_LOCK:
						fLEDs[0] = item;
						break;
					case B_HID_UID_LED_CAPS_LOCK:
						fLEDs[1] = item;
						break;
					case B_HID_UID_LED_SCROLL_LOCK:
						fLEDs[2] = item;
						break;
				}
			}
		}
	}

#ifdef KEYBOARD_SUPPORTS_KDL
	if (atomic_add(&sDebuggerCommandAdded, 1) == 0) {
#ifdef USB_KDL
		add_debugger_command("get_usb_keyboard_config",
			&debug_get_keyboard_config,
			"Gets the required config of the USB keyboard");
#endif
	}
#endif
}


KeyboardProtocolHandler::~KeyboardProtocolHandler()
{
	free(fLastKeys);

#ifdef KEYBOARD_SUPPORTS_KDL
	if (atomic_add(&sDebuggerCommandAdded, -1) == 1) {
#ifdef USB_KDL
		remove_debugger_command("get_usb_keyboard_config",
			&debug_get_keyboard_config);
#endif
	}
#endif

	mutex_destroy(&fLock);
}


void
KeyboardProtocolHandler::AddHandlers(HIDDevice &device,
	HIDCollection &collection, ProtocolHandler *&handlerList)
{
	bool handled = false;
	switch (collection.UsagePage()) {
		case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
		{
			switch (collection.UsageID()) {
				case B_HID_UID_GD_KEYBOARD:
				case B_HID_UID_GD_KEYPAD:
#if 0
				// This is not specific enough to deserve a keyboard device on
				// its own (some mice have one such descriptor, for example).
				// If your keyboard uses this, do a more extensive check of
				// the descriptor to make sure there actually are keys in it.
				case B_HID_UID_GD_SYSTEM_CONTROL:
#endif
					handled = true;
			}

			break;
		}

		case B_HID_USAGE_PAGE_CONSUMER:
		{
			switch (collection.UsageID()) {
				case B_HID_UID_CON_CONSUMER_CONTROL:
					handled = true;
			}

			break;
		}
	}

	if (!handled) {
		TRACE("collection not a supported keyboard subset\n");
		return;
	}

	HIDParser &parser = device.Parser();
	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
	if (maxReportCount == 0)
		return;

	uint32 inputReportCount = 0;
	HIDReport *inputReports[maxReportCount];
	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
		inputReportCount);

	TRACE("input report count: %" B_PRIu32 "\n", inputReportCount);

	for (uint32 i = 0; i < inputReportCount; i++) {
		HIDReport *inputReport = inputReports[i];

//		bool mayHaveOutput = false;
		bool foundKeyboardUsage = false;
		for (uint32 j = 0; j < inputReport->CountItems(); j++) {
			HIDReportItem *item = inputReport->ItemAt(j);
			if (!item->HasData())
				continue;

			if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD
				|| (item->UsagePage() == B_HID_USAGE_PAGE_CONSUMER
					&& item->Array())
				|| (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON
					&& item->Array())) {
				// found at least one item with a keyboard usage or with
				// a consumer/button usage that is handled like a key
//				mayHaveOutput = item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD;
				foundKeyboardUsage = true;
				break;
			}
		}

		if (!foundKeyboardUsage)
			continue;

		bool foundOutputReport = false;
		HIDReport *outputReport = NULL;
		do {
			// try to find the led output report
			maxReportCount =  parser.CountReports(HID_REPORT_TYPE_OUTPUT);
			if (maxReportCount == 0)
				break;

			uint32 outputReportCount = 0;
			HIDReport *outputReports[maxReportCount];
			collection.BuildReportList(HID_REPORT_TYPE_OUTPUT,
				outputReports, outputReportCount);

			for (uint32  j = 0; j < outputReportCount; j++) {
				outputReport = outputReports[j];

				for (uint32 k = 0; k < outputReport->CountItems(); k++) {
					HIDReportItem *item = outputReport->ItemAt(k);
					if (item->UsagePage() == B_HID_USAGE_PAGE_LED) {
						foundOutputReport = true;
						break;
					}
				}

				if (foundOutputReport)
					break;
			}
		} while (false);

		ProtocolHandler *newHandler = new(std::nothrow) KeyboardProtocolHandler(
			*inputReport, foundOutputReport ? outputReport : NULL);
		if (newHandler == NULL) {
			TRACE("failed to allocated keyboard protocol handler\n");
			continue;
		}

		newHandler->SetNextHandler(handlerList);
		handlerList = newHandler;
	}
}


status_t
KeyboardProtocolHandler::Open(uint32 flags, uint32 *cookie)
{
	status_t status = ProtocolHandler::Open(flags, cookie);
	if (status != B_OK) {
		TRACE_ALWAYS("keyboard device failed to open: %s\n",
			strerror(status));
		return status;
	}

	if (Device()->OpenCount() == 1) {
		fCurrentRepeatDelay = B_INFINITE_TIMEOUT;
		fCurrentRepeatKey = 0;
	}

	return B_OK;
}


status_t
KeyboardProtocolHandler::Close(uint32 *cookie)
{
	if ((*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER) != 0)
		fHasDebugReader = false;
	if ((*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_READER) != 0)
		atomic_and(&fHasReader, 0);

	return ProtocolHandler::Close(cookie);
}


status_t
KeyboardProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
	size_t length)
{
	switch (op) {
		case B_GET_DEVICE_NAME:
		{
			const char name[] = DEVICE_NAME" Keyboard";
			return IOGetDeviceName(name,buffer,length);
		}

		case KB_READ:
		{
			if (*cookie == 0) {
				if (atomic_or(&fHasReader, 1) != 0)
					return B_BUSY;

				// We're the first, so we become the only reader
				*cookie = KEYBOARD_HANDLER_COOKIE_FLAG_READER;
			}

			while (true) {
				MutexLocker locker(fLock);

				bigtime_t enterTime = system_time();
				while (RingBufferReadable() == 0) {
					status_t result = _ReadReport(fCurrentRepeatDelay, cookie);
					if (result != B_OK && result != B_TIMED_OUT)
						return result;

					if (!Device()->IsOpen())
						return B_ERROR;

					if (RingBufferReadable() == 0 && fCurrentRepeatKey != 0
						&& system_time() - enterTime > fCurrentRepeatDelay) {
						// this case is for handling key repeats, it means no
						// interrupt transfer has happened or it didn't produce
						// any new key events, but a repeated key down is due
						_WriteKey(fCurrentRepeatKey, true);

						// the next timeout is reduced to the repeat_rate
						fCurrentRepeatDelay = fRepeatRate;
						break;
					}
				}

				if (fHasDebugReader
					&& (*cookie & KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER)
						== 0) {
					// Handover buffer to the debugger instead
					locker.Unlock();
					snooze(25000);
					continue;
				}

				if (!IS_USER_ADDRESS(buffer))
					return B_BAD_ADDRESS;

				// process what is in the ring_buffer, it could be written
				// there because we handled an interrupt transfer or because
				// we wrote the current repeat key
				return RingBufferRead(buffer, sizeof(raw_key_info));
			}
		}

		case KB_SET_LEDS:
		{
			uint8 ledData[4];
			if (!IS_USER_ADDRESS(buffer)
				|| user_memcpy(ledData, buffer, sizeof(ledData)) != B_OK) {
				return B_BAD_ADDRESS;
			}
			return _SetLEDs(ledData);
		}

		case KB_SET_KEY_REPEAT_RATE:
		{
			int32 repeatRate;
			if (!IS_USER_ADDRESS(buffer)
				|| user_memcpy(&repeatRate, buffer, sizeof(repeatRate))
					!= B_OK) {
				return B_BAD_ADDRESS;
			}

			if (repeatRate == 0 || repeatRate > 1000000)
				return B_BAD_VALUE;

			fRepeatRate = 10000000 / repeatRate;
			return B_OK;
		}

		case KB_GET_KEY_REPEAT_RATE:
		{
			int32 repeatRate = 10000000 / fRepeatRate;
			if (!IS_USER_ADDRESS(buffer)
				|| user_memcpy(buffer, &repeatRate, sizeof(repeatRate))
					!= B_OK) {
				return B_BAD_ADDRESS;
			}
			return B_OK;
		}

		case KB_SET_KEY_REPEAT_DELAY:
			if (!IS_USER_ADDRESS(buffer)
				|| user_memcpy(&fRepeatDelay, buffer, sizeof(fRepeatDelay))
					!= B_OK) {
				return B_BAD_ADDRESS;
			}
			return B_OK;

		case KB_GET_KEY_REPEAT_DELAY:
			if (!IS_USER_ADDRESS(buffer)
				|| user_memcpy(buffer, &fRepeatDelay, sizeof(fRepeatDelay))
					!= B_OK) {
				return B_BAD_ADDRESS;
			}
			return B_OK;

		case KB_SET_DEBUG_READER:
#ifdef KEYBOARD_SUPPORTS_KDL
			if (fHasDebugReader)
				return B_BUSY;

			*cookie |= KEYBOARD_HANDLER_COOKIE_FLAG_DEBUGGER;
			fHasDebugReader = true;
			return B_OK;
#else
			return B_NOT_SUPPORTED;
#endif
	}

	TRACE_ALWAYS("keyboard device unhandled control 0x%08" B_PRIx32 "\n", op);
	return B_ERROR;
}


void
KeyboardProtocolHandler::_WriteKey(uint32 key, bool down)
{
	raw_key_info info;
	info.keycode = key;
	info.is_keydown = down;
	info.timestamp = system_time();
	RingBufferWrite(&info, sizeof(raw_key_info));
}


status_t
KeyboardProtocolHandler::_SetLEDs(uint8 *data)
{
	if (fOutputReport == NULL || fOutputReport->Device()->IsRemoved())
		return B_ERROR;

	for (uint32 i = 0; i < MAX_LEDS; i++) {
		if (fLEDs[i] == NULL)
			continue;

		fLEDs[i]->SetData(data[i]);
	}

	return fOutputReport->SendReport();
}


status_t
KeyboardProtocolHandler::_ReadReport(bigtime_t timeout, uint32 *cookie)
{
	status_t result = fInputReport.WaitForReport(timeout);
	if (result != B_OK) {
		if (fInputReport.Device()->IsRemoved()) {
			TRACE("device has been removed\n");
			return B_ERROR;
		}

		if (result == B_INTERRUPTED)
			return result;
		if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0)
			return B_FILE_ERROR;

		if (result != B_TIMED_OUT && result != B_BUSY) {
			// we expect timeouts as we do repeat key handling this way,
			// "busy" happens when other reports come in on the same endpoint
			TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
		}

		// signal that we simply want to try again
		return B_OK;
	}

	TRACE("got keyboard input report\n");

	uint8 modifiers = 0;
	for (uint32 i = 0; i < fModifierCount; i++) {
		HIDReportItem *modifier = fModifiers[i];
		if (modifier == NULL)
			break;

		if (modifier->Extract() == B_OK && modifier->Valid()) {
			modifiers |= (modifier->Data() & 1)
				<< (modifier->UsageID() - B_HID_UID_KB_LEFT_CONTROL);
		}
	}

	for (uint32 i = 0; i < fKeyCount; i++) {
		HIDReportItem *key = fKeys[i];
		if (key == NULL)
			break;

		if (key->Extract() == B_OK && key->Valid()) {
			// handle both array and bitmap based keyboard reports
			if (key->Array()) {
				fCurrentKeys[i] = key->Data() - key->Minimum();
			} else {
				if (key->Data() == 1)
					fCurrentKeys[i] = key->UsageID();
				else
					fCurrentKeys[i] = 0;
			}
		}
		else
			fCurrentKeys[i] = 0;
	}

	fInputReport.DoneProcessing();

	static const uint32 kModifierTable[] = {
		KEY_ControlL,
		KEY_ShiftL,
		KEY_AltL,
		KEY_WinL,
		KEY_ControlR,
		KEY_ShiftR,
		KEY_AltR,
		KEY_WinR
	};

	// find modifier changes and push them into the buffer
	uint8 modifierChange = fLastModifiers ^ modifiers;
	for (uint8 i = 0; modifierChange; i++, modifierChange >>= 1) {
		if (modifierChange & 1)
			_WriteKey(kModifierTable[i], (modifiers >> i) & 1);
	}

	fLastModifiers = modifiers;

	static const uint32 kKeyTable[] = {
		0x00,	// ERROR
		0x00,	// ERROR
		0x00,	// ERROR
		0x00,	// ERROR
		0x3c,	// A
		0x50,	// B
		0x4e,	// C
		0x3e,	// D
		0x29,	// E
		0x3f,	// F
		0x40,	// G
		0x41,	// H
		0x2e,	// I
		0x42,	// J
		0x43,	// K
		0x44,	// L
		0x52,	// M
		0x51,	// N
		0x2f,	// O
		0x30,	// P
		0x27,	// Q
		0x2a,	// R
		0x3d,	// S
		0x2b,	// T
		0x2d,	// U
		0x4f,	// V
		0x28,	// W
		0x4d,	// X
		0x2c,	// Y
		0x4c,	// Z
		0x12,	// 1
		0x13,	// 2
		0x14,	// 3
		0x15,	// 4
		0x16,	// 5
		0x17,	// 6
		0x18,	// 7
		0x19,	// 8
		0x1a,	// 9
		0x1b,	// 0
		0x47,	// enter
		0x01,	// Esc
		0x1e,	// Backspace
		0x26,	// Tab
		0x5e,	// Space
		0x1c,	// -
		0x1d,	// =
		0x31,	// [
		0x32,	// ]
		0x33,	// backslash
		0x33,	// backslash
		0x45,	// ;
		0x46,	// '
		0x11,	// `
		0x53,	// ,
		0x54,	// .
		0x55,	// /
		B_CAPS_LOCK_KEY,	// Caps
		0x02,	// F1
		0x03,	// F2
		0x04,	// F3
		0x05,	// F4
		0x06,	// F5
		0x07,	// F6
		0x08,	// F7
		0x09,	// F8
		0x0a,	// F9
		0x0b,	// F10
		0x0c,	// F11
		0x0d,	// F12
		0x0e,	// PrintScreen
		B_SCROLL_KEY,	// Scroll Lock
		B_PAUSE_KEY,	// Pause (0x7f with Ctrl)
		0x1f,	// Insert
		0x20,	// Home
		0x21,	// Page up
		0x34,	// Delete
		0x35,	// End
		0x36,	// Page down
		0x63,	// Right arrow
		0x61,	// Left arrow
		0x62,	// Down arrow
		0x57,	// Up arrow
		0x22,	// Num Lock
		0x23,	// Pad /
		0x24,	// Pad *
		0x25,	// Pad -
		0x3a,	// Pad +
		0x5b,	// Pad Enter
		0x58,	// Pad 1
		0x59,	// Pad 2
		0x5a,	// Pad 3
		0x48,	// Pad 4
		0x49,	// Pad 5
		0x4a,	// Pad 6
		0x37,	// Pad 7
		0x38,	// Pad 8
		0x39,	// Pad 9
		0x64,	// Pad 0
		0x65,	// Pad .
		0x69,	// <
		KEY_Menu,	// Menu
		0x00,	// Power unmapped
		B_NUMPAD_EQUAL_KEY,	// Pad =
		0x00,	// F13 unmapped
		0x00,	// F14 unmapped
		0x00,	// F15 unmapped
		0x00,	// F16 unmapped
		0x00,	// F17 unmapped
		0x00,	// F18 unmapped
		0x00,	// F19 unmapped
		0x00,	// F20 unmapped
		0x00,	// F21 unmapped
		0x00,	// F22 unmapped
		0x00,	// F23 unmapped
		0x00,	// F24 unmapped
		0x00,	// Execute unmapped
		0x00,	// Help unmapped
		0x00,	// Menu unmapped
		0x00,	// Select unmapped
		0x00,	// Stop unmapped
		0x00,	// Again unmapped
		0x00,	// Undo unmapped
		0x00,	// Cut unmapped
		0x00,	// Copy unmapped
		0x00,	// Paste unmapped
		0x00,	// Find unmapped
		0x00,	// Mute unmapped
		0x00,	// Volume up unmapped
		0x00,	// Volume down unmapped
		0x00,	// CapsLock unmapped
		0x00,	// NumLock unmapped
		0x00,	// Scroll lock unmapped
		0x70,	// Keypad . on Brazilian ABNT2
		0x00,	// = sign
		0x6b,	// Ro (\\ key, japanese)
		0x6e,	// Katakana/Hiragana, second key right to spacebar, japanese
		0x6a,	// Yen (macron key, japanese)
		0x6d,	// Henkan, first key right to spacebar, japanese
		0x6c,	// Muhenkan, key left to spacebar, japanese
		0x00,	// Keyboard International6 unmapped
		0x00,	// Keyboard International7 unmapped
		0x00,	// Keyboard International8 unmapped
		0x00,	// Keyboard International9 unmapped
		0xf0,	// Hangul, korean, Kana, Mac japanese USB
		0xf1,	// Hangul_Hanja, korean, Eisu, Mac japanese USB
	};

	static const size_t kKeyTableSize = B_COUNT_OF(kKeyTable);

	bool phantomState = true;
	for (size_t i = 0; i < fKeyCount; i++) {
		if (fCurrentKeys[i] != 1
			|| fKeys[i]->UsagePage() != B_HID_USAGE_PAGE_KEYBOARD) {
			phantomState = false;
			break;
		}
	}

	if (phantomState) {
		// no valid key information is present in this state and we don't
		// want to overwrite our last buffer as otherwise we generate
		// spurious key ups now and spurious key downs when leaving the
		// phantom state again
		return B_OK;
	}

	static bool sysReqPressed = false;

	bool keyDown = false;
	uint16 *current = fLastKeys;
	uint16 *compare = fCurrentKeys;
	for (int32 twice = 0; twice < 2; twice++) {
		for (size_t i = 0; i < fKeyCount; i++) {
			if (current[i] == 0 || (current[i] == 1
				&& fKeys[i]->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD))
				continue;

			bool found = false;
			for (size_t j = 0; j < fKeyCount; j++) {
				if (compare[j] == current[i]) {
					found = true;
					break;
				}
			}

			if (found)
				continue;

			// a change occured
			uint32 key = 0;
			if (fKeys[i]->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD) {
				if (current[i] < kKeyTableSize)
					key = kKeyTable[current[i]];

				if (key == B_PAUSE_KEY && (modifiers & ALT_KEYS) != 0)
					key = KEY_Break;
				else if (key == 0xe && (modifiers & ALT_KEYS) != 0) {
					key = KEY_SysRq;
					sysReqPressed = keyDown;
				} else if (sysReqPressed && keyDown
					&& current[i] >= 4 && current[i] <= 29
					&& (fLastModifiers & ALT_KEYS) != 0) {
					// Alt-SysReq+letter was pressed
#ifdef KEYBOARD_SUPPORTS_KDL
#ifdef USB_KDL
					sDebugKeyboardPipe
						= fInputReport.Device()->InterruptPipe();
#endif
					sDebugKeyboardReportSize
						= fInputReport.Parser()->MaxReportSize(HID_REPORT_TYPE_INPUT);
#endif

					char letter = current[i] - 4 + 'a';

					if (debug_emergency_key_pressed(letter)) {
						// we probably have lost some keys, so reset our key
						// state
						sysReqPressed = false;
						continue;
					}
				}
			}

			if (key == 0) {
				// unmapped normal key or consumer/button key
				key = fInputReport.Usages()[0] + current[i];
			}

			_WriteKey(key, keyDown);

			if (keyDown) {
				// repeat handling
				fCurrentRepeatKey = key;
				fCurrentRepeatDelay = fRepeatDelay;
			} else {
				// cancel the repeats if they are for this key
				if (fCurrentRepeatKey == key) {
					fCurrentRepeatDelay = B_INFINITE_TIMEOUT;
					fCurrentRepeatKey = 0;
				}
			}
		}

		current = fCurrentKeys;
		compare = fLastKeys;
		keyDown = true;
	}

	memcpy(fLastKeys, fCurrentKeys, fKeyCount * sizeof(uint16));
	return B_OK;
}