[haiku-commits] r41794 - haiku/trunk/src/add-ons/kernel/drivers/input/usb_hid

  • From: mmlr@xxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 28 May 2011 21:59:03 +0200 (CEST)

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 ...]

Other related posts: