* 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:
@@ -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_
|
||||
|
||||
Reference in New Issue
Block a user