Author: mmlr Date: 2011-05-28 21:59:02 +0200 (Sat, 28 May 2011) New Revision: 41794 Changeset: https://dev.haiku-os.org/changeset/41794 Ticket: https://dev.haiku-os.org/ticket/4499 Ticket: https://dev.haiku-os.org/ticket/5989 Ticket: https://dev.haiku-os.org/ticket/7354 Ticket: https://dev.haiku-os.org/ticket/7429 Ticket: https://dev.haiku-os.org/ticket/7481 Added: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.h haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/MouseProtocolHandler.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/MouseProtocolHandler.h Removed: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.h haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.h Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/Jamfile haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.h Log: * Changed the way how devices are enumerated and protocol handlers are added to handle the different device classes. Handlers are now added based on the application collections that the HID descriptor describes instead of by enumerating the different report items inside the reports. This means that a device is now logically treated as a mouse when it comes with an application collection that designates it as a mouse, instead of when there is a report that contains an X and a Y axis. This resolves the conflicts that gamepads and joysticks were added as mice due to them containing such elements. This therefore fixes #4499 and opens up the way to properly handle other device types like joysticks (#7429), gamepads, tablets (#7354, #5989 and #7481) and so on. I'll work on gamepads/joysticks next and see where we stand for tablets later. * Added a few enumeration functions to HIDCollection to support the above. * Fix the root collection handling. A device doesn't describe a single root collection and then adds everything as a child. Instead it just has multiple collections on level 0. We account for that now by always creating an empty logical collection as the root collection where all the collections of the descriptor get added. * Rename the {Mouse|Keyboard}Device.{cpp|h} to {Mouse|Keyboard}ProtocolHandler.{cpp|h} as that more clearly describes their purpose. These classes are protocol handlers, i.e. they handle the ioctl based mouse and keyboard protocol between the driver and the input_server add-ons. * Change a lot of stuff to use references instead of pointers where it makes sense (not necessarily complete yet). I've tested this successfully on a keyboard with extended keys, a combo device with a keyboard with extended keys and a mouse, a mouse and a gamepad (that now doesn't do anything anymore) and found no regressions. However, since there are a lot of very varied ways how to describe such functions with HID, it's not too unlikely that some more curiously described devices will now stop working. These have to be handled case by case and their usages have to be added to the added to the appropriate handlers (or new handlers have to be written). Please test and create bug reports (preferrably including the report descriptor that is written out to /tmp). Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp 2011-05-28 19:59:02 UTC (rev 41794) @@ -1,5 +1,5 @@ /* - * Copyright 2009, Michael Lotz, mmlr@xxxxxxxxx + * Copyright 2009-2011, Michael Lotz, mmlr@xxxxxxxxx * Distributed under the terms of the MIT License. */ @@ -10,6 +10,7 @@ #endif #include "HIDCollection.h" +#include "HIDReport.h" #include "HIDReportItem.h" #include <new> @@ -36,7 +37,10 @@ usageValue.u.extended = localState.usage_minimum.u.extended; else if (localState.usage_maximum_set) usageValue.u.extended = localState.usage_maximum.u.extended; - else { + else if (type == COLLECTION_LOGICAL) { + // this is just a logical grouping collection + usageValue.u.extended = 0; + } else { TRACE_ALWAYS("non of the possible usages for the collection are set\n"); } @@ -53,6 +57,24 @@ } +uint16 +HIDCollection::UsagePage() +{ + usage_value value; + value.u.extended = fUsage; + return value.u.s.usage_page; +} + + +uint16 +HIDCollection::UsageID() +{ + usage_value value; + value.u.extended = fUsage; + return value.u.s.usage_id; +} + + status_t HIDCollection::AddChild(HIDCollection *child) { @@ -79,6 +101,32 @@ } +uint32 +HIDCollection::CountChildrenFlat(uint8 type) +{ + uint32 count = 0; + if (type == COLLECTION_ALL || fType == type) + count++; + + for (uint32 i = 0; i < fChildCount; i++) { + HIDCollection *child = fChildren[i]; + if (child == NULL) + continue; + + count += child->CountChildrenFlat(type); + } + + return count; +} + + +HIDCollection * +HIDCollection::ChildAtFlat(uint8 type, uint32 index) +{ + return _ChildAtFlat(type, index); +} + + void HIDCollection::AddItem(HIDReportItem *item) { @@ -109,6 +157,28 @@ } +uint32 +HIDCollection::CountItemsFlat() +{ + uint32 count = fItemCount; + + for (uint32 i = 0; i < fChildCount; i++) { + HIDCollection *child = fChildren[i]; + if (child != NULL) + count += child->CountItemsFlat(); + } + + return count; +} + + +HIDReportItem * +HIDCollection::ItemAtFlat(uint32 index) +{ + return _ItemAtFlat(index); +} + + void HIDCollection::PrintToStream(uint32 indentLevel) { @@ -161,3 +231,87 @@ child->PrintToStream(indentLevel + 1); } } + + +HIDCollection * +HIDCollection::_ChildAtFlat(uint8 type, uint32 &index) +{ + if (type == COLLECTION_ALL || fType == type) { + if (index == 0) + return this; + + index--; + } + + for (uint32 i = 0; i < fChildCount; i++) { + HIDCollection *child = fChildren[i]; + if (child == NULL) + continue; + + HIDCollection *result = child->_ChildAtFlat(type, index); + if (result != NULL) + return result; + } + + return NULL; +} + + +HIDReportItem * +HIDCollection::_ItemAtFlat(uint32 &index) +{ + if (index < fItemCount) + return fItems[index]; + + index -= fItemCount; + + for (uint32 i = 0; i < fChildCount; i++) { + HIDCollection *child = fChildren[i]; + if (child == NULL) + continue; + + HIDReportItem *result = child->_ItemAtFlat(index); + if (result != NULL) + return result; + } + + return NULL; +} + + +void +HIDCollection::BuildReportList(uint8 reportType, + HIDReport **reportList, uint32 &reportCount) +{ + + for (uint32 i = 0; i < fItemCount; i++) { + HIDReportItem *item = fItems[i]; + if (item == NULL) + continue; + + HIDReport *report = item->Report(); + if (reportType != HID_REPORT_TYPE_ANY && report->Type() != reportType) + continue; + + bool found = false; + for (uint32 j = 0; j < reportCount; j++) { + if (reportList[j] == report) { + found = true; + break; + } + } + + if (found) + continue; + + reportList[reportCount++] = report; + } + + for (uint32 i = 0; i < fChildCount; i++) { + HIDCollection *child = fChildren[i]; + if (child == NULL) + continue; + + child->BuildReportList(reportType, reportList, reportCount); + } +} Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h 2011-05-28 19:59:02 UTC (rev 41794) @@ -1,5 +1,5 @@ /* - * Copyright 2009, Michael Lotz, mmlr@xxxxxxxxx + * Copyright 2009-2011, Michael Lotz, mmlr@xxxxxxxxx * Distributed under the terms of the MIT License. */ #ifndef HID_COLLECTION_H @@ -7,6 +7,7 @@ #include "HIDParser.h" +class HIDReport; class HIDReportItem; class HIDCollection { @@ -15,19 +16,37 @@ uint8 type, local_item_state &localState); ~HIDCollection(); + uint8 Type() { return fType; }; + + uint16 UsagePage(); + uint16 UsageID(); + HIDCollection * Parent() { return fParent; }; status_t AddChild(HIDCollection *child); uint32 CountChildren() { return fChildCount; }; HIDCollection * ChildAt(uint32 index); + uint32 CountChildrenFlat(uint8 type); + HIDCollection * ChildAtFlat(uint8 type, uint32 index); + void AddItem(HIDReportItem *item); uint32 CountItems() { return fItemCount; }; HIDReportItem * ItemAt(uint32 index); + uint32 CountItemsFlat(); + HIDReportItem * ItemAtFlat(uint32 index); + + void BuildReportList(uint8 reportType, + HIDReport **reportList, + uint32 &reportCount); + void PrintToStream(uint32 indentLevel = 0); private: + HIDCollection * _ChildAtFlat(uint8 type, uint32 &index); + HIDReportItem * _ItemAtFlat(uint32 &index); + HIDCollection * fParent; uint8 fType; Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h 2011-05-28 19:59:02 UTC (rev 41794) @@ -1,5 +1,5 @@ /* - * Copyright 2009, Michael Lotz, mmlr@xxxxxxxxx + * Copyright 2009-2011, Michael Lotz, mmlr@xxxxxxxxx * Distributed under the terms of the MIT License. */ #ifndef HID_DATA_TYPES_H @@ -51,6 +51,7 @@ #define COLLECTION_NAMED_ARRAY 0x04 #define COLLECTION_USAGE_SWITCH 0x05 #define COLLECTION_USAGE_MODIFIER 0x06 +#define COLLECTION_ALL 0xff #define UNIT_SYSTEM 0x0 #define UNIT_LENGTH 0x1 Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp 2011-05-28 19:59:02 UTC (rev 41794) @@ -1,5 +1,5 @@ /* - * Copyright 2008-2009 Michael Lotz <mmlr@xxxxxxxx> + * Copyright 2008-2011, Michael Lotz <mmlr@xxxxxxxx> * Distributed under the terms of the MIT license. */ @@ -35,7 +35,7 @@ fRemoved(false), fParser(this), fProtocolHandlerCount(0), - fProtocolHandlers(NULL) + fProtocolHandlerList(NULL) { uint8 *reportDescriptor = NULL; size_t descriptorLength = 0; @@ -166,18 +166,21 @@ return; } - ProtocolHandler::AddHandlers(this, &fProtocolHandlers, - &fProtocolHandlerCount); + ProtocolHandler::AddHandlers(*this, fProtocolHandlerList, + fProtocolHandlerCount); fStatus = B_OK; } HIDDevice::~HIDDevice() { - for (uint32 i = 0; i < fProtocolHandlerCount; i++) - delete fProtocolHandlers[i]; + ProtocolHandler *handler = fProtocolHandlerList; + while (handler != NULL) { + ProtocolHandler *next = handler->NextHandler(); + delete handler; + handler = next; + } - free(fProtocolHandlers); free(fTransferBuffer); } @@ -250,9 +253,16 @@ ProtocolHandler * HIDDevice::ProtocolHandlerAt(uint32 index) const { - if (index >= fProtocolHandlerCount) - return NULL; - return fProtocolHandlers[index]; + ProtocolHandler *handler = fProtocolHandlerList; + while (handler != NULL) { + if (index == 0) + return handler; + + handler = handler->NextHandler(); + index--; + } + + return NULL; } Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h 2011-05-28 19:59:02 UTC (rev 41794) @@ -1,5 +1,5 @@ /* - * Copyright 2008 Michael Lotz <mmlr@xxxxxxxx> + * Copyright 2008-2011, Michael Lotz <mmlr@xxxxxxxx> * Distributed under the terms of the MIT license. */ #ifndef USB_HID_DEVICE_H @@ -38,7 +38,7 @@ status_t SendReport(HIDReport *report); - HIDParser * Parser() { return &fParser; } + HIDParser & Parser() { return fParser; } ProtocolHandler * ProtocolHandlerAt(uint32 index) const; // only to be used for the kernel debugger information @@ -66,7 +66,7 @@ HIDParser fParser; uint32 fProtocolHandlerCount; - ProtocolHandler ** fProtocolHandlers; + ProtocolHandler * fProtocolHandlerList; }; Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp 2011-05-28 19:59:02 UTC (rev 41794) @@ -1,5 +1,5 @@ /* - * Copyright 2009, Michael Lotz, mmlr@xxxxxxxxx + * Copyright 2009-2011, Michael Lotz, mmlr@xxxxxxxxx * Distributed under the terms of the MIT License. */ @@ -62,7 +62,14 @@ return B_NO_MEMORY; } - HIDCollection *collection = NULL; + fRootCollection = new(std::nothrow) HIDCollection(NULL, COLLECTION_LOGICAL, + localState); + if (fRootCollection == NULL) { + TRACE_ALWAYS("no memory to allocate root collection\n"); + return B_NO_MEMORY; + } + + HIDCollection *collection = fRootCollection; const uint8 *pointer = reportDescriptor; const uint8 *end = pointer + descriptorLength; @@ -138,14 +145,10 @@ break; } - if (collection == NULL) - fRootCollection = newCollection; - else - collection->AddChild(newCollection); - + collection->AddChild(newCollection); collection = newCollection; } else if (item->tag == ITEM_TAG_MAIN_END_COLLECTION) { - if (collection == NULL) { + if (collection == fRootCollection) { TRACE_ALWAYS("end collection with no open one\n"); break; } Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h 2011-05-28 19:59:02 UTC (rev 41794) @@ -17,6 +17,8 @@ uint32 minimum, uint32 maximum, uint32 usageMinimum, uint32 usageMaximum); + HIDReport * Report() { return fReport; }; + bool HasData() { return fHasData; }; bool Relative() { return fRelative; }; bool Array() { return fArray; }; Modified: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/Jamfile 2011-05-28 19:32:38 UTC (rev 41793) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/Jamfile 2011-05-28 19:59:02 UTC (rev 41794) @@ -10,12 +10,13 @@ DeviceList.cpp Driver.cpp HIDDevice.cpp - KeyboardDevice.cpp - MouseDevice.cpp - ProtocolHandler.cpp HIDCollection.cpp HIDParser.cpp HIDReport.cpp HIDReportItem.cpp + + ProtocolHandler.cpp + KeyboardProtocolHandler.cpp + MouseProtocolHandler.cpp ; Copied: haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp (from rev 41762, haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.cpp) =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid/KeyboardProtocolHandler.cpp 2011-05-28 19:59:02 UTC (rev 41794) @@ -0,0 +1,754 @@ +/* + * Copyright 2008-2011 Michael Lotz <mmlr@xxxxxxxx> + * Distributed under the terms of the MIT license. + */ + + +#include <new> +#include <stdlib.h> +#include <string.h> + +#include <usb/USB_hid.h> +#include <util/AutoLock.h> + +#include <debug.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_FLAG_READER 0x01 +#define KEYBOARD_FLAG_DEBUGGER 0x02 + + +static usb_id sDebugKeyboardPipe = 0; +static size_t sDebugKeyboardReportSize = 0; +static int32 sDebuggerCommandAdded = 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; +} + + +// #pragma mark - + + +KeyboardProtocolHandler::KeyboardProtocolHandler(HIDReport &inputReport, + HIDReport *outputReport) + : + ProtocolHandler(inputReport.Device(), "input/keyboard/usb/", 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, "usb keyboard"); + + // find modifiers and keys + 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 %lx\n", item->UsageMinimum()); + + if (item->Array()) { + // normal or "consumer"/button keys handled as array items + if (fKeyCount < MAX_KEYS) + fKeys[fKeyCount++] = item; + } else { + if (item->UsagePage() == B_HID_USAGE_PAGE_KEYBOARD + && item->UsageID() >= B_HID_UID_KB_LEFT_CONTROL + && item->UsageID() <= B_HID_UID_KB_RIGHT_GUI) { + // modifiers are generally implemented as bitmaps + if (fModifierCount < MAX_MODIFIERS) + fModifiers[fModifierCount++] = item; + } + } + } + } + + TRACE("keyboard device with %lu keys and %lu 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; + } + + // 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; + } + } + } + } + + if (atomic_add(&sDebuggerCommandAdded, 1) == 0) { + add_debugger_command("get_usb_keyboard_config", + &debug_get_keyboard_config, + "Gets the required config of the USB keyboard"); + } +} + + +KeyboardProtocolHandler::~KeyboardProtocolHandler() +{ + free(fLastKeys); + + if (atomic_add(&sDebuggerCommandAdded, -1) == 1) { + remove_debugger_command("get_usb_keyboard_config", + &debug_get_keyboard_config); + } + + 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: + case B_HID_UID_GD_SYSTEM_CONTROL: + 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: %lu\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_FLAG_DEBUGGER) != 0) + fHasDebugReader = false; + if ((*cookie & KEYBOARD_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 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_FLAG_READER; + } + + while (true) { + MutexLocker locker(fLock); + + bigtime_t enterTime = system_time(); + while (RingBufferReadable() == 0) { + status_t result = _ReadReport(fCurrentRepeatDelay); + 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_FLAG_DEBUGGER) + == 0) { + // Handover buffer to the debugger instead + locker.Unlock(); + snooze(25000); + continue; + } + + // 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 (user_memcpy(ledData, buffer, sizeof(ledData)) != B_OK) + return B_BAD_ADDRESS; + return _SetLEDs(ledData); + } + + case KB_SET_KEY_REPEAT_RATE: + { + int32 repeatRate; + if (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 (user_memcpy(buffer, &repeatRate, sizeof(repeatRate)) != B_OK) + return B_BAD_ADDRESS; + return B_OK; + } + + case KB_SET_KEY_REPEAT_DELAY: + if (user_memcpy(&fRepeatDelay, buffer, sizeof(fRepeatDelay)) + != B_OK) + return B_BAD_ADDRESS; + return B_OK; + + case KB_GET_KEY_REPEAT_DELAY: + if (user_memcpy(buffer, &fRepeatDelay, sizeof(fRepeatDelay)) + != B_OK) + return B_BAD_ADDRESS; + return B_OK; + + case KB_SET_DEBUG_READER: + if (fHasDebugReader) + return B_BUSY; + + *cookie |= KEYBOARD_FLAG_DEBUGGER; + fHasDebugReader = true; + return B_OK; + } + + TRACE_ALWAYS("keyboard device unhandled control 0x%08lx\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) +{ + 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_TIMED_OUT && result != B_INTERRUPTED) { + // we expect timeouts as we do repeat key handling this way, + // interrupts happen 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()) + fCurrentKeys[i] = key->Data(); + 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 [... truncated: 740 lines follow ...]