* Completely rework the USB HID driver to make use of the HID framework

introduced earlier.
* Reworked the previous device classes to make them ProtocolHandlers handling
  their respective input_server <-> driver protocol.
* Implement setting report item data and building/sending reports based on that.
* Remove the old HID parsing code.

This enables us to use all HID devices as we now parse and use the HID
descriptors/reports. Non-boot-porotocol devices should therefore work.

The next step will be to implement a generic input/output framework in userland
that can communicate with a generic protocol handler in usb_hid. This will then
enable applications to make use of all the non-mapped HID stuff directly.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31790 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2009-07-27 00:35:21 +00:00
parent d665b9ca2e
commit 8e1857f795
20 changed files with 945 additions and 1172 deletions
+74 -53
View File
@@ -17,13 +17,13 @@
// HID Interface Subclasses
enum {
USB_HID_INTERFACE_NO_SUBCLASS = 0x00, // No Subclass
USB_HID_INTERFACE_NO_SUBCLASS = 0x00, // No Subclass
USB_HID_INTERFACE_BOOT_SUBCLASS // Boot Interface Subclass
};
// HID Class-Specific descriptor subtypes
enum {
USB_HID_DESCRIPTOR_HID = 0x21,
USB_HID_DESCRIPTOR_HID = 0x21,
USB_HID_DESCRIPTOR_REPORT,
USB_HID_DESCRIPTOR_PHYSICAL
};
@@ -34,7 +34,7 @@ enum {
USB_REQUEST_HID_GET_IDLE,
USB_REQUEST_HID_GET_PROTOCOL,
USB_REQUEST_HID_SET_REPORT = 0x09,
USB_REQUEST_HID_SET_REPORT = 0x09,
USB_REQUEST_HID_SET_IDLE,
USB_REQUEST_HID_SET_PROTOCOL
};
@@ -55,67 +55,88 @@ typedef struct {
/* Usage Pages/IDs */
enum {
USAGE_PAGE_GENERIC_DESKTOP = 0x1,
USAGE_PAGE_SIMULATION,
USAGE_PAGE_VR,
USAGE_PAGE_SPORT,
USAGE_PAGE_GAME,
USAGE_PAGE_GENERIC,
USAGE_PAGE_KEYBOARD,
USAGE_PAGE_LED,
USAGE_PAGE_BUTTON,
USAGE_PAGE_ORDINAL,
USAGE_PAGE_TELEPHONY,
USAGE_PAGE_CONSUMER,
USAGE_PAGE_DIGITIZER,
USAGE_PAGE_PID = 0xf,
USAGE_PAGE_UNICODE,
USAGE_PAGE_ALPHANUM_DISPLAY = 0x14,
USAGE_PAGE_MEDICAL = 0x40,
USAGE_PAGE_MICROSOFT = 0xff00
HID_USAGE_PAGE_GENERIC_DESKTOP = 0x1,
HID_USAGE_PAGE_SIMULATION,
HID_USAGE_PAGE_VR,
HID_USAGE_PAGE_SPORT,
HID_USAGE_PAGE_GAME,
HID_USAGE_PAGE_GENERIC,
HID_USAGE_PAGE_KEYBOARD,
HID_USAGE_PAGE_LED,
HID_USAGE_PAGE_BUTTON,
HID_USAGE_PAGE_ORDINAL,
HID_USAGE_PAGE_TELEPHONY,
HID_USAGE_PAGE_CONSUMER,
HID_USAGE_PAGE_DIGITIZER,
HID_USAGE_PAGE_PID = 0xf,
HID_USAGE_PAGE_UNICODE,
HID_USAGE_PAGE_ALPHANUM_DISPLAY = 0x14,
HID_USAGE_PAGE_MEDICAL = 0x40,
HID_USAGE_PAGE_MICROSOFT = 0xff00
};
/* Page 1: Generic Desktop */
enum {
USAGE_ID_POINTER = 0x1,
USAGE_ID_MOUSE,
USAGE_ID_JOYSTICK = 0x4,
USAGE_ID_GAMEPAD,
USAGE_ID_KEYBOARD,
USAGE_ID_KEYPAD,
USAGE_ID_MULTIAXIS = 0x8,
HID_USAGE_ID_POINTER = 0x1,
HID_USAGE_ID_MOUSE,
HID_USAGE_ID_JOYSTICK = 0x4,
HID_USAGE_ID_GAMEPAD,
HID_USAGE_ID_KEYBOARD,
HID_USAGE_ID_KEYPAD,
HID_USAGE_ID_MULTIAXIS = 0x8,
USAGE_ID_X = 0x30,
USAGE_ID_Y,
USAGE_ID_Z,
USAGE_ID_RX,
USAGE_ID_RY,
USAGE_ID_RZ,
USAGE_ID_SLIDER,
USAGE_ID_DIAL,
USAGE_ID_WHEEL,
USAGE_ID_HAT_SWITCH,
USAGE_ID_COUNTED_BUFFER,
USAGE_ID_BYTE_COUNT,
USAGE_ID_MOTION_WAKEUP,
USAGE_ID_START,
USAGE_ID_SELECT,
USAGE_ID_VX = 0x40,
USAGE_ID_VY,
USAGE_ID_VZ,
USAGE_ID_VBRX,
USAGE_ID_VBRY,
USAGE_ID_VBRZ,
USAGE_ID_VNO,
USAGE_ID_FEATURE_NOTIFICATION
HID_USAGE_ID_X = 0x30,
HID_USAGE_ID_Y,
HID_USAGE_ID_Z,
HID_USAGE_ID_RX,
HID_USAGE_ID_RY,
HID_USAGE_ID_RZ,
HID_USAGE_ID_SLIDER,
HID_USAGE_ID_DIAL,
HID_USAGE_ID_WHEEL,
HID_USAGE_ID_HAT_SWITCH,
HID_USAGE_ID_COUNTED_BUFFER,
HID_USAGE_ID_BYTE_COUNT,
HID_USAGE_ID_MOTION_WAKEUP,
HID_USAGE_ID_START,
HID_USAGE_ID_SELECT,
HID_USAGE_ID_VX = 0x40,
HID_USAGE_ID_VY,
HID_USAGE_ID_VZ,
HID_USAGE_ID_VBRX,
HID_USAGE_ID_VBRY,
HID_USAGE_ID_VBRZ,
HID_USAGE_ID_VNO,
HID_USAGE_ID_FEATURE_NOTIFICATION
};
/* Page 2: Simulation */
enum {
USAGE_ID_RUDDER = 0xBA,
USAGE_ID_THROTTLE = 0xBB,
HID_USAGE_ID_RUDDER = 0xba,
HID_USAGE_ID_THROTTLE = 0xbb,
};
/* Page 7: Keyboard */
enum {
HID_USAGE_ID_LEFT_CONTROL = 0xe0,
HID_USAGE_ID_LEFT_SHIFT,
HID_USAGE_ID_LEFT_ALT,
HID_USAGE_ID_LEFT_GUI,
HID_USAGE_ID_RIGHT_CONTROL,
HID_USAGE_ID_RIGHT_SHIFT,
HID_USAGE_ID_RIGHT_ALT,
HID_USAGE_ID_RIGHT_GUI
};
/* Page 8: LED */
enum {
HID_USAGE_ID_LED_NUM_LOCK = 0x01,
HID_USAGE_ID_LED_CAPS_LOCK,
HID_USAGE_ID_LED_SCROLL_LOCK
};
#endif // _USB_HID_H
@@ -1,22 +1,18 @@
/*
Driver for USB Human Interface Devices.
Copyright (C) 2008 Michael Lotz <[email protected]>
Copyright (C) 2008-2009 Michael Lotz <[email protected]>
Distributed under the terms of the MIT license.
Some parts of the code are based on the previous usb_hid driver which
was written by Jérôme Duval.
*/
#include "DeviceList.h"
#include "Driver.h"
#include "HIDDevice.h"
#include "ProtocolHandler.h"
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
#include <lock.h> // for mutex
#else
#include "BeOSCompatibility.h" // for pseudo mutex
#endif
#include <lock.h>
#include <new>
#include <stdio.h>
#include <string.h>
int32 api_version = B_CUR_DRIVER_API_VERSION;
@@ -74,17 +70,43 @@ usb_hid_device_added(usb_device device, void **cookie)
i, interfaceClass, interfaceSubclass,
interface->descr->interface_protocol);
if (interfaceClass == USB_INTERFACE_CLASS_HID
&& interfaceSubclass == USB_INTERFACE_SUBCLASS_HID_BOOT) {
if (interfaceClass == USB_INTERFACE_CLASS_HID) {
mutex_lock(&sDriverLock);
HIDDevice *hidDevice = HIDDevice::MakeHIDDevice(device, config, i);
HIDDevice *hidDevice
= new(std::nothrow) HIDDevice(device, config, i);
if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) {
hidDevice->SetParentCookie(parentCookie);
gDeviceList->AddDevice(hidDevice->Name(), hidDevice);
devicesFound = true;
for (uint32 i = 0;; i++) {
ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i);
if (handler == NULL)
break;
// As devices can be un- and replugged at will, we cannot
// simply rely on a device count. If there is just one
// keyboard, this does not mean that it uses the 0 name.
// There might have been two keyboards and the one using 0
// might have been unplugged. So we just generate names
// until we find one that is not currently in use.
int32 index = 0;
char pathBuffer[128];
const char *basePath = handler->BasePath();
while (true) {
sprintf(pathBuffer, "%s%ld", basePath, index++);
if (gDeviceList->FindDevice(pathBuffer) == NULL) {
// this name is still free, use it
handler->SetPublishPath(strdup(pathBuffer));
break;
}
}
gDeviceList->AddDevice(handler->PublishPath(), handler);
devicesFound = true;
}
} else
delete hidDevice;
mutex_unlock(&sDriverLock);
}
}
@@ -105,21 +127,32 @@ usb_hid_device_removed(void *cookie)
TRACE("device_removed(%ld)\n", parentCookie);
for (int32 i = 0; i < gDeviceList->CountDevices(); i++) {
HIDDevice *device = (HIDDevice *)gDeviceList->DeviceAt(i);
if (!device)
ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i);
if (!handler)
continue;
if (device->ParentCookie() == parentCookie) {
// this device belongs to the one removed
if (device->IsOpen()) {
// the device will be deleted upon being freed
device->Removed();
} else {
// remove the device and start over
gDeviceList->RemoveDevice(NULL, device);
i = -1;
}
HIDDevice *device = handler->Device();
if (device->ParentCookie() != parentCookie)
continue;
// this handler's device belongs to the one removed
if (device->IsOpen()) {
// the device and it's handlers will be deleted in the free hook
device->Removed();
break;
}
// remove all the handlers
for (uint32 i = 0;; i++) {
handler = device->ProtocolHandlerAt(i);
if (handler == NULL)
break;
gDeviceList->RemoveDevice(NULL, handler);
}
delete device;
break;
}
mutex_unlock(&sDriverLock);
@@ -136,14 +169,14 @@ usb_hid_open(const char *name, uint32 flags, void **cookie)
TRACE("open(%s, %lu, %p)\n", name, flags, cookie);
mutex_lock(&sDriverLock);
HIDDevice *device = (HIDDevice *)gDeviceList->FindDevice(name);
if (device == NULL) {
ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(name);
if (handler == NULL) {
mutex_unlock(&sDriverLock);
return B_ENTRY_NOT_FOUND;
}
status_t result = device->Open(flags);
*cookie = device;
status_t result = handler->Open(flags);
*cookie = handler;
mutex_unlock(&sDriverLock);
return result;
}
@@ -152,9 +185,9 @@ usb_hid_open(const char *name, uint32 flags, void **cookie)
static status_t
usb_hid_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
TRACE("read(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
HIDDevice *device = (HIDDevice *)cookie;
return device->Read((uint8 *)buffer, numBytes);
TRACE_ALWAYS("read on hid device\n");
*numBytes = 0;
return B_ERROR;
}
@@ -162,9 +195,9 @@ static status_t
usb_hid_write(void *cookie, off_t position, const void *buffer,
size_t *numBytes)
{
TRACE("write(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
HIDDevice *device = (HIDDevice *)cookie;
return device->Write((const uint8 *)buffer, numBytes);
TRACE_ALWAYS("write on hid device\n");
*numBytes = 0;
return B_ERROR;
}
@@ -172,8 +205,8 @@ static status_t
usb_hid_control(void *cookie, uint32 op, void *buffer, size_t length)
{
TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
HIDDevice *device = (HIDDevice *)cookie;
return device->Control(op, buffer, length);
ProtocolHandler *handler = (ProtocolHandler *)cookie;
return handler->Control(op, buffer, length);
}
@@ -181,8 +214,8 @@ static status_t
usb_hid_close(void *cookie)
{
TRACE("close(%p)\n", cookie);
HIDDevice *device = (HIDDevice *)cookie;
return device->Close();
ProtocolHandler *handler = (ProtocolHandler *)cookie;
return handler->Close();
}
@@ -190,18 +223,27 @@ static status_t
usb_hid_free(void *cookie)
{
TRACE("free(%p)\n", cookie);
HIDDevice *device = (HIDDevice *)cookie;
mutex_lock(&sDriverLock);
status_t status = device->Free();
if (device->IsRemoved()
&& gDeviceList->RemoveDevice(NULL, device) == B_OK) {
// the device is removed already but as it was open the removed hook
// has not deleted the object
HIDDevice *device = ((ProtocolHandler *)cookie)->Device();
if (device->IsOpen()) {
// another handler of this device is still open so we can't free it
} else if (device->IsRemoved()) {
// the parent device is removed already and none of its handlers are
// open anymore so we can free it here
for (uint32 i = 0;; i++) {
ProtocolHandler *handler = device->ProtocolHandlerAt(i);
if (handler == NULL)
break;
gDeviceList->RemoveDevice(NULL, handler);
}
delete device;
}
mutex_unlock(&sDriverLock);
return status;
return B_OK;
}
@@ -21,9 +21,6 @@
#define USB_DEFAULT_CONFIGURATION 0
#define USB_VENDOR_WACOM 0x056a
#define USB_HID_DEVICE_TYPE_KEYBOARD 0x06090105
#define USB_HID_DEVICE_TYPE_MOUSE 0x02090105
extern usb_module_info *gUSBModule;
extern DeviceList *gDeviceList;
@@ -1,78 +1,44 @@
/*
Driver for USB Human Interface Devices.
Copyright (C) 2008 Michael Lotz <[email protected]>
Copyright (C) 2008-2009 Michael Lotz <[email protected]>
Distributed under the terms of the MIT license.
*/
#include "Driver.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "ProtocolHandler.h"
#include <usb/USB_hid.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <new>
// includes for the different device types
#include "KeyboardDevice.h"
#include "MouseDevice.h"
HIDDevice::HIDDevice(usb_device device, usb_pipe interruptPipe,
size_t interfaceIndex, report_insn *instructions, size_t instructionCount,
size_t totalReportSize, size_t ringBufferSize)
HIDDevice::HIDDevice(usb_device device, const usb_configuration_info *config,
size_t interfaceIndex)
: fStatus(B_NO_INIT),
fDevice(device),
fInterruptPipe(interruptPipe),
fInterfaceIndex(interfaceIndex),
fInstructions(instructions),
fInstructionCount(instructionCount),
fTotalReportSize(totalReportSize),
fTransferUnprocessed(false),
fTransferStatus(B_ERROR),
fTransferActualLength(0),
fTransferScheduled(0),
fTransferBufferSize(0),
fTransferBuffer(NULL),
fTransferNotifySem(-1),
fName(NULL),
fParentCookie(-1),
fOpen(false),
fOpenCount(0),
fRemoved(false),
fRingBuffer(NULL)
{
if (ringBufferSize > 0)
fRingBuffer = create_ring_buffer(ringBufferSize);
fTransferBuffer = (uint8 *)malloc(fTotalReportSize);
if (fTransferBuffer == NULL) {
fStatus = B_NO_MEMORY;
return;
}
fStatus = B_OK;
}
HIDDevice::~HIDDevice()
{
if (fRingBuffer) {
delete_ring_buffer(fRingBuffer);
fRingBuffer = NULL;
}
if (fTransferNotifySem >= 0)
delete_sem(fTransferNotifySem);
free(fTransferBuffer);
free(fName);
}
HIDDevice *
HIDDevice::MakeHIDDevice(usb_device device,
const usb_configuration_info *config, size_t interfaceIndex)
fParser(this),
fProtocolHandlerCount(0),
fProtocolHandlers(NULL)
{
// read HID descriptor
size_t descriptorLength = sizeof(usb_hid_descriptor);
usb_hid_descriptor *hidDescriptor = (usb_hid_descriptor *)malloc(descriptorLength);
if (hidDescriptor == NULL)
return NULL;
usb_hid_descriptor *hidDescriptor
= (usb_hid_descriptor *)malloc(descriptorLength);
if (hidDescriptor == NULL) {
TRACE_ALWAYS("failed to allocate buffer for hid descriptor\n");
fStatus = B_NO_MEMORY;
return;
}
status_t result = gUSBModule->send_request(device,
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
@@ -80,7 +46,8 @@ HIDDevice::MakeHIDDevice(usb_device device,
USB_HID_DESCRIPTOR_HID << 8, interfaceIndex, descriptorLength,
hidDescriptor, &descriptorLength);
TRACE("get_hid_desc: result: 0x%08lx; length: %lu\n", result, descriptorLength);
TRACE("get hid descriptor: result: 0x%08lx; length: %lu\n", result,
descriptorLength);
if (result == B_OK)
descriptorLength = hidDescriptor->descriptor_info[0].descriptor_length;
else
@@ -88,8 +55,11 @@ HIDDevice::MakeHIDDevice(usb_device device,
free(hidDescriptor);
uint8 *reportDescriptor = (uint8 *)malloc(descriptorLength);
if (reportDescriptor == NULL)
return NULL;
if (reportDescriptor == NULL) {
TRACE_ALWAYS("failed to allocate buffer for report descriptor\n");
fStatus = B_NO_MEMORY;
return;
}
result = gUSBModule->send_request(device,
USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
@@ -97,10 +67,12 @@ HIDDevice::MakeHIDDevice(usb_device device,
USB_HID_DESCRIPTOR_REPORT << 8, interfaceIndex, descriptorLength,
reportDescriptor, &descriptorLength);
TRACE("get_hid_rep_desc: result: 0x%08lx; length: %lu\n", result, descriptorLength);
TRACE("get report descriptor: result: 0x%08lx; length: %lu\n", result,
descriptorLength);
if (result != B_OK) {
TRACE_ALWAYS("failed tot get report descriptor\n");
free(reportDescriptor);
return NULL;
return;
}
#if 1
@@ -118,100 +90,57 @@ HIDDevice::MakeHIDDevice(usb_device device,
}
#endif
// decompose report descriptor
size_t itemCount = descriptorLength;
decomp_item *items = (decomp_item *)malloc(sizeof(decomp_item) * itemCount);
if (items == NULL) {
free(reportDescriptor);
return NULL;
}
decompose_report_descriptor(reportDescriptor, descriptorLength, items,
&itemCount);
uint32 deviceType = *(uint32 *)reportDescriptor;
result = fParser.ParseReportDescriptor(reportDescriptor, descriptorLength);
free(reportDescriptor);
// parse report descriptor
size_t instructionCount = itemCount;
report_insn *instructions
= (report_insn *)malloc(sizeof(report_insn) * instructionCount);
if (instructions == NULL) {
free(items);
return NULL;
if (result != B_OK) {
TRACE_ALWAYS("parsing the report descriptor failed\n");
fStatus = result;
return;
}
int firstReportID = 0;
size_t totalReportSize = 0;
parse_report_descriptor(items, itemCount, instructions,
&instructionCount, &totalReportSize, &firstReportID);
free(items);
report_insn *finalInstructions = (report_insn *)realloc(instructions,
sizeof(report_insn) * instructionCount);
if (finalInstructions == NULL) {
free(instructions);
return NULL;
}
TRACE("%lu items, %lu instructions, %lu bytes\n", itemCount,
instructionCount, totalReportSize);
// find the interrupt in pipe
usb_pipe interruptPipe = 0;
usb_interface_info *interface = config->interface[interfaceIndex].active;
for (size_t i = 0; i < interface->endpoint_count; i++) {
usb_endpoint_descriptor *descriptor = interface->endpoint[i].descr;
if ((descriptor->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
&& (descriptor->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_INTERRUPT) {
interruptPipe = interface->endpoint[i].handle;
&& (descriptor->attributes & USB_ENDPOINT_ATTR_MASK)
== USB_ENDPOINT_ATTR_INTERRUPT) {
fInterruptPipe = interface->endpoint[i].handle;
break;
}
}
if (interruptPipe == 0) {
if (fInterruptPipe == 0) {
TRACE_ALWAYS("didn't find a suitable interrupt pipe\n");
free(finalInstructions);
return NULL;
return;
}
// determine device type and create the device object
HIDDevice *hidDevice = NULL;
if (deviceType == USB_HID_DEVICE_TYPE_KEYBOARD) {
hidDevice = new(std::nothrow) KeyboardDevice(device, interruptPipe,
interfaceIndex, finalInstructions, instructionCount,
totalReportSize);
} else if (deviceType == USB_HID_DEVICE_TYPE_MOUSE) {
hidDevice = new(std::nothrow) MouseDevice(device, interruptPipe,
interfaceIndex, finalInstructions, instructionCount,
totalReportSize);
} else
TRACE_ALWAYS("unsupported device type 0x%08lx\n", deviceType);
fTransferBufferSize = fParser.MaxReportSize();
if (fTransferBufferSize == 0) {
TRACE_ALWAYS("report claims a report size of 0\n");
return;
}
if (hidDevice == NULL)
free(finalInstructions);
return hidDevice;
fTransferBuffer = (uint8 *)malloc(fTransferBufferSize);
if (fTransferBuffer == NULL) {
TRACE_ALWAYS("failed to allocate transfer buffer\n");
fStatus = B_NO_MEMORY;
return;
}
ProtocolHandler::AddHandlers(this, &fProtocolHandlers,
&fProtocolHandlerCount);
fStatus = B_OK;
}
void
HIDDevice::SetBaseName(const char *baseName)
HIDDevice::~HIDDevice()
{
// As devices can be un- and replugged at will, we cannot simply rely on
// a device count. If there is just one keyboard, this does not mean that
// it uses the 0 name. There might have been two keyboards and the one
// using 0 might have been unplugged. So we just generate names until we
// find one that is not currently in use.
int32 index = 0;
char nameBuffer[128];
while (true) {
sprintf(nameBuffer, "%s%ld", baseName, index++);
if (gDeviceList->FindDevice(nameBuffer) == NULL) {
// this name is still free, use it
free(fName);
fName = strdup(nameBuffer);
return;
}
}
for (uint32 i = 0; i < fProtocolHandlerCount; i++)
delete fProtocolHandlers[i];
free(fProtocolHandlers);
free(fTransferBuffer);
}
@@ -223,71 +152,21 @@ HIDDevice::SetParentCookie(int32 cookie)
status_t
HIDDevice::Open(uint32 flags)
HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
{
if (fOpen)
return B_BUSY;
if (fTransferNotifySem < 0) {
fTransferNotifySem = create_sem(0, "hid device transfer notify sem");
if (fTransferNotifySem < 0)
return (status_t)fTransferNotifySem;
}
fOpen = true;
atomic_add(&fOpenCount, 1);
return B_OK;
}
status_t
HIDDevice::Close()
HIDDevice::Close(ProtocolHandler *handler)
{
fOpen = false;
// make threads waiting for a transfer bail out
if (fTransferNotifySem >= 0) {
gUSBModule->cancel_queued_transfers(fInterruptPipe);
delete_sem(fTransferNotifySem);
fTransferNotifySem = -1;
_SetTransferProcessed();
}
atomic_add(&fOpenCount, -1);
return B_OK;
}
status_t
HIDDevice::Free()
{
return B_OK;
}
status_t
HIDDevice::Read(uint8 *buffer, size_t *numBytes)
{
TRACE_ALWAYS("read on hid device\n");
*numBytes = 0;
return B_ERROR;
}
status_t
HIDDevice::Write(const uint8 *buffer, size_t *numBytes)
{
TRACE_ALWAYS("write on hid device\n");
*numBytes = 0;
return B_ERROR;
}
status_t
HIDDevice::Control(uint32 op, void *buffer, size_t length)
{
TRACE_ALWAYS("control on base class\n");
return B_ERROR;
}
void
HIDDevice::Removed()
{
@@ -296,62 +175,46 @@ HIDDevice::Removed()
}
void
HIDDevice::_SetTransferProcessed()
{
fTransferUnprocessed = false;
}
bool
HIDDevice::_IsTransferUnprocessed()
{
return fTransferUnprocessed;
}
status_t
HIDDevice::_ScheduleTransfer()
HIDDevice::MaybeScheduleTransfer()
{
if (fTransferNotifySem < 0)
return B_ERROR;
if (fTransferUnprocessed)
return B_BUSY;
if (fRemoved)
return B_ERROR;
if (atomic_set(&fTransferScheduled, 1) != 0) {
// someone else already caused a transfer to be scheduled
return B_OK;
}
TRACE("scheduling interrupt transfer of %lu bytes\n", fTransferBufferSize);
status_t result = gUSBModule->queue_interrupt(fInterruptPipe,
fTransferBuffer, fTotalReportSize, _TransferCallback, this);
if (result < B_OK) {
fTransferBuffer, fTransferBufferSize, _TransferCallback, this);
if (result != B_OK) {
TRACE_ALWAYS("failed to schedule interrupt transfer 0x%08lx\n", result);
return result;
}
fTransferUnprocessed = true;
return B_OK;
}
int32
HIDDevice::_RingBufferReadable()
{
return ring_buffer_readable(fRingBuffer);
}
status_t
HIDDevice::_RingBufferRead(void *buffer, size_t length)
{
ring_buffer_user_read(fRingBuffer, (uint8 *)buffer, length);
return B_OK;
}
status_t
HIDDevice::_RingBufferWrite(const void *buffer, size_t length)
HIDDevice::SendReport(HIDReport *report)
{
ring_buffer_write(fRingBuffer, (const uint8 *)buffer, length);
return B_OK;
size_t actualLength;
return gUSBModule->send_request(fDevice,
USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
USB_REQUEST_HID_SET_REPORT, 0x200 | report->ID(), fInterfaceIndex,
report->ReportSize(), report->CurrentReport(), &actualLength);
}
ProtocolHandler *
HIDDevice::ProtocolHandlerAt(uint32 index)
{
if (index >= fProtocolHandlerCount)
return NULL;
return fProtocolHandlers[index];
}
@@ -360,7 +223,12 @@ HIDDevice::_TransferCallback(void *cookie, status_t status, void *data,
size_t actualLength)
{
HIDDevice *device = (HIDDevice *)cookie;
device->fTransferStatus = status;
device->fTransferActualLength = actualLength;
release_sem_etc(device->fTransferNotifySem, 1, B_DO_NOT_RESCHEDULE);
if (status == B_DEV_STALLED && !device->fRemoved) {
// try clearing stalls right away, the report listeners will resubmit
gUSBModule->clear_feature(device->fInterruptPipe,
USB_FEATURE_ENDPOINT_HALT);
}
atomic_set(&device->fTransferScheduled, 0);
device->fParser.SetReport(status, device->fTransferBuffer, actualLength);
}
@@ -3,85 +3,66 @@
Copyright (C) 2008 Michael Lotz <[email protected]>
Distributed under the terms of the MIT license.
*/
#ifndef _USB_HID_DEVICE_H_
#define _USB_HID_DEVICE_H_
#ifndef USB_HID_DEVICE_H
#define USB_HID_DEVICE_H
#include "HIDParser.h"
#include "hidparse.h"
#include "ring_buffer.h"
#include <USB3.h>
class ProtocolHandler;
class HIDDevice {
public:
HIDDevice(usb_device device,
usb_pipe interruptPipe,
size_t interfaceIndex,
report_insn *instructions,
size_t instructionCount,
size_t totalReportSize,
size_t ringBufferSize);
virtual ~HIDDevice();
static HIDDevice * MakeHIDDevice(usb_device device,
const usb_configuration_info *config,
size_t interfaceIndex);
void SetBaseName(const char *baseName);
const char * Name() { return fName; };
~HIDDevice();
void SetParentCookie(int32 cookie);
int32 ParentCookie() { return fParentCookie; };
status_t InitCheck() { return fStatus; };
virtual status_t Open(uint32 flags);
bool IsOpen() { return fOpen; };
bool IsOpen() { return fOpenCount > 0; };
status_t Open(ProtocolHandler *handler, uint32 flags);
status_t Close(ProtocolHandler *handler);
virtual status_t Close();
virtual status_t Free();
virtual status_t Read(uint8 *buffer, size_t *numBytes);
virtual status_t Write(const uint8 *buffer, size_t *numBytes);
virtual status_t Control(uint32 op, void *buffer, size_t length);
virtual void Removed();
void Removed();
bool IsRemoved() { return fRemoved; };
protected:
void _SetTransferProcessed();
bool _IsTransferUnprocessed();
status_t _ScheduleTransfer();
status_t MaybeScheduleTransfer();
int32 _RingBufferReadable();
status_t _RingBufferRead(void *buffer, size_t length);
status_t _RingBufferWrite(const void *buffer,
size_t length);
status_t SendReport(HIDReport *report);
status_t fStatus;
usb_device fDevice;
usb_pipe fInterruptPipe;
size_t fInterfaceIndex;
report_insn * fInstructions;
size_t fInstructionCount;
size_t fTotalReportSize;
HIDParser * Parser() { return &fParser; };
ProtocolHandler * ProtocolHandlerAt(uint32 index);
// transfer data
bool fTransferUnprocessed;
status_t fTransferStatus;
size_t fTransferActualLength;
uint8 * fTransferBuffer;
sem_id fTransferNotifySem;
// only to be used for the kernel debugger information
usb_pipe InterruptPipe() { return fInterruptPipe; };
private:
static void _TransferCallback(void *cookie,
status_t status, void *data,
size_t actualLength);
char * fName;
status_t fStatus;
usb_device fDevice;
usb_pipe fInterruptPipe;
size_t fInterfaceIndex;
int32 fTransferScheduled;
size_t fTransferBufferSize;
uint8 * fTransferBuffer;
int32 fParentCookie;
volatile bool fOpen;
int32 fOpenCount;
bool fRemoved;
struct ring_buffer * fRingBuffer;
HIDParser fParser;
uint32 fProtocolHandlerCount;
ProtocolHandler ** fProtocolHandlers;
};
#endif // _USB_HID_DEVICE_H_
#endif // USB_HID_DEVICE_H
@@ -438,6 +438,9 @@ HIDParser::MaxReportSize()
maxSize = report->ReportSize();
}
if (fUsesReportIDs)
maxSize++;
return maxSize;
}
@@ -459,12 +462,13 @@ HIDParser::SetReport(status_t status, uint8 *report, size_t length)
return;
}
HIDReport *target = fReports[0];
HIDReport *target = NULL;
if (fUsesReportIDs) {
target = FindReport(HID_REPORT_TYPE_INPUT, report[0]);
report++;
length--;
}
} else
target = FindReport(HID_REPORT_TYPE_INPUT, 0);
if (target == NULL) {
TRACE_ALWAYS("got report buffer but found no report to handle it\n");
@@ -158,6 +158,33 @@ HIDReport::SetReport(status_t status, uint8 *report, size_t length)
}
status_t
HIDReport::SendReport()
{
size_t reportSize = ReportSize();
uint8 *report = (uint8 *)malloc(reportSize);
if (report == NULL)
return B_NO_MEMORY;
fCurrentReport = report;
memset(fCurrentReport, 0, reportSize);
for (uint32 i = 0; i < fItemsUsed; i++) {
HIDReportItem *item = fItems[i];
if (item == NULL)
continue;
item->Insert();
}
status_t result = fParser->Device()->SendReport(this);
fCurrentReport = NULL;
free(report);
return result;
}
HIDReportItem *
HIDReport::ItemAt(uint32 index)
{
@@ -167,13 +194,25 @@ HIDReport::ItemAt(uint32 index)
}
HIDReportItem *
HIDReport::FindItem(uint16 usagePage, uint16 usageID)
{
for (uint32 i = 0; i < fItemsUsed; i++) {
if (fItems[i]->UsagePage() == usagePage
&& fItems[i]->UsageID() == usageID)
return fItems[i];
}
return NULL;
}
status_t
HIDReport::WaitForReport(bigtime_t timeout)
{
while (atomic_or(&fBusyCount, 0) != 0)
while (atomic_get(&fBusyCount) != 0)
snooze(1000);
#ifndef TEST_MODE
ConditionVariableEntry conditionVariableEntry;
fConditionVariable.Add(&conditionVariableEntry);
status_t result = fParser->Device()->MaybeScheduleTransfer();
@@ -182,14 +221,13 @@ HIDReport::WaitForReport(bigtime_t timeout)
return result;
}
result = conditionVariableEntry.Wait(B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
timeout);
result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout);
TRACE("waiting for report returned with result: %s\n", strerror(result));
if (result != B_OK)
return result;
if (fReportStatus != B_OK)
return fReportStatus;
#endif
atomic_add(&fBusyCount, 1);
return B_OK;
@@ -39,8 +39,11 @@ public:
size_t length);
uint8 * CurrentReport() { return fCurrentReport; };
status_t SendReport();
uint32 CountItems() { return fItemsUsed; };
HIDReportItem * ItemAt(uint32 index);
HIDReportItem * FindItem(uint16 usagePage, uint16 usageID);
status_t WaitForReport(bigtime_t timeout);
void DoneProcessing();
@@ -39,6 +39,15 @@ HIDReportItem::UsagePage()
}
uint16
HIDReportItem::UsageID()
{
usage_value value;
value.u.extended = fUsageMinimum;
return value.u.s.usage_id;
}
status_t
HIDReportItem::Extract()
{
@@ -67,6 +76,40 @@ HIDReportItem::Extract()
}
status_t
HIDReportItem::Insert()
{
uint8 *report = fReport->CurrentReport();
if (report == NULL)
return B_NO_INIT;
uint32 value;
memcpy(&value, report + fByteOffset, sizeof(uint32));
value &= ~(fMask << fShift);
if (fValid)
value |= (fData & fMask) << fShift;
memcpy(report + fByteOffset, &value, sizeof(uint32));
return B_OK;
}
status_t
HIDReportItem::SetData(uint32 data)
{
fData = data;
if (Signed()) {
fValid = (int32)fData >= (int32)fMinimum
&& (int32)fData <= (int32)fMaximum;
} else
fValid = fData >= fMinimum && fData <= fMaximum;
return fValid ? B_OK : B_BAD_VALUE;
}
void
HIDReportItem::PrintToStream(uint32 indentLevel)
{
@@ -23,13 +23,19 @@ public:
bool Signed() { return fMinimum > fMaximum; };
uint16 UsagePage();
uint16 UsageID();
uint32 UsageMinimum() { return fUsageMinimum; };
uint32 UsageMaximum() { return fUsageMaximum; };
status_t Extract();
bool Valid() { return fValid; };
status_t Insert();
status_t SetData(uint32 data);
uint32 Data() { return fData; };
bool Valid() { return fValid; };
void PrintToStream(uint32 indentLevel = 0);
private:
HIDReport * fReport;
@@ -1,17 +1,10 @@
SubDir HAIKU_TOP src add-ons kernel drivers input usb_hid ;
SetSubDirSupportedPlatformsBeOSCompatible ;
SubDirC++Flags -fno-rtti ;
SubDirSysHdrs $(HAIKU_TOP) headers os drivers ;
UsePrivateHeaders [ FDirName kernel util ] input ;
UsePrivateHeaders kernel ;
local buffer_impl = ;
if ! $(TARGET_PLATFORM_HAIKU_COMPATIBLE) {
buffer_impl = ring_buffer.cpp ;
}
UsePrivateHeaders [ FDirName kernel util ] input drivers ;
UsePrivateKernelHeaders ;
KernelAddon usb_hid :
DeviceList.cpp
@@ -19,14 +12,10 @@ KernelAddon usb_hid :
HIDDevice.cpp
KeyboardDevice.cpp
MouseDevice.cpp
hidparse.c
$(buffer_impl)
ProtocolHandler.cpp
HIDCollection.cpp
HIDParser.cpp
HIDReport.cpp
HIDReportItem.cpp
;
SEARCH on [ FGristFiles ring_buffer.cpp ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ;
Package haiku-inputkit-cvs :
usb_hid :
boot home config add-ons kernel drivers bin ;
PackageDriverSymLink haiku-inputkit-cvs : input usb_hid ;
@@ -1,14 +1,8 @@
/*
* Copyright 2008 Michael Lotz <mmlr@mlotz.ch>
* Copyright 2008-2009 Michael Lotz <mmlr@mlotz.ch>
* Distributed under the terms of the MIT license.
*/
/*! Driver for USB Human Interface Devices.
Interpretation code based on the previous usb_hid driver which was written
by Jérôme Duval.
*/
#include <string.h>
#include <usb/USB_hid.h>
@@ -18,6 +12,10 @@
#include "Driver.h"
#include "KeyboardDevice.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "HIDReportItem.h"
// input server private for raw_key_info, KB_READ, etc...
#include "kb_mouse_driver.h"
@@ -43,26 +41,81 @@ debug_get_keyboard_config(int argc, char **argv)
// #pragma mark -
KeyboardDevice::KeyboardDevice(usb_device device, usb_pipe interruptPipe,
size_t interfaceIndex, report_insn *instructions, size_t instructionCount,
size_t totalReportSize)
KeyboardDevice::KeyboardDevice(HIDReport *inputReport, HIDReport *outputReport)
:
HIDDevice(device, interruptPipe, interfaceIndex, instructions,
instructionCount, totalReportSize, 512),
ProtocolHandler(inputReport->Device(), "input/keyboard/usb/", 512),
fInputReport(inputReport),
fOutputReport(outputReport),
fRepeatDelay(300000),
fRepeatRate(35000),
fLastTransferBuffer(NULL)
fCurrentRepeatDelay(B_INFINITE_TIMEOUT),
fCurrentRepeatKey(0),
fKeyCount(0),
fModifierCount(0),
fLastModifiers(0),
fCurrentKeys(NULL),
fLastKeys(NULL)
{
fCurrentRepeatDelay = B_INFINITE_TIMEOUT;
fCurrentRepeatKey = 0;
// find modifiers and keys
for (uint32 i = 0; i < inputReport->CountItems(); i++) {
HIDReportItem *item = inputReport->ItemAt(i);
if (!item->HasData())
continue;
fLastTransferBuffer = (uint8 *)malloc(totalReportSize);
if (fLastTransferBuffer == NULL) {
if (item->UsagePage() == HID_USAGE_PAGE_KEYBOARD) {
TRACE("keyboard item with usage %lx\n", item->UsageMinimum());
item->PrintToStream();
if (item->Array()) {
// normal keys handled as array items
if (fKeyCount < MAX_KEYS)
fKeys[fKeyCount++] = item;
} else {
if (item->UsageID() >= HID_USAGE_ID_LEFT_CONTROL
&& item->UsageID() <= HID_USAGE_ID_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);
fLastKeys = (uint8 *)malloc(fKeyCount * 2);
fCurrentKeys = fLastKeys + fKeyCount;
if (fLastKeys == NULL) {
fStatus = B_NO_MEMORY;
return;
}
SetBaseName("input/keyboard/usb/");
// find leds if we have an output report
fLEDs[0] = fLEDs[1] = fLEDs[2] = 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() == HID_USAGE_PAGE_LED) {
switch (item->UsageID()) {
case HID_USAGE_ID_LED_NUM_LOCK:
fLEDs[0] = item;
break;
case HID_USAGE_ID_LED_CAPS_LOCK:
fLEDs[1] = item;
break;
case HID_USAGE_ID_LED_SCROLL_LOCK:
fLEDs[2] = item;
break;
}
}
}
}
if (atomic_add(&sDebuggerCommandAdded, 1) == 0) {
add_debugger_command("get_usb_keyboard_config",
@@ -74,7 +127,7 @@ KeyboardDevice::KeyboardDevice(usb_device device, usb_pipe interruptPipe,
KeyboardDevice::~KeyboardDevice()
{
free(fLastTransferBuffer);
free(fLastKeys);
if (atomic_add(&sDebuggerCommandAdded, -1) == 1) {
remove_debugger_command("get_usb_keyboard_config",
@@ -83,10 +136,62 @@ KeyboardDevice::~KeyboardDevice()
}
ProtocolHandler *
KeyboardDevice::AddHandler(HIDDevice *device)
{
HIDParser *parser = device->Parser();
// try to find the keyboard usage in any input report
HIDReport *input = NULL;
bool foundKeyboardUsage = false;
uint32 reportCount = parser->CountReports(HID_REPORT_TYPE_INPUT);
for (uint32 i = 0; i < reportCount; i++) {
input = parser->ReportAt(HID_REPORT_TYPE_INPUT, i);
for (uint32 j = 0; j < input->CountItems(); j++) {
HIDReportItem *item = input->ItemAt(j);
if (item->UsagePage() == HID_USAGE_PAGE_KEYBOARD) {
// found at least one item with a keyboard usage
foundKeyboardUsage = true;
break;
}
}
if (foundKeyboardUsage)
break;
}
if (!foundKeyboardUsage)
return NULL;
// try to find the led output report
HIDReport *output = NULL;
bool foundOutputReport = false;
reportCount = parser->CountReports(HID_REPORT_TYPE_OUTPUT);
for (uint32 i = 0; i < reportCount; i++) {
output = parser->ReportAt(HID_REPORT_TYPE_OUTPUT, i);
for (uint32 j = 0; j < output->CountItems(); j++) {
HIDReportItem *item = output->ItemAt(j);
if (item->UsagePage() == HID_USAGE_PAGE_LED) {
foundOutputReport = true;
break;
}
}
if (foundOutputReport)
break;
}
return new(std::nothrow) KeyboardDevice(input,
foundOutputReport ? output : NULL);
}
status_t
KeyboardDevice::Open(uint32 flags)
{
status_t status = HIDDevice::Open(flags);
status_t status = ProtocolHandler::Open(flags);
if (status != B_OK) {
TRACE_ALWAYS("keyboard device failed to open: %s\n",
strerror(status));
@@ -95,7 +200,6 @@ KeyboardDevice::Open(uint32 flags)
fCurrentRepeatDelay = B_INFINITE_TIMEOUT;
fCurrentRepeatKey = 0;
return B_OK;
}
@@ -107,30 +211,15 @@ KeyboardDevice::Control(uint32 op, void *buffer, size_t length)
case KB_READ:
{
bigtime_t enterTime = system_time();
while (_RingBufferReadable() == 0) {
if (!_IsTransferUnprocessed()) {
status_t result = _ScheduleTransfer();
if (result != B_OK)
return result;
}
// NOTE: this thread is now blocking until the semaphore is
// released from the callback function or the repeat timeout
// expires
status_t result = acquire_sem_etc(fTransferNotifySem, 1,
B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, fCurrentRepeatDelay);
if (result == B_OK) {
result = _InterpretBuffer();
_SetTransferProcessed();
if (result != B_OK)
return result;
} else if (result != B_TIMED_OUT)
while (RingBufferReadable() == 0) {
status_t result = _ReadReport(fCurrentRepeatDelay);
if (result != B_OK && result != B_TIMED_OUT)
return result;
if (!IsOpen())
if (!Device()->IsOpen())
return B_ERROR;
if (_RingBufferReadable() == 0 && fCurrentRepeatKey != 0
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
@@ -146,7 +235,7 @@ KeyboardDevice::Control(uint32 op, void *buffer, size_t length)
// 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));
return RingBufferRead(buffer, sizeof(raw_key_info));
}
case KB_SET_LEDS:
@@ -203,50 +292,72 @@ KeyboardDevice::_WriteKey(uint32 key, bool down)
info.be_keycode = key;
info.is_keydown = down;
info.timestamp = system_time();
_RingBufferWrite(&info, sizeof(raw_key_info));
RingBufferWrite(&info, sizeof(raw_key_info));
}
status_t
KeyboardDevice::_SetLEDs(uint8 *data)
{
if (IsRemoved())
if (fOutputReport == NULL || fOutputReport->Device()->IsRemoved())
return B_ERROR;
uint8 leds = 0;
if (data[0] == 1)
leds |= (1 << 0);
if (data[1] == 1)
leds |= (1 << 1);
if (data[2] == 1)
leds |= (1 << 2);
for (uint32 i = 0; i < MAX_LEDS; i++) {
if (fLEDs[i] == NULL)
continue;
size_t actualLength;
return gUSBModule->send_request(fDevice,
USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
USB_REQUEST_HID_SET_REPORT,
0x200 | 0 /* TODO: report id */, fInterfaceIndex,
sizeof(leds), &leds, &actualLength);
fLEDs[i]->SetData(data[i]);
}
return fOutputReport->SendReport();
}
status_t
KeyboardDevice::_InterpretBuffer()
KeyboardDevice::_ReadReport(bigtime_t timeout)
{
if (fTransferStatus != B_OK) {
if (IsRemoved())
return B_ERROR;
if (fTransferStatus == B_DEV_STALLED
&& gUSBModule->clear_feature(fInterruptPipe,
USB_FEATURE_ENDPOINT_HALT) != B_OK)
status_t result = fInputReport->WaitForReport(timeout);
if (result != B_OK) {
if (fInputReport->Device()->IsRemoved()) {
TRACE("device has been removed\n");
return B_ERROR;
}
TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
// signal that we simply want to try again
return B_OK;
}
const static uint32 kModifierTable[] = {
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() - HID_USAGE_ID_LEFT_CONTROL);
}
}
for (uint32 i = 0; i < fKeyCount; i++) {
HIDReportItem *key = fKeys[i];
if (key == NULL)
break;
if (key->Extract() == B_OK && key->Valid()) {
// note that if there are ever more than 8 bit usage ids
// we need to change all these key arrays to use bugger sizes
fCurrentKeys[i] = key->Data();
} else
fCurrentKeys[i] = 0;
}
fInputReport->DoneProcessing();
static const uint32 kModifierTable[] = {
KEY_ControlL,
KEY_ShiftL,
KEY_AltL,
@@ -257,7 +368,16 @@ KeyboardDevice::_InterpretBuffer()
KEY_WinR
};
const static uint32 kKeyTable[] = {
// 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
@@ -400,18 +520,12 @@ KeyboardDevice::_InterpretBuffer()
0x6c, // Muhenkan, key left to spacebar, japanese
};
const static size_t kKeyTableSize
static const size_t kKeyTableSize
= sizeof(kKeyTable) / sizeof(kKeyTable[0]);
uint8 modifierChange = fLastTransferBuffer[0] ^ fTransferBuffer[0];
for (uint8 i = 0; modifierChange; i++, modifierChange >>= 1) {
if (modifierChange & 1)
_WriteKey(kModifierTable[i], (fTransferBuffer[0] >> i) & 1);
}
bool phantomState = true;
for (size_t i = 2; i < fTotalReportSize; i++) {
if (fTransferBuffer[i] != 0x01) {
for (size_t i = 0; i < fKeyCount; i++) {
if (fCurrentKeys[i] != 0x01) {
phantomState = false;
break;
}
@@ -428,15 +542,15 @@ KeyboardDevice::_InterpretBuffer()
static bool sysReqPressed = false;
bool keyDown = false;
uint8 *current = fLastTransferBuffer;
uint8 *compare = fTransferBuffer;
uint8 *current = fLastKeys;
uint8 *compare = fCurrentKeys;
for (int32 twice = 0; twice < 2; twice++) {
for (size_t i = 2; i < fTotalReportSize; i++) {
for (size_t i = 0; i < fKeyCount; i++) {
if (current[i] == 0x00 || current[i] == 0x01)
continue;
bool found = false;
for (size_t j = 2; j < fTotalReportSize; j++) {
for (size_t j = 0; j < fKeyCount; j++) {
if (compare[j] == current[i]) {
found = true;
break;
@@ -451,17 +565,18 @@ KeyboardDevice::_InterpretBuffer()
if (current[i] < kKeyTableSize)
key = kKeyTable[current[i]];
if (key == KEY_Pause && (current[0] & ALT_KEYS) != 0)
if (key == KEY_Pause && (modifiers & ALT_KEYS) != 0)
key = KEY_Break;
else if (key == 0xe && (current[0] & ALT_KEYS) != 0) {
else if (key == 0xe && (modifiers & ALT_KEYS) != 0) {
key = KEY_SysRq;
sysReqPressed = keyDown;
} else if (sysReqPressed && keyDown
&& current[i] >= 4 && current[i] <= 30
&& (fLastTransferBuffer[0] & ALT_KEYS) != 0) {
&& current[i] >= 4 && current[i] <= 29
&& (fLastModifiers & ALT_KEYS) != 0) {
// Alt-SysReq+letter was pressed
sDebugKeyboardPipe = fInterruptPipe;
sDebugKeyboardReportSize = fTotalReportSize;
sDebugKeyboardPipe = fInputReport->Device()->InterruptPipe();
sDebugKeyboardReportSize
= fInputReport->Parser()->MaxReportSize();
char letter = current[i] - 4 + 'a';
@@ -490,11 +605,11 @@ KeyboardDevice::_InterpretBuffer()
}
}
current = fTransferBuffer;
compare = fLastTransferBuffer;
current = fCurrentKeys;
compare = fLastKeys;
keyDown = true;
}
memcpy(fLastTransferBuffer, fTransferBuffer, fTotalReportSize);
memcpy(fLastKeys, fCurrentKeys, fKeyCount);
return B_OK;
}
@@ -1,37 +1,52 @@
/*
Driver for USB Human Interface Devices.
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
Copyright (C) 2008-2009 Michael Lotz <mmlr@mlotz.ch>
Distributed under the terms of the MIT license.
*/
#ifndef _USB_KEYBOARD_DEVICE_H_
#define _USB_KEYBOARD_DEVICE_H_
#ifndef USB_KEYBOARD_DEVICE_H
#define USB_KEYBOARD_DEVICE_H
#include "HIDDevice.h"
#include "ProtocolHandler.h"
class KeyboardDevice : public HIDDevice {
#define MAX_MODIFIERS 16
#define MAX_KEYS 16
#define MAX_LEDS 3
class HIDReportItem;
class KeyboardDevice : public ProtocolHandler {
public:
KeyboardDevice(usb_device device,
usb_pipe interruptPipe,
size_t interfaceIndex,
report_insn *instructions,
size_t instructionCount,
size_t totalReportSize);
KeyboardDevice(HIDReport *inputReport,
HIDReport *outputReport);
virtual ~KeyboardDevice();
static ProtocolHandler * AddHandler(HIDDevice *device);
virtual status_t Open(uint32 flags);
virtual status_t Control(uint32 op, void *buffer, size_t length);
private:
void _WriteKey(uint32 key, bool down);
status_t _SetLEDs(uint8 *data);
status_t _InterpretBuffer();
status_t _ReadReport(bigtime_t timeout);
HIDReport * fInputReport;
HIDReport * fOutputReport;
HIDReportItem * fModifiers[MAX_MODIFIERS];
HIDReportItem * fKeys[MAX_KEYS];
HIDReportItem * fLEDs[MAX_LEDS];
bigtime_t fRepeatDelay;
bigtime_t fRepeatRate;
bigtime_t fCurrentRepeatDelay;
uint32 fCurrentRepeatKey;
uint8 * fLastTransferBuffer;
uint32 fKeyCount;
uint32 fModifierCount;
uint8 fLastModifiers;
uint8 * fCurrentKeys;
uint8 * fLastKeys;
};
#endif // _USB_KEYBOARD_DEVICE_H_
#endif // USB_KEYBOARD_DEVICE_H
@@ -1,13 +1,16 @@
/*
Driver for USB Human Interface Devices.
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
Copyright (C) 2008-2009 Michael Lotz <mmlr@mlotz.ch>
Distributed under the terms of the MIT license.
Interpretation code based on the previous usb_hid driver which was written
by Jérôme Duval.
*/
#include "Driver.h"
#include "MouseDevice.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "HIDReportItem.h"
#include <string.h>
#include <usb/USB_hid.h>
@@ -15,18 +18,62 @@
#include "kb_mouse_driver.h"
MouseDevice::MouseDevice(usb_device device, usb_pipe interruptPipe,
size_t interfaceIndex, report_insn *instructions, size_t instructionCount,
size_t totalReportSize)
: HIDDevice(device, interruptPipe, interfaceIndex, instructions,
instructionCount, totalReportSize, 512),
fLastButtons(0),
fClickCount(0),
fLastClickTime(0),
fClickSpeed(250000),
fMaxButtons(16)
MouseDevice::MouseDevice(HIDReport *report, HIDReportItem *xAxis,
HIDReportItem *yAxis)
:
ProtocolHandler(report->Device(), "input/mouse/usb/", 512),
fReport(report),
fXAxis(xAxis),
fYAxis(yAxis),
fWheel(NULL),
fLastButtons(0),
fClickCount(0),
fLastClickTime(0),
fClickSpeed(250000)
{
SetBaseName("input/mouse/usb/");
uint32 buttonCount = 0;
for (uint32 i = 0; i < report->CountItems(); i++) {
HIDReportItem *item = report->ItemAt(i);
if (!item->HasData())
continue;
if (item->UsagePage() == HID_USAGE_PAGE_BUTTON
&& item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS)
fButtons[buttonCount++] = item;
}
fButtons[buttonCount] = NULL;
fWheel = report->FindItem(HID_USAGE_PAGE_GENERIC_DESKTOP,
HID_USAGE_ID_WHEEL);
}
ProtocolHandler *
MouseDevice::AddHandler(HIDDevice *device)
{
HIDParser *parser = device->Parser();
// try to find at least an x and y axis
HIDReport *report = NULL;
HIDReportItem *xAxis = NULL;
HIDReportItem *yAxis = NULL;
uint32 reportCount = parser->CountReports(HID_REPORT_TYPE_INPUT);
for (uint32 i = 0; i < reportCount; i++) {
report = parser->ReportAt(HID_REPORT_TYPE_INPUT, i);
xAxis = report->FindItem(HID_USAGE_PAGE_GENERIC_DESKTOP,
HID_USAGE_ID_X);
yAxis = report->FindItem(HID_USAGE_PAGE_GENERIC_DESKTOP,
HID_USAGE_ID_Y);
if (xAxis != NULL && yAxis != NULL)
break;
}
if (xAxis == NULL || yAxis == NULL)
return NULL;
return new(std::nothrow) MouseDevice(report, xAxis, yAxis);
}
@@ -35,30 +82,16 @@ MouseDevice::Control(uint32 op, void *buffer, size_t length)
{
switch (op) {
case MS_READ:
while (_RingBufferReadable() == 0) {
if (!_IsTransferUnprocessed()) {
status_t result = _ScheduleTransfer();
if (result != B_OK)
return result;
}
// NOTE: this thread is now blocking until the semaphore is
// released in the callback function
status_t result = acquire_sem_etc(fTransferNotifySem, 1,
B_CAN_INTERRUPT, B_INFINITE_TIMEOUT);
if (result != B_OK)
return result;
result = _InterpretBuffer();
_SetTransferProcessed();
while (RingBufferReadable() == 0) {
status_t result = _ReadReport();
if (result != B_OK)
return result;
}
return _RingBufferRead(buffer, sizeof(mouse_movement));
return RingBufferRead(buffer, sizeof(mouse_movement));
case MS_NUM_EVENTS:
return _RingBufferReadable() / sizeof(mouse_movement);
return RingBufferReadable() / sizeof(mouse_movement);
case MS_SET_CLICKSPEED:
#ifdef __HAIKU__
@@ -74,55 +107,43 @@ MouseDevice::Control(uint32 op, void *buffer, size_t length)
status_t
MouseDevice::_InterpretBuffer()
MouseDevice::_ReadReport()
{
if (fTransferStatus != B_OK) {
if (IsRemoved())
return B_ERROR;
if (fTransferStatus == B_DEV_STALLED
&& gUSBModule->clear_feature(fInterruptPipe,
USB_FEATURE_ENDPOINT_HALT) != B_OK)
status_t result = fReport->WaitForReport(B_INFINITE_TIMEOUT);
if (result != B_OK) {
if (fReport->Device()->IsRemoved()) {
TRACE("device has been removed\n");
return B_ERROR;
}
TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
// signal that we simply want to try again
return B_OK;
}
mouse_movement info;
memset(&info, 0, sizeof(info));
for (size_t i = 0; i < fInstructionCount; i++) {
const report_insn *instruction = &fInstructions[i];
int32 value = (((fTransferBuffer[instruction->byte_idx + 1] << 8)
| fTransferBuffer[instruction->byte_idx]) >> instruction->bit_pos)
& ((1 << instruction->num_bits) - 1);
switch (instruction->usage_page) {
case USAGE_PAGE_BUTTON:
if (instruction->usage_id - 1 < fMaxButtons)
info.buttons |= (value & 1) << (instruction->usage_id - 1);
break;
if (fXAxis->Extract() == B_OK && fXAxis->Valid())
info.xdelta = fXAxis->Data();
if (fYAxis->Extract() == B_OK && fYAxis->Valid())
info.ydelta = -fYAxis->Data();
case USAGE_PAGE_GENERIC_DESKTOP:
if (instruction->is_phy_signed)
value = sign_extend(value, instruction->num_bits);
if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid())
info.wheel_ydelta = -fWheel->Data();
switch (instruction->usage_id) {
case USAGE_ID_X:
info.xdelta = value;
break;
for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) {
HIDReportItem *button = fButtons[i];
if (button == NULL)
break;
case USAGE_ID_Y:
info.ydelta = -value;
break;
case USAGE_ID_WHEEL:
info.wheel_ydelta = -value;
break;
}
break;
}
if (button->Extract() == B_OK && button->Valid())
info.buttons |= (button->Data() & 1) << (button->UsageID() - 1);
}
fReport->DoneProcessing();
TRACE("got mouse report\n");
bigtime_t timestamp = system_time();
if (info.buttons != 0) {
if (fLastButtons == 0) {
@@ -138,5 +159,5 @@ MouseDevice::_InterpretBuffer()
fLastButtons = info.buttons;
info.timestamp = timestamp;
return _RingBufferWrite(&info, sizeof(mouse_movement));
return RingBufferWrite(&info, sizeof(mouse_movement));
}
@@ -1,26 +1,34 @@
/*
Driver for USB Human Interface Devices.
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
Copyright (C) 2008-2009 Michael Lotz <mmlr@mlotz.ch>
Distributed under the terms of the MIT license.
*/
#ifndef _USB_MOUSE_DEVICE_H_
#define _USB_MOUSE_DEVICE_H_
#ifndef USB_MOUSE_DEVICE_H
#define USB_MOUSE_DEVICE_H
#include "HIDDevice.h"
#include <InterfaceDefs.h>
class MouseDevice : public HIDDevice {
#include "ProtocolHandler.h"
class HIDReportItem;
class MouseDevice : public ProtocolHandler {
public:
MouseDevice(usb_device device,
usb_pipe interruptPipe,
size_t interfaceIndex,
report_insn *instructions,
size_t instructionCount,
size_t totalReportSize);
MouseDevice(HIDReport *report,
HIDReportItem *xAxis, HIDReportItem *yAxis);
static ProtocolHandler * AddHandler(HIDDevice *device);
virtual status_t Control(uint32 op, void *buffer, size_t length);
private:
status_t _InterpretBuffer();
status_t _ReadReport();
HIDReport * fReport;
HIDReportItem * fXAxis;
HIDReportItem * fYAxis;
HIDReportItem * fWheel;
HIDReportItem * fButtons[B_MAX_MOUSE_BUTTONS];
uint32 fLastButtons;
uint32 fClickCount;
@@ -29,4 +37,4 @@ private:
uint32 fMaxButtons;
};
#endif // _USB_MOUSE_DEVICE_H_
#endif // USB_MOUSE_DEVICE_H
@@ -0,0 +1,141 @@
/*
* Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
* Distributed under the terms of the MIT License.
*/
#include <ring_buffer.h>
#include "Driver.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "ProtocolHandler.h"
// includes for the different protocol handlers
#include "KeyboardDevice.h"
#include "MouseDevice.h"
//#include "GenericDevice.h"
ProtocolHandler::ProtocolHandler(HIDDevice *device, const char *basePath,
size_t ringBufferSize)
: fStatus(B_NO_INIT),
fDevice(device),
fBasePath(basePath),
fPublishPath(NULL),
fRingBuffer(NULL)
{
if (ringBufferSize > 0) {
fRingBuffer = create_ring_buffer(ringBufferSize);
if (fRingBuffer == NULL) {
TRACE_ALWAYS("failed to create requested ring buffer\n");
fStatus = B_NO_MEMORY;
return;
}
}
fStatus = B_OK;
}
ProtocolHandler::~ProtocolHandler()
{
if (fRingBuffer) {
delete_ring_buffer(fRingBuffer);
fRingBuffer = NULL;
}
free(fPublishPath);
}
void
ProtocolHandler::SetPublishPath(char *publishPath)
{
free(fPublishPath);
fPublishPath = publishPath;
}
void
ProtocolHandler::AddHandlers(HIDDevice *device, ProtocolHandler ***handlerList,
uint32 *handlerCount)
{
TRACE("adding protocol handlers\n");
uint32 count = 3;
ProtocolHandler *handlers[count];
handlers[0] = KeyboardDevice::AddHandler(device);
handlers[1] = MouseDevice::AddHandler(device);
handlers[2] = NULL; //GenericDevice::AddHandler(device);
*handlerCount = 0;
uint32 actualCount = 0;
for (uint32 i = 0; i < count; i++) {
if (handlers[i] != NULL)
actualCount++;
}
if (actualCount == 0) {
TRACE_ALWAYS("no handlers for hid device\n");
return;
}
*handlerList = (ProtocolHandler **)malloc(sizeof(ProtocolHandler *)
* actualCount);
if (*handlerList == NULL) {
TRACE_ALWAYS("out of memory allocating handler list\n");
return;
}
for (uint32 i = 0; i < count; i++) {
if (handlers[i] != NULL)
*handlerList[(*handlerCount)++] = handlers[i];
}
TRACE("added %ld handlers for hid device\n", *handlerCount);
}
status_t
ProtocolHandler::Open(uint32 flags)
{
return fDevice->Open(this, flags);
}
status_t
ProtocolHandler::Close()
{
return fDevice->Close(this);
}
status_t
ProtocolHandler::Control(uint32 op, void *buffer, size_t length)
{
TRACE_ALWAYS("control on base class\n");
return B_ERROR;
}
int32
ProtocolHandler::RingBufferReadable()
{
return ring_buffer_readable(fRingBuffer);
}
status_t
ProtocolHandler::RingBufferRead(void *buffer, size_t length)
{
ring_buffer_user_read(fRingBuffer, (uint8 *)buffer, length);
return B_OK;
}
status_t
ProtocolHandler::RingBufferWrite(const void *buffer, size_t length)
{
ring_buffer_write(fRingBuffer, (const uint8 *)buffer, length);
return B_OK;
}
@@ -0,0 +1,52 @@
/*
* Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
* Distributed under the terms of the MIT License.
*/
#ifndef PROTOCOL_HANDLER_H
#define PROTOCOL_HANDLER_H
#include <SupportDefs.h>
class HIDDevice;
class HIDReport;
class ProtocolHandler {
public:
ProtocolHandler(HIDDevice *device,
const char *basePath,
size_t ringBufferSize);
virtual ~ProtocolHandler();
status_t InitCheck() { return fStatus; };
HIDDevice * Device() { return fDevice; };
const char * BasePath() { return fBasePath; };
void SetPublishPath(char *publishPath);
const char * PublishPath() { return fPublishPath; };
static void AddHandlers(HIDDevice *device,
ProtocolHandler ***handlerList,
uint32 *handlerCount);
virtual status_t Open(uint32 flags);
virtual status_t Close();
virtual status_t Control(uint32 op, void *buffer, size_t length);
int32 RingBufferReadable();
status_t RingBufferRead(void *buffer, size_t length);
status_t RingBufferWrite(const void *buffer,
size_t length);
protected:
status_t fStatus;
private:
HIDDevice * fDevice;
const char * fBasePath;
char * fPublishPath;
struct ring_buffer * fRingBuffer;
};
#endif // PROTOCOL_HANDLER_H
@@ -1,352 +0,0 @@
/*****************************************************************************/
// HID usb driver
// Written by Jérôme Duval
//
// hidparse.c
//
// Copyright (c) 2004 Haiku Project
//
// Some portions of code are copyrighted by
// USB Joystick driver for BeOS R5
// Copyright 2000 (C) ITO, Takayuki
// All rights reserved
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <SupportDefs.h>
#include "hidparse.h"
#include <usb/USB_hid.h>
/*
decompose report descriptor into array of uniform structure
*/
int
sign_extend(int value, int size)
{
if (value & (1 << (size - 1)))
return value | (UINT_MAX << size);
return value;
}
int
decompose_report_descriptor(const unsigned char *desc,
size_t desc_len, decomp_item *items, size_t *num_items)
{
unsigned int desc_idx, item_idx;
static int item_size [4] = { 0, 1, 2, 4 };
if (desc == NULL || items == NULL || num_items == NULL ||
desc_len <= 0 || *num_items <= 0)
return FAILURE;
/* process one item per loop */
for (desc_idx = item_idx = 0;
desc_idx < desc_len && item_idx < *num_items; item_idx++) {
item_prefix pfx;
int size;
unsigned short value16;
unsigned char value8;
decomp_item *item = &items [item_idx];
item->offset = desc_idx;
item->prefix = pfx.i = desc [desc_idx++];
item->type = pfx.b.bType;
if (pfx.i == LONG_ITEM_PREFIX) {
/* XXX no long item support */
item->size = desc [desc_idx++];
item->tag = desc [desc_idx++];
item->value = item->signed_value = 0;
desc_idx += item->size;
continue;
}
item->size = size = item_size [pfx.b.bSize];
item->tag = pfx.b.bTag;
switch (size) {
case 4:
item->signed_value =
item->value =
((unsigned long) desc [desc_idx + 3] << 24) |
((unsigned long) desc [desc_idx + 2] << 16) |
( desc [desc_idx + 1] << 8) |
desc [desc_idx ];
break;
case 2:
value16 = (desc [desc_idx + 1] << 8) | desc [desc_idx];
item->value = value16;
item->signed_value = (signed short) value16;
break;
case 1:
value8 = desc [desc_idx];
item->value = value8;
item->signed_value = (signed char) value8;
break;
case 0:
item->value = item->signed_value = 0;
break;
}
desc_idx += size;
}
*num_items = item_idx;
return SUCCESS;
}
/*
parse decomposed items and generate "instructions" to interpret a report
limitations:
only frequently used items are supported
only first collection is used
report ID is abandoned
output/feature reports are not implemented
*/
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
int
parse_report_descriptor(const decomp_item *items, size_t num_items,
report_insn *insns, size_t *num_insns,
size_t *total_report_size, int *first_report_id)
{
#define ITEM_STACK_SIZE 32
/* global items */
typedef struct global_items {
unsigned int usage_page;
BOOL is_log_signed, is_phy_signed;
unsigned long log_max, log_min,
phy_max, phy_min;
signed long signed_log_max, signed_log_min,
signed_phy_max, signed_phy_min;
int report_size, report_id;
unsigned int report_count;
} global_items;
global_items item_state, item_stack [ITEM_STACK_SIZE];
int item_sp = ITEM_STACK_SIZE;
/* local items */
unsigned int usage_id = 0, usage_min = 0, usage_max;
unsigned int insn_idx_bias = 0, i, insn_idx, item_idx;
int bit_pos = 0, byte_idx = 0, collection = 0;
if (items == NULL || insns == NULL || num_insns == NULL ||
*num_insns <= 0 || num_items <= 0)
return FAILURE;
memset (&item_state, 0, sizeof (item_state));
/* interpret one item per loop */
for (insn_idx = item_idx = 0;
item_idx < num_items && insn_idx < *num_insns;
item_idx++)
{
const decomp_item *item = &items [item_idx];
report_insn *insn = &insns [insn_idx];
int tag = item->tag;
switch (item->type) {
case ITEM_TYPE_MAIN:
switch (tag) {
case ITEM_TAG_INPUT:
for (i = 0; i < item_state.report_count && insn_idx < *num_insns; i++) {
if ((item->value & 3) == 2) { /* data,variable */
if (insn_idx_bias == 0) {
insn->usage_page = item_state.usage_page;
insn->usage_id = usage_id;
} else
--insn_idx_bias;
insn->attributes = item->value;
insn->is_log_signed = item_state.is_log_signed;
if (item_state.is_log_signed) {
insn->signed_log_min = item_state.signed_log_min;
insn->signed_log_max = item_state.signed_log_max;
} else {
insn->log_min = item_state.log_min;
insn->log_max = item_state.log_max;
}
insn->is_phy_signed = item_state.is_phy_signed;
if (item_state.is_phy_signed) {
insn->signed_phy_min = item_state.signed_phy_min;
insn->signed_phy_max = item_state.signed_phy_max;
} else {
insn->phy_min = item_state.phy_min;
insn->phy_max = item_state.phy_max;
}
insn->byte_idx = byte_idx;
insn->bit_pos = bit_pos;
insn->num_bits = item_state.report_size;
insn++;
insn_idx++;
}
else
insn_idx_bias = 0; /* XXX */
/* to next bit-field */
bit_pos += item_state.report_size;
if (bit_pos >= 8) {
byte_idx += bit_pos / 8;
bit_pos = bit_pos % 8;
}
}
break;
case ITEM_TAG_OUTPUT:
/* XXX similar to input */
break;
case ITEM_TAG_COLLECTION:
collection++;
insn_idx_bias = 0;
break;
case ITEM_TAG_FEATURE:
/* XXX similar to input */
break;
case ITEM_TAG_END_COLLECTION:
if (--collection == 0)
/* exit at end of first collection */
goto end;
break;
default:
break;
}
break;
case ITEM_TYPE_GLOBAL:
switch (tag) {
case ITEM_TAG_USAGE_PAGE:
item_state.usage_page = item->value;
break;
case ITEM_TAG_LOGICAL_MINIMUM:
item_state.log_min = item->value;
item_state.signed_log_min = item->signed_value;
/* FALLTHROUGH */
case ITEM_TAG_PHYSICAL_MINIMUM:
item_state.phy_min = item->value;
item_state.signed_phy_min = item->signed_value;
break;
case ITEM_TAG_LOGICAL_MAXIMUM:
item_state.log_max = item->value;
item_state.signed_log_max = item->signed_value;
item_state.is_log_signed = item_state.log_min > item_state.log_max;
/* FALLTHROUGH */
case ITEM_TAG_PHYSICAL_MAXIMUM:
item_state.phy_max = item->value;
item_state.signed_phy_max = item->signed_value;
item_state.is_phy_signed = item_state.phy_min > item_state.phy_max;
break;
case ITEM_TAG_UNIT_EXPONENT:
break;
case ITEM_TAG_UNIT:
break;
case ITEM_TAG_REPORT_SIZE:
item_state.report_size = item->value;
break;
case ITEM_TAG_REPORT_ID:
/* FIXME */
if (item_state.report_id == 0) {
item_state.report_id = item->value;
byte_idx++;
}
else
goto end;
break;
case ITEM_TAG_REPORT_COUNT:
item_state.report_count = item->value;
break;
case ITEM_TAG_PUSH:
if (item_sp > 0)
item_stack [--item_sp] = item_state;
break;
case ITEM_TAG_POP:
if (item_sp < ITEM_STACK_SIZE)
item_state = item_stack [item_sp++];
break;
}
break;
case ITEM_TYPE_LOCAL:
switch (tag)
{
case ITEM_TAG_USAGE:
/* can be sequentially used */
/* XXX should be limited to "practical" usages */
/* XXX should support extended (32-bit) usage */
if (insn_idx + insn_idx_bias < *num_insns) {
insn [insn_idx_bias].usage_page = item_state.usage_page;
insn [insn_idx_bias].usage_id = usage_id = item->value;
insn_idx_bias++;
}
break;
case ITEM_TAG_USAGE_MINIMUM:
usage_min = item->value;
break;
case ITEM_TAG_USAGE_MAXIMUM:
usage_max = item->value;
insn_idx_bias = min (usage_max - usage_min + 1,
*num_insns - insn_idx);
for (i = 0; i < insn_idx_bias; i++) {
insn [i].usage_page = item_state.usage_page;
insn [i].usage_id = usage_min + i;
}
break;
case ITEM_TAG_DESIGNATOR_INDEX:
break;
case ITEM_TAG_DESIGNATOR_MINIMUM:
break;
case ITEM_TAG_DESIGNATOR_MAXIMUM:
break;
case ITEM_TAG_STRING_INDEX:
break;
case ITEM_TAG_STRING_MINIMUM:
break;
case ITEM_TAG_STRING_MAXIMUM:
break;
case ITEM_TAG_DELIMITER:
break;
}
break;
}
}
end:
*num_insns = insn_idx;
if (total_report_size != NULL)
*total_report_size = byte_idx;
if (first_report_id != NULL)
*first_report_id = item_state.report_id;
return SUCCESS;
}
@@ -1,174 +0,0 @@
/*****************************************************************************/
// HID usb driver
// Written by Jérôme Duval
//
// hidparse.h
//
// Copyright (c) 2004 Haiku Project
//
// Some portions of code are copyrighted by
// USB Joystick driver for BeOS R5
// Copyright 2000 (C) ITO, Takayuki
// All rights reserved
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/
#include <sys/types.h>
#define SUCCESS 0
#define FAILURE (-1)
#define BOOL int
#define TRUE 1
#define FALSE 0
/*
Report Descriptor Items
see
5.3 Generic Item Format
6.2.2 Report Descriptor
8. Report Protocol
*/
enum
{
ITEM_SIZE_0,
ITEM_SIZE_1,
ITEM_SIZE_2, /* long item */
ITEM_SIZE_4,
};
enum
{
ITEM_TYPE_MAIN,
ITEM_TYPE_GLOBAL,
ITEM_TYPE_LOCAL,
ITEM_TYPE_RESERVED, /* long item */
};
enum
{
/* main items */
ITEM_TAG_INPUT = 8,
ITEM_TAG_OUTPUT,
ITEM_TAG_COLLECTION,
ITEM_TAG_FEATURE,
ITEM_TAG_END_COLLECTION,
/* global items */
ITEM_TAG_USAGE_PAGE = 0,
ITEM_TAG_LOGICAL_MINIMUM,
ITEM_TAG_LOGICAL_MAXIMUM,
ITEM_TAG_PHYSICAL_MINIMUM,
ITEM_TAG_PHYSICAL_MAXIMUM,
ITEM_TAG_UNIT_EXPONENT,
ITEM_TAG_UNIT,
ITEM_TAG_REPORT_SIZE,
ITEM_TAG_REPORT_ID,
ITEM_TAG_REPORT_COUNT,
ITEM_TAG_PUSH,
ITEM_TAG_POP,
/* local items */
ITEM_TAG_USAGE = 0,
ITEM_TAG_USAGE_MINIMUM,
ITEM_TAG_USAGE_MAXIMUM,
ITEM_TAG_DESIGNATOR_INDEX,
ITEM_TAG_DESIGNATOR_MINIMUM,
ITEM_TAG_DESIGNATOR_MAXIMUM,
/* 6 is missing */
ITEM_TAG_STRING_INDEX = 7,
ITEM_TAG_STRING_MINIMUM,
ITEM_TAG_STRING_MAXIMUM,
ITEM_TAG_DELIMITER,
};
typedef struct
{
unsigned char bSize:2, bType:2, bTag:4;
} item_prefix_bitfields;
typedef union
{
item_prefix_bitfields b;
unsigned char i;
} item_prefix;
#define LONG_ITEM_PREFIX 0xfe
/*
parsed item
*/
typedef struct
{
int offset; /* byte offset in report descriptor */
int prefix, size, type, tag;
unsigned long value;
signed long signed_value; /* sign extended: for logical min/max */
} decomp_item;
/*
report "instruction"
*/
typedef struct
{
unsigned int usage_page, usage_id;
int ctrl_type; /* TYPE_*; set by count_controls() */
int attributes;
BOOL is_log_signed, is_phy_signed;
unsigned long log_min, log_max;
unsigned long phy_min, phy_max;
signed long signed_log_min, signed_log_max;
signed long signed_phy_min, signed_phy_max;
/* used to extract bit-field from report */
/* value = (report [byte_idx] >> bit_pos) & ((1 << num_bits) - 1) */
/* N.B. you need sign extension if signed */
int byte_idx, bit_pos, num_bits;
} report_insn;
#if defined(__cplusplus)
extern "C" {
#endif
int sign_extend(int value, int size);
int decompose_report_descriptor
(const unsigned char *desc,
size_t desc_len,
decomp_item *items,
size_t *num_items);
int parse_report_descriptor
(const decomp_item *items,
size_t num_items,
report_insn *insns,
size_t *num_insns,
size_t *total_report_size,
int *first_report_id);
#if defined(__cplusplus)
}
#endif
@@ -1,45 +0,0 @@
#ifndef _KERNEL_CPP_H_
#define _KERNEL_CPP_H_
#include <malloc.h>
inline void *
operator new(size_t size)
{
return malloc(size);
}
inline void *
operator new[](size_t size)
{
return malloc(size);
}
inline void
operator delete(void *pointer)
{
free(pointer);
}
inline void
operator delete[](void *pointer)
{
free(pointer);
}
inline void
terminate(void)
{
}
static inline void
__throw()
{
}
#endif // _KERNEL_CPP_H_