diff --git a/src/add-ons/kernel/drivers/input/usb_hid/BeOSCompatibility.h b/src/add-ons/kernel/drivers/input/usb_hid/BeOSCompatibility.h new file mode 100644 index 0000000000..cfbd47c72b --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/BeOSCompatibility.h @@ -0,0 +1,50 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef HAIKU_TARGET_PLATFORM_HAIKU +#ifndef _BEOS_COMPATIBILITY_H_ +#define _BEOS_COMPATIBILITY_H_ + +#include + +typedef struct mutex { + sem_id sem; + int32 count; +} mutex; + + +static inline void +mutex_init(mutex *lock, const char *name) +{ + lock->sem = create_sem(0, name); + lock->count = 0; +} + + +static inline void +mutex_destroy(mutex *lock) +{ + delete_sem(lock->sem); +} + + +static inline status_t +mutex_lock(mutex *lock) +{ + if (atomic_add(&lock->count, -1) < 0) + return acquire_sem(lock->sem); + return B_OK; +} + + +static inline void +mutex_unlock(mutex *lock) +{ + if (atomic_add(&lock->count, 1) < -1) + release_sem(lock->sem); +} + +#endif /* !HAIKU_TARGET_PLATFORM_HAIKU */ +#endif /* _BEOS_COMPATIBILITY_H_ */ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/DeviceList.cpp b/src/add-ons/kernel/drivers/input/usb_hid/DeviceList.cpp new file mode 100644 index 0000000000..206ec6b050 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/DeviceList.cpp @@ -0,0 +1,191 @@ +/* + Generic device list for use in drivers. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#include "DeviceList.h" +#include +#include +#include + +struct device_list_entry { + char * name; + void * device; + device_list_entry * next; +}; + + +DeviceList::DeviceList() + : fDeviceList(NULL), + fDeviceCount(0), + fPublishList(NULL) +{ +} + + +DeviceList::~DeviceList() +{ + _FreePublishList(); + + device_list_entry *current = fDeviceList; + while (current) { + device_list_entry *next = current->next; + free(current->name); + delete current; + current = next; + } +} + + +status_t +DeviceList::AddDevice(const char *name, void *device) +{ + device_list_entry *entry = new device_list_entry; + if (entry == NULL) + return B_NO_MEMORY; + + entry->name = strdup(name); + if (entry->name == NULL) + return B_NO_MEMORY; + + entry->device = device; + entry->next = NULL; + + if (fDeviceList == NULL) + fDeviceList = entry; + else { + device_list_entry *current = fDeviceList; + while (current) { + if (current->next == NULL) { + current->next = entry; + break; + } + + current = current->next; + } + } + + fDeviceCount++; + return B_OK; +} + + +status_t +DeviceList::RemoveDevice(const char *name, void *device) +{ + if (name == NULL && device == NULL) + return B_BAD_VALUE; + + device_list_entry *previous = NULL; + device_list_entry *current = fDeviceList; + while (current) { + if ((name != NULL && strcmp(current->name, name) == 0) + || (device != NULL && current->device == device)) { + if (previous == NULL) + fDeviceList = current->next; + else + previous->next = current->next; + + free(current->name); + delete current; + fDeviceCount--; + return B_OK; + } + + current = current->next; + } + + return B_ENTRY_NOT_FOUND; +} + + +void * +DeviceList::FindDevice(const char *name, void *device) +{ + if (name == NULL && device == NULL) + return NULL; + + device_list_entry *current = fDeviceList; + while (current) { + if ((name != NULL && strcmp(current->name, name) == 0) + || (device != NULL && current->device == device)) + return current->device; + + current = current->next; + } + + return NULL; +} + + +int32 +DeviceList::CountDevices(const char *baseName) +{ + if (baseName == NULL) + return fDeviceCount; + + int32 count = 0; + int32 baseNameLength = strlen(baseName); + device_list_entry *current = fDeviceList; + while (current) { + if (strncmp(current->name, baseName, baseNameLength) == 0) + count++; + + current = current->next; + } + + return count; +} + + +void * +DeviceList::DeviceAt(int32 index) +{ + device_list_entry *current = fDeviceList; + while (current) { + if (index-- == 0) + return current->device; + + current = current->next; + } + + return NULL; +} + + +const char ** +DeviceList::PublishDevices() +{ + _FreePublishList(); + + fPublishList = (char **)malloc((fDeviceCount + 1) * sizeof(char *)); + if (fPublishList == NULL) + return NULL; + + int32 index = 0; + device_list_entry *current = fDeviceList; + while (current) { + fPublishList[index++] = strdup(current->name); + current = current->next; + } + + fPublishList[index] = NULL; + return (const char **)fPublishList; +} + + +void +DeviceList::_FreePublishList() +{ + if (fPublishList == NULL) + return; + + int32 index = 0; + while (fPublishList[index] != NULL) { + free(fPublishList[index]); + index++; + } + + free(fPublishList); + fPublishList = NULL; +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/DeviceList.h b/src/add-ons/kernel/drivers/input/usb_hid/DeviceList.h new file mode 100644 index 0000000000..821cb17752 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/DeviceList.h @@ -0,0 +1,35 @@ +/* + Generic device list for use in drivers. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _DEVICE_LIST_H_ +#define _DEVICE_LIST_H_ + +#include + +struct device_list_entry; + +class DeviceList { +public: + DeviceList(); + ~DeviceList(); + + status_t AddDevice(const char *name, void *device); + status_t RemoveDevice(const char *name, void *device = NULL); + void * FindDevice(const char *name, void *device = NULL); + + int32 CountDevices(const char *baseName = NULL); + void * DeviceAt(int32 index); + + const char ** PublishDevices(); + +private: + void _FreePublishList(); + + device_list_entry * fDeviceList; + int32 fDeviceCount; + char ** fPublishList; +}; + +#endif // _DEVICE_LIST_H_ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/Driver.cpp b/src/add-ons/kernel/drivers/input/usb_hid/Driver.cpp new file mode 100644 index 0000000000..ad337713ce --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/Driver.cpp @@ -0,0 +1,293 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + 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" + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU +#include // for mutex +#else +#include "BeOSCompatibility.h" // for pseudo mutex +#endif + + +int32 api_version = B_CUR_DRIVER_API_VERSION; +usb_module_info *gUSBModule = NULL; +DeviceList *gDeviceList = NULL; +static int32 sParentCookie = 0; +static mutex sDriverLock; + + +// #pragma mark - notify hooks + + +static status_t +usb_hid_device_added(usb_device device, void **cookie) +{ + TRACE("device_added()\n"); + const usb_device_descriptor *deviceDescriptor + = gUSBModule->get_device_descriptor(device); + + TRACE("vendor id: 0x%04x; product id: 0x%04x\n", + deviceDescriptor->vendor_id, deviceDescriptor->product_id); + + // wacom devices are handled by the dedicated wacom driver + if (deviceDescriptor->vendor_id == USB_VENDOR_WACOM) + return B_ERROR; + + const usb_configuration_info *config + = gUSBModule->get_nth_configuration(device, USB_DEFAULT_CONFIGURATION); + if (config == NULL) { + TRACE_ALWAYS("cannot get default configuration\n"); + return B_ERROR; + } + + // ensure default configuration is set + status_t result = gUSBModule->set_configuration(device, config); + if (result != B_OK) { + TRACE_ALWAYS("set_configuration() failed 0x%08lx\n", result); + return result; + } + + // refresh config + config = gUSBModule->get_configuration(device); + if (config == NULL) { + TRACE_ALWAYS("cannot get current configuration\n"); + return B_ERROR; + } + + bool devicesFound = false; + int32 parentCookie = atomic_add(&sParentCookie, 1); + for (size_t i = 0; i < config->interface_count; i++) { + const usb_interface_info *interface = config->interface[i].active; + uint8 interfaceClass = interface->descr->interface_class; + uint8 interfaceSubclass = interface->descr->interface_subclass; + uint8 interfaceProtocol = interface->descr->interface_protocol; + TRACE("interface %lu: class: %u; subclass: %u; protocol: %u\n", + i, interfaceClass, interfaceSubclass, interfaceProtocol); + + if (interfaceClass == USB_INTERFACE_CLASS_HID + && interfaceSubclass == USB_INTERFACE_SUBCLASS_HID_BOOT) { + mutex_lock(&sDriverLock); + HIDDevice *hidDevice = HIDDevice::MakeHIDDevice(device, config, i); + + if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) { + hidDevice->SetParentCookie(parentCookie); + gDeviceList->AddDevice(hidDevice->Name(), hidDevice); + devicesFound = true; + } else + delete hidDevice; + mutex_unlock(&sDriverLock); + } + } + + if (!devicesFound) + return B_ERROR; + + *cookie = (void *)parentCookie; + return B_OK; +} + + +static status_t +usb_hid_device_removed(void *cookie) +{ + mutex_lock(&sDriverLock); + int32 parentCookie = (int32)cookie; + TRACE("device_removed(%ld)\n", parentCookie); + + for (int32 i = 0; i < gDeviceList->CountDevices(); i++) { + HIDDevice *device = (HIDDevice *)gDeviceList->DeviceAt(i); + if (!device) + 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; + } + } + } + + mutex_unlock(&sDriverLock); + return B_OK; +} + + +// #pragma mark - driver hooks + + +static status_t +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) { + mutex_unlock(&sDriverLock); + return B_ENTRY_NOT_FOUND; + } + + status_t result = device->Open(flags); + *cookie = device; + mutex_unlock(&sDriverLock); + return result; +} + + +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); +} + + +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); +} + + +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); +} + + +static status_t +usb_hid_close(void *cookie) +{ + TRACE("close(%p)\n", cookie); + HIDDevice *device = (HIDDevice *)cookie; + return device->Close(); +} + + +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 (gDeviceList->RemoveDevice(NULL, device) == B_OK) { + // the device is removed already but as it was open the removed hook + // has not deleted the object + delete device; + } + + mutex_unlock(&sDriverLock); + return status; +} + + +// #pragma mark - driver API + + +status_t +init_hardware() +{ + TRACE("init_hardware() " __DATE__ " " __TIME__ "\n"); + return B_OK; +} + + +status_t +init_driver() +{ + TRACE("init_driver() " __DATE__ " " __TIME__ "\n"); + if (get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule) != B_OK) + return B_ERROR; + + gDeviceList = new DeviceList(); + if (gDeviceList == NULL) { + put_module(B_USB_MODULE_NAME); + return B_NO_MEMORY; + } + + static usb_notify_hooks notifyHooks = { + &usb_hid_device_added, + &usb_hid_device_removed + }; + + static usb_support_descriptor supportDescriptor = { + USB_INTERFACE_CLASS_HID, 0, 0, 0, 0 + }; + + gUSBModule->register_driver(DRIVER_NAME, &supportDescriptor, 1, NULL); + gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); + TRACE("init_driver() OK\n"); + return B_OK; +} + + +void +uninit_driver() +{ + TRACE("uninit_driver()\n"); + gUSBModule->uninstall_notify(DRIVER_NAME); + put_module(B_USB_MODULE_NAME); + delete gDeviceList; + gDeviceList = NULL; +} + + +const char ** +publish_devices() +{ + TRACE("publish_devices()\n"); + const char **publishList = gDeviceList->PublishDevices(); + + int32 index = 0; + while (publishList[index] != NULL) { + TRACE("publishing %s\n", publishList[index]); + index++; + } + + return publishList; +} + + +device_hooks * +find_device(const char *name) +{ + static device_hooks hooks = { + usb_hid_open, + usb_hid_close, + usb_hid_free, + usb_hid_control, + usb_hid_read, + usb_hid_write, + NULL, /* select */ + NULL /* deselect */ + }; + + TRACE("find_device(%s)\n", name); + if (gDeviceList->FindDevice(name) == NULL) { + TRACE_ALWAYS("didn't find device %s\n", name); + return NULL; + } + + return &hooks; +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/Driver.h b/src/add-ons/kernel/drivers/input/usb_hid/Driver.h new file mode 100644 index 0000000000..2ca3e9f406 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/Driver.h @@ -0,0 +1,43 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _USB_HID_DRIVER_H_ +#define _USB_HID_DRIVER_H_ + +#include +#include +#include +#include + +#include "DeviceList.h" +#include "kernel_cpp.h" + +#define DRIVER_NAME "usb_hid" + +#define USB_INTERFACE_CLASS_HID 3 +#define USB_INTERFACE_SUBCLASS_HID_BOOT 1 +#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; + +extern "C" { +status_t usb_hid_device_added(usb_device device, void **cookie); +status_t usb_hid_device_removed(void *cookie); + +status_t init_hardware(); +void uninit_driver(); +const char ** publish_devices(); +device_hooks * find_device(const char *name); +} + +#define TRACE(x...) /*dprintf(DRIVER_NAME ": " x)*/ +#define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME ": " x) + +#endif //_USB_HID_DRIVER_H_ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp new file mode 100644 index 0000000000..0fc4587b39 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp @@ -0,0 +1,344 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#include "Driver.h" +#include "HIDDevice.h" +#include +#include +#include + +// 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) + : fStatus(B_NO_INIT), + fDevice(device), + fInterruptPipe(interruptPipe), + fInterfaceIndex(interfaceIndex), + fInstructions(instructions), + fInstructionCount(instructionCount), + fTotalReportSize(totalReportSize), + fTransferUnprocessed(false), + fTransferStatus(B_ERROR), + fTransferActualLength(0), + fTransferBuffer(NULL), + fTransferNotifySem(-1), + fName(NULL), + fParentCookie(-1), + fOpen(false), + fRemoved(false), + fRingBuffer(NULL) +{ + if (ringBufferSize > 0) + fRingBuffer = create_ring_buffer(ringBufferSize); + + fTransferNotifySem = create_sem(0, "hid device transfer notify sem"); + if (fTransferNotifySem < B_OK) { + fStatus = fTransferNotifySem; + return; + } + + 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) +{ + // read HID descriptor + size_t descriptorLength = sizeof(usb_hid_descriptor); + usb_hid_descriptor *hidDescriptor = (usb_hid_descriptor *)malloc(descriptorLength); + if (hidDescriptor == NULL) + return NULL; + + status_t result = gUSBModule->send_request(device, + USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, + USB_REQUEST_GET_DESCRIPTOR, + USB_HID_DESCRIPTOR_HID << 8, interfaceIndex, descriptorLength, + hidDescriptor, &descriptorLength); + + TRACE("get_hid_desc: result: 0x%08lx; length: %lu\n", result, descriptorLength); + if (result == B_OK) + descriptorLength = hidDescriptor->descriptor_info[0].descriptor_length; + else + descriptorLength = 256; /* XXX */ + free(hidDescriptor); + + uint8 *reportDescriptor = (uint8 *)malloc(descriptorLength); + if (reportDescriptor == NULL) + return NULL; + + result = gUSBModule->send_request(device, + USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, + USB_REQUEST_GET_DESCRIPTOR, + USB_HID_DESCRIPTOR_REPORT << 8, interfaceIndex, descriptorLength, + reportDescriptor, &descriptorLength); + + TRACE("get_hid_rep_desc: result: 0x%08lx; length: %lu\n", result, descriptorLength); + if (result != B_OK) { + free(reportDescriptor); + return NULL; + } + +#if 1 + // save report descriptor for troubleshooting + const usb_device_descriptor *deviceDescriptor + = gUSBModule->get_device_descriptor(device); + char outputFile[128]; + sprintf(outputFile, "/tmp/usb_hid_report_descriptor_%04x_%04x.bin", + deviceDescriptor->vendor_id, deviceDescriptor->product_id); + int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) { + write(fd, reportDescriptor, descriptorLength); + close(fd); + } +#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; + 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; + } + + 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; + break; + } + } + + if (interruptPipe == 0) { + TRACE_ALWAYS("didn't find a suitable interrupt pipe\n"); + free(finalInstructions); + return NULL; + } + + // determine device type and create the device object + if (deviceType == USB_HID_DEVICE_TYPE_KEYBOARD) { + return new KeyboardDevice(device, interruptPipe, interfaceIndex, + finalInstructions, instructionCount, totalReportSize); + } else if (deviceType == USB_HID_DEVICE_TYPE_MOUSE) { + return new MouseDevice(device, interruptPipe, interfaceIndex, + finalInstructions, instructionCount, totalReportSize); + } + + TRACE_ALWAYS("unsupported device type 0x%08lx\n", deviceType); + free(finalInstructions); + return NULL; +} + + +void +HIDDevice::SetBaseName(const char *baseName) +{ + // 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; + } + } +} + + +void +HIDDevice::SetParentCookie(int32 cookie) +{ + fParentCookie = cookie; +} + + +status_t +HIDDevice::Open(uint32 flags) +{ + fOpen = true; + return B_OK; +} + + +status_t +HIDDevice::Close() +{ + fOpen = false; + 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() +{ + fRemoved = true; +} + + +void +HIDDevice::_SetTransferProcessed() +{ + fTransferUnprocessed = false; +} + + +bool +HIDDevice::_IsTransferUnprocessed() +{ + return fTransferUnprocessed; +} + + +status_t +HIDDevice::_ScheduleTransfer() +{ + if (fTransferUnprocessed) + return B_BUSY; + + status_t result = gUSBModule->queue_interrupt(fInterruptPipe, + fTransferBuffer, fTotalReportSize, _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) +{ + ring_buffer_write(fRingBuffer, (const uint8 *)buffer, length); + return B_OK; +} + + +void +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); +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h new file mode 100644 index 0000000000..3acb560785 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h @@ -0,0 +1,87 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _USB_HID_DEVICE_H_ +#define _USB_HID_DEVICE_H_ + +#include "hidparse.h" +#include "ring_buffer.h" +#include + +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; }; + + void SetParentCookie(int32 cookie); + int32 ParentCookie() { return fParentCookie; }; + + status_t InitCheck() { return fStatus; }; + +virtual status_t Open(uint32 flags); + bool IsOpen() { return fOpen; }; + +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(); + bool IsRemoved() { return fRemoved; }; + +protected: + void _SetTransferProcessed(); + bool _IsTransferUnprocessed(); + status_t _ScheduleTransfer(); + + int32 _RingBufferReadable(); + status_t _RingBufferRead(void *buffer, size_t length); + status_t _RingBufferWrite(const void *buffer, + size_t length); + + status_t fStatus; + usb_device fDevice; + usb_pipe fInterruptPipe; + size_t fInterfaceIndex; + report_insn * fInstructions; + size_t fInstructionCount; + size_t fTotalReportSize; + + // transfer data + bool fTransferUnprocessed; + status_t fTransferStatus; + size_t fTransferActualLength; + uint8 * fTransferBuffer; + sem_id fTransferNotifySem; + +private: +static void _TransferCallback(void *cookie, + status_t status, void *data, + size_t actualLength); + + char * fName; + int32 fParentCookie; + bool fOpen; + bool fRemoved; + + struct ring_buffer * fRingBuffer; +}; + +#endif // _USB_HID_DEVICE_H_ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/Jamfile b/src/add-ons/kernel/drivers/input/usb_hid/Jamfile index 52a3ad1365..d090d8de8b 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/Jamfile +++ b/src/add-ons/kernel/drivers/input/usb_hid/Jamfile @@ -2,6 +2,8 @@ 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 ; @@ -12,23 +14,19 @@ if ! $(TARGET_PLATFORM_HAIKU_COMPATIBLE) { } KernelAddon usb_hid : - hid.c + DeviceList.cpp + Driver.cpp + HIDDevice.cpp + KeyboardDevice.cpp + MouseDevice.cpp hidparse.c - devlist.c $(buffer_impl) ; SEARCH on [ FGristFiles ring_buffer.cpp ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; -ObjectHdrs [ FGristFiles hid$(SUFOBJ) ] - : [ FDirName $(TARGET_COMMON_DEBUG_OBJECT_DIR) preferences devices ] ; - -Includes [ FGristFiles hid.c ] : - usbdevs.h usbdevs_data.h ; - Package haiku-inputkit-cvs : usb_hid : boot home config add-ons kernel drivers bin ; PackageDriverSymLink haiku-inputkit-cvs : input usb_hid ; - diff --git a/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.cpp b/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.cpp new file mode 100644 index 0000000000..6527bf797e --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.cpp @@ -0,0 +1,348 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + 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 "KeyboardDevice.h" +#include +#include + +// input server private for raw_key_info, KB_READ, etc... +#include "kb_mouse_driver.h" + + +KeyboardDevice::KeyboardDevice(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), + fRepeatDelay(300000), + fRepeatRate(35000), + fLastTransferBuffer(NULL) +{ + fCurrentRepeatDelay = B_INFINITE_TIMEOUT; + fCurrentRepeatKey = 0; + + fLastTransferBuffer = (uint8 *)malloc(totalReportSize); + if (fLastTransferBuffer == NULL) { + fStatus = B_NO_MEMORY; + return; + } + + SetBaseName("input/keyboard/usb/"); +} + + +KeyboardDevice::~KeyboardDevice() +{ + free(fLastTransferBuffer); +} + + +status_t +KeyboardDevice::Control(uint32 op, void *buffer, size_t length) +{ + switch (op) { + case KB_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 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) { + // this case is for handling key repeats, it means + // no interrupt transfer has happened + _WriteKey(fCurrentRepeatKey, true); + // the next timeout is reduced to the repeat_rate + fCurrentRepeatDelay = fRepeatRate; + } else if (result == B_INTERRUPTED) + continue; + else if (result != B_OK) + return result; + } + + // process what is in the ring_buffer, it could be written + // there because we handled an interrupt transfer or because + // we wrote the current repeat key + return _RingBufferRead(buffer, sizeof(raw_key_info)); + + case KB_SET_LEDS: + return _SetLEDs((uint8 *)buffer); + } + + TRACE_ALWAYS("keyboard device unhandled control 0x%08lx\n", op); + return B_ERROR; +} + + +void +KeyboardDevice::_WriteKey(uint32 key, bool down) +{ + raw_key_info info; + info.be_keycode = key; + info.is_keydown = down; + info.timestamp = system_time(); + + _RingBufferWrite(&info, sizeof(raw_key_info)); +} + + +status_t +KeyboardDevice::_SetLEDs(uint8 *data) +{ + uint8 leds = 0; + if (data[0] == 1) + leds |= (1 << 0); + if (data[1] == 1) + leds |= (1 << 1); + if (data[2] == 1) + leds |= (1 << 2); + + 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); +} + + +status_t +KeyboardDevice::_InterpretBuffer() +{ + static uint32 sModifierTable[] = { + KEY_ControlL, + KEY_ShiftL, + KEY_AltL, + KEY_WinL, + KEY_ControlR, + KEY_ShiftR, + KEY_AltR, + KEY_WinR + }; + + static uint32 sKeyTable[] = { + 0x00, // ERROR + 0x00, // ERROR + 0x00, // ERROR + 0x00, // ERROR + 0x3c, // A + 0x50, // B + 0x4e, // C + 0x3e, // D + 0x29, // E + 0x3f, // F + 0x40, // G + 0x41, // H + 0x2e, // I + 0x42, // J + 0x43, // K + 0x44, // L + 0x52, // M + 0x51, // N + 0x2f, // O + 0x30, // P + 0x27, // Q + 0x2a, // R + 0x3d, // S + 0x2b, // T + 0x2d, // U + 0x4f, // V + 0x28, // W + 0x4d, // X + 0x2c, // Y + 0x4c, // Z + 0x12, // 1 + 0x13, // 2 + 0x14, // 3 + 0x15, // 4 + 0x16, // 5 + 0x17, // 6 + 0x18, // 7 + 0x19, // 8 + 0x1a, // 9 + 0x1b, // 0 + 0x47, // enter + 0x01, // Esc + 0x1e, // Backspace + 0x26, // Tab + 0x5e, // Space + 0x1c, // - + 0x1d, // = + 0x31, // [ + 0x32, // ] + 0x00, // unmapped + 0x33, // \ + 0x45, // ; + 0x46, // ' + 0x11, // ` + 0x53, // , + 0x54, // . + 0x55, // / + KEY_CapsLock, // Caps + 0x02, // F1 + 0x03, // F2 + 0x04, // F3 + 0x05, // F4 + 0x06, // F5 + 0x07, // F6 + 0x08, // F7 + 0x09, // F8 + 0x0a, // F9 + 0x0b, // F10 + 0x0c, // F11 + 0x0d, // F12 + 0x0e, // PrintScreen + KEY_Scroll, // Scroll Lock + KEY_Pause, // Pause (0x7f with Ctrl) + 0x1f, // Insert + 0x20, // Home + 0x21, // Page up + 0x34, // Delete + 0x35, // End + 0x36, // Page down + 0x63, // Right arrow + 0x61, // Left arrow + 0x62, // Down arrow + 0x57, // Up arrow + 0x22, // Num Lock + 0x23, // Pad / + 0x24, // Pad * + 0x25, // Pad - + 0x3a, // Pad + + 0x5b, // Pad Enter + 0x58, // Pad 1 + 0x59, // Pad 2 + 0x5a, // Pad 3 + 0x48, // Pad 4 + 0x49, // Pad 5 + 0x4a, // Pad 6 + 0x37, // Pad 7 + 0x38, // Pad 8 + 0x39, // Pad 9 + 0x64, // Pad 0 + 0x65, // Pad . + 0x69, // < + KEY_Menu, // Menu + KEY_Power, // Power + KEY_NumEqual, // Pad = + 0x00, // F13 unmapped + 0x00, // F14 unmapped + 0x00, // F15 unmapped + 0x00, // F16 unmapped + 0x00, // F17 unmapped + 0x00, // F18 unmapped + 0x00, // F19 unmapped + 0x00, // F20 unmapped + 0x00, // F21 unmapped + 0x00, // F22 unmapped + 0x00, // F23 unmapped + 0x00, // F24 unmapped + 0x00, // Execute unmapped + 0x00, // Help unmapped + 0x00, // Menu unmapped + 0x00, // Select unmapped + 0x00, // Stop unmapped + 0x00, // Again unmapped + 0x00, // Undo unmapped + 0x00, // Cut unmapped + 0x00, // Copy unmapped + 0x00, // Paste unmapped + 0x00, // Find unmapped + 0x00, // Mute unmapped + 0x00, // Volume up unmapped + 0x00, // Volume down unmapped + 0x00, // CapsLock unmapped + 0x00, // NumLock unmapped + 0x00, // Scroll lock unmapped + 0x70, // Keypad . on Brazilian ABNT2 + 0x00, // = sign + 0x6b, // Ro (\\ key, japanese) + 0x6e, // Katakana/Hiragana, second key right to spacebar, japanese + 0x6a, // Yen (macron key, japanese) + 0x6d, // Henkan, first key right to spacebar, japanese + 0x6c, // Muhenkan, key left to spacebar, japanese + }; + + static size_t sKeyTableSize = sizeof(sKeyTable) / sizeof(sKeyTable[0]); + + uint8 modifierChange = fLastTransferBuffer[0] ^ fTransferBuffer[0]; + for (uint8 i = 0; modifierChange; i++, modifierChange >>= 1) { + if (modifierChange & 1) + _WriteKey(sModifierTable[i], (fTransferBuffer[0] >> i) & 1); + } + + bool keyDown = false; + uint8 *current = fLastTransferBuffer; + uint8 *compare = fTransferBuffer; + for (int32 twice = 0; twice < 2; twice++) { + for (size_t i = 2; i < fTotalReportSize; i++) { + if (current[i] != 0x00 && current[i] != 0x01) { + bool found = false; + for (size_t j = 2; j < fTotalReportSize; j++) { + if (compare[j] == current[i]) { + found = true; + break; + } + } + + if (found) + continue; + + // a change occured + uint32 key = 0; + if (current[i] < sKeyTableSize) + key = sKeyTable[current[i]]; + + if (key == KEY_Pause && (current[0] & 1)) + key = KEY_Break; + else if (key == 0xe && (current[0] & 1)) + key = KEY_SysRq; +#if HAIKU_TARGET_PLATFORM_HAIKU + else if (keyDown && key == 0x0d) // ToDo: remove again + panic("keyboard requested halt.\n"); +#endif + else if (key == 0) { + // unmapped key + key = 0x200000 + current[i]; + } + + _WriteKey(key, keyDown); + + if (keyDown) { + // repeat handling + fCurrentRepeatKey = key; + fCurrentRepeatDelay = fRepeatDelay; + } else { + // cancel the repeats if they are for this key + if (fCurrentRepeatKey == key) + fCurrentRepeatDelay = B_INFINITE_TIMEOUT; + } + } else + break; + } + + current = fTransferBuffer; + compare = fLastTransferBuffer; + keyDown = true; + } + + memcpy(fLastTransferBuffer, fTransferBuffer, fTotalReportSize); + return B_OK; +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.h b/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.h new file mode 100644 index 0000000000..620f2cf112 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/KeyboardDevice.h @@ -0,0 +1,36 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _USB_KEYBOARD_DEVICE_H_ +#define _USB_KEYBOARD_DEVICE_H_ + +#include "HIDDevice.h" + +class KeyboardDevice : public HIDDevice { +public: + KeyboardDevice(usb_device device, + usb_pipe interruptPipe, + size_t interfaceIndex, + report_insn *instructions, + size_t instructionCount, + size_t totalReportSize); +virtual ~KeyboardDevice(); + +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(); + + bigtime_t fRepeatDelay; + bigtime_t fRepeatRate; + bigtime_t fCurrentRepeatDelay; + uint32 fCurrentRepeatKey; + + uint8 * fLastTransferBuffer; +}; + +#endif // _USB_KEYBOARD_DEVICE_H_ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.cpp b/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.cpp new file mode 100644 index 0000000000..abc9ca1af2 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.cpp @@ -0,0 +1,132 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + 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 +#include + +// input server private for mouse_movement, MS_READ, etc... +#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) +{ + SetBaseName("input/mouse/usb/"); +} + + +status_t +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, 0); + if (result == B_INTERRUPTED) + continue; + else if (result != B_OK) + return result; + + result = _InterpretBuffer(); + _SetTransferProcessed(); + if (result != B_OK) + return result; + } + + return _RingBufferRead(buffer, sizeof(mouse_movement)); + + case MS_NUM_EVENTS: + return _RingBufferReadable() / sizeof(mouse_movement); + + case MS_SET_CLICKSPEED: +#ifdef __HAIKU__ + return user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t)); +#else + fClickSpeed = *(bigtime_t *)buffer; + return B_OK; +#endif + } + + return B_ERROR; +} + + +status_t +MouseDevice::_InterpretBuffer() +{ + 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; + + case USAGE_PAGE_GENERIC_DESKTOP: + if (instruction->is_phy_signed) + value = sign_extend(value, instruction->num_bits); + + switch (instruction->usage_id) { + case USAGE_ID_X: + info.xdelta = value; + break; + + case USAGE_ID_Y: + info.ydelta = -value; + break; + + case USAGE_ID_WHEEL: + info.wheel_ydelta = -value; + break; + } + break; + } + } + + bigtime_t timestamp = system_time(); + if (info.buttons != 0) { + if (fLastButtons == 0) { + if (fLastClickTime + fClickSpeed > timestamp) + fClickCount++; + else + fClickCount = 1; + } + + fLastClickTime = timestamp; + info.clicks = fClickCount; + } + + fLastButtons = info.buttons; + info.timestamp = timestamp; + return _RingBufferWrite(&info, sizeof(mouse_movement)); +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.h b/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.h new file mode 100644 index 0000000000..dff6bf0134 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/MouseDevice.h @@ -0,0 +1,32 @@ +/* + Driver for USB Human Interface Devices. + Copyright (C) 2008 Michael Lotz + Distributed under the terms of the MIT license. +*/ +#ifndef _USB_MOUSE_DEVICE_H_ +#define _USB_MOUSE_DEVICE_H_ + +#include "HIDDevice.h" + +class MouseDevice : public HIDDevice { +public: + MouseDevice(usb_device device, + usb_pipe interruptPipe, + size_t interfaceIndex, + report_insn *instructions, + size_t instructionCount, + size_t totalReportSize); + +virtual status_t Control(uint32 op, void *buffer, size_t length); + +private: + status_t _InterpretBuffer(); + + uint32 fLastButtons; + uint32 fClickCount; + bigtime_t fLastClickTime; + bigtime_t fClickSpeed; + uint32 fMaxButtons; +}; + +#endif // _USB_MOUSE_DEVICE_H_ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/devlist.c b/src/add-ons/kernel/drivers/input/usb_hid/devlist.c deleted file mode 100644 index 86edbcc7e9..0000000000 --- a/src/add-ons/kernel/drivers/input/usb_hid/devlist.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2004-2006, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Jérôme Duval - * - * Some portions of code are copyrighted by - * USB Joystick driver for BeOS R5 - * Copyright 2000 (C) ITO, Takayuki. All rights reserved - */ - - -#include "hid.h" - -#include -#include - - -sem_id gDeviceListLock = -1; -bool gDeviceListChanged = true; /* added or removed */ -/* dynamically generated */ -char **gDeviceNames = NULL; - - -static hid_device_info *sDeviceList = NULL; -static int sDeviceCount = 0; - - -void -add_device_info(hid_device_info *device) -{ - assert(device != NULL); - - acquire_sem(gDeviceListLock); - device->next = sDeviceList; - sDeviceList = device; - sDeviceCount++; - gDeviceListChanged = true; - release_sem(gDeviceListLock); -} - - -void -remove_device_info(hid_device_info *device) -{ - assert(device != NULL); - - acquire_sem(gDeviceListLock); - - if (sDeviceList == device) { - sDeviceList = device->next; - --sDeviceCount; - gDeviceListChanged = true; - } else { - hid_device_info *previous; - for (previous = sDeviceList; previous != NULL; previous = previous->next) { - if (previous->next == device) { - previous->next = device->next; - --sDeviceCount; - gDeviceListChanged = true; - break; - } - } - assert(previous != NULL); - } - release_sem(gDeviceListLock); -} - - -hid_device_info * -search_device_info(const char* name) -{ - hid_device_info *device; - - acquire_sem(gDeviceListLock); - for (device = sDeviceList; device != NULL; device = device->next) { - if (strcmp(device->name, name) == 0) - break; - } - - release_sem(gDeviceListLock); - return device; -} - - -// #pragma mark - device names - - -void -alloc_device_names(void) -{ - assert(gDeviceNames == NULL); - gDeviceNames = malloc(sizeof(char *) * (sDeviceCount + 1)); -} - - -void -free_device_names(void) -{ - if (gDeviceNames != NULL) { - int i; - for (i = 0; gDeviceNames [i] != NULL; i++) { - free(gDeviceNames[i]); - } - - free(gDeviceNames); - gDeviceNames = NULL; - } -} - - -void -rebuild_device_names(void) -{ - int i; - hid_device_info *device; - - assert(gDeviceNames != NULL); - acquire_sem(gDeviceListLock); - for (i = 0, device = sDeviceList; device != NULL; device = device->next) { - gDeviceNames[i++] = strdup(device->name); - DPRINTF_INFO((MY_ID "publishing %s\n", device->name)); - } - gDeviceNames[i] = NULL; - release_sem(gDeviceListLock); -} - diff --git a/src/add-ons/kernel/drivers/input/usb_hid/hid.c b/src/add-ons/kernel/drivers/input/usb_hid/hid.c deleted file mode 100644 index a0db5daced..0000000000 --- a/src/add-ons/kernel/drivers/input/usb_hid/hid.c +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * Copyright 2004-2008, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Jérôme Duval - * Michael Lotz - * Stephan Aßmus - * - * Some portions of code are copyrighted by - * USB Joystick driver for BeOS R5 - * Copyright 2000 (C) ITO, Takayuki. All rights reserved - */ - - -#include "hid.h" -#include "kb_mouse_driver.h" -#include "usbdevs.h" - -#include - -#include -#include -#include - -#define MAX_BUTTONS 16 - -static status_t hid_device_added(const usb_device *dev, void **cookie); -static status_t hid_device_removed(void *cookie); - - -typedef struct driver_cookie { - struct driver_cookie *next; - hid_device_info *device; -} driver_cookie; - -int32 api_version = B_CUR_DRIVER_API_VERSION; - -static usb_notify_hooks sNotifyHooks = { - hid_device_added, hid_device_removed -}; - -#define SUPPORTED_DEVICES 1 -static usb_support_descriptor sSupportedDevices[SUPPORTED_DEVICES] = { - { USB_HID_DEVICE_CLASS, 0, 0, 0, 0 }, -}; - -const uint32 modifier_table[] = { - KEY_ControlL, - KEY_ShiftL, - KEY_AltL, - KEY_WinL, - KEY_ControlR, - KEY_ShiftR, - KEY_AltR, - KEY_WinR -}; - -const uint32 key_table[] = { - 0x00, // ERROR - 0x00, // ERROR - 0x00, // ERROR - 0x00, // ERROR - 0x3c, // A - 0x50, // B - 0x4e, // C - 0x3e, // D - 0x29, // E - 0x3f, // F - 0x40, // G - 0x41, // H - 0x2e, // I - 0x42, // J - 0x43, // K - 0x44, // L - 0x52, // M - 0x51, // N - 0x2f, // O - 0x30, // P - 0x27, // Q - 0x2a, // R - 0x3d, // S - 0x2b, // T - 0x2d, // U - 0x4f, // V - 0x28, // W - 0x4d, // X - 0x2c, // Y - 0x4c, // Z - 0x12, // 1 - 0x13, // 2 - 0x14, // 3 - 0x15, // 4 - 0x16, // 5 - 0x17, // 6 - 0x18, // 7 - 0x19, // 8 - 0x1a, // 9 - 0x1b, // 0 - 0x47, // enter - 0x01, // Esc - 0x1e, // Backspace - 0x26, // Tab - 0x5e, // Space - 0x1c, // - - 0x1d, // = - 0x31, // [ - 0x32, // ] - 0x00, // unmapped - 0x33, // \ - 0x45, // ; - 0x46, // ' - 0x11, // ` - 0x53, // , - 0x54, // . - 0x55, // / - KEY_CapsLock, // Caps - 0x02, // F1 - 0x03, // F2 - 0x04, // F3 - 0x05, // F4 - 0x06, // F5 - 0x07, // F6 - 0x08, // F7 - 0x09, // F8 - 0x0a, // F9 - 0x0b, // F10 - 0x0c, // F11 - 0x0d, // F12 - 0x0e, // PrintScreen - KEY_Scroll, // Scroll Lock - KEY_Pause, // Pause (0x7f with Ctrl) - 0x1f, // Insert - 0x20, // Home - 0x21, // Page up - 0x34, // Delete - 0x35, // End - 0x36, // Page down - 0x63, // Right arrow - 0x61, // Left arrow - 0x62, // Down arrow - 0x57, // Up arrow - 0x22, // Num Lock - 0x23, // Pad / - 0x24, // Pad * - 0x25, // Pad - - 0x3a, // Pad + - 0x5b, // Pad Enter - 0x58, // Pad 1 - 0x59, // Pad 2 - 0x5a, // Pad 3 - 0x48, // Pad 4 - 0x49, // Pad 5 - 0x4a, // Pad 6 - 0x37, // Pad 7 - 0x38, // Pad 8 - 0x39, // Pad 9 - 0x64, // Pad 0 - 0x65, // Pad . - 0x69, // < - KEY_Menu, // Menu - KEY_Power, // Power - KEY_NumEqual, // Pad = - 0x00, // F13 unmapped - 0x00, // F14 unmapped - 0x00, // F15 unmapped - 0x00, // F16 unmapped - 0x00, // F17 unmapped - 0x00, // F18 unmapped - 0x00, // F19 unmapped - 0x00, // F20 unmapped - 0x00, // F21 unmapped - 0x00, // F22 unmapped - 0x00, // F23 unmapped - 0x00, // F24 unmapped - 0x00, // Execute unmapped - 0x00, // Help unmapped - 0x00, // Menu unmapped - 0x00, // Select unmapped - 0x00, // Stop unmapped - 0x00, // Again unmapped - 0x00, // Undo unmapped - 0x00, // Cut unmapped - 0x00, // Copy unmapped - 0x00, // Paste unmapped - 0x00, // Find unmapped - 0x00, // Mute unmapped - 0x00, // Volume up unmapped - 0x00, // Volume down unmapped - 0x00, // CapsLock unmapped - 0x00, // NumLock unmapped - 0x00, // Scroll lock unmapped - 0x70, // Keypad . on Brazilian ABNT2 - 0x00, // = sign - 0x6b, // Ro (\\ key, japanese) - 0x6e, // Katakana/Hiragana, second key right to spacebar, japanese - 0x6a, // Yen (macron key, japanese) - 0x6d, // Henkan, first key right to spacebar, japanese - 0x6c, // Muhenkan, key left to spacebar, japanese - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped - 0x00, // unmapped -}; - -usb_module_info *usb; - -static const char *kDriverName = DRIVER_NAME; - -static int sKeyboardDeviceNumber = 0; -static int sMouseDeviceNumber = 0; -static const char *sKeyboardBaseName = "input/keyboard/usb/"; -static const char *sMouseBaseName = "input/mouse/usb/"; - - -// #pragma mark - support functions - - -hid_device_info * -create_device(const usb_device *dev, const usb_interface_info *ii, - uint16 ifno, bool isKeyboard) -{ - hid_device_info *device = NULL; - int number; - area_id area; - sem_id sem; - char area_name[32]; - const char *base_name; - - assert (usb != NULL && dev != NULL); - - if (isKeyboard) { - number = sKeyboardDeviceNumber++; - base_name = sKeyboardBaseName; - } else { - number = sMouseDeviceNumber++; - base_name = sMouseBaseName; - } - - device = malloc(sizeof(hid_device_info)); - if (device == NULL) - return NULL; - - device->sem_cb = sem = create_sem(0, DRIVER_NAME "_cb"); - if (sem < B_OK) { - DPRINTF_ERR((MY_ID "create_sem() failed %d\n", (int)sem)); - free(device); - return NULL; - } - - device->sem_lock = sem = create_sem(1, DRIVER_NAME "_lock"); - if (sem < B_OK) { - DPRINTF_ERR((MY_ID "create_sem() failed %d\n", (int)sem)); - delete_sem(device->sem_cb); - free(device); - return NULL; - } - - sprintf(area_name, DRIVER_NAME "_buffer%d", number); - device->buffer_area = area = create_area(area_name, - (void **) &device->buffer, B_ANY_KERNEL_ADDRESS, - B_PAGE_SIZE, B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); - if (area < B_OK) { - DPRINTF_ERR((MY_ID "create_area() failed %d\n", (int)area)); - delete_sem(device->sem_cb); - delete_sem(device->sem_lock); - free(device); - return NULL; - } - - sprintf(device->name, "%s%d", base_name, number); - device->dev = dev; - device->ifno = ifno; - device->open = 0; - device->open_fds = NULL; - device->active = true; - device->transfer_scheduled = false; - device->insns = NULL; - device->num_insns = 0; - device->flags = 0; - device->rbuf = create_ring_buffer(384); - device->is_keyboard = isKeyboard; - - // double click handling - device->click_count = 1; - device->click_speed = 250000; - device->last_click_time = 0; - device->last_buttons = 0; - - device->repeat_timer.current_delay = B_INFINITE_TIMEOUT; - device->repeat_timer.key = 0; - - // default values taken from the PS/2 driver - device->repeat_rate = 35000; - device->repeat_delay = 300000; - - return device; -} - - -void -delete_device(hid_device_info *device) -{ - assert(device != NULL); - - if (device->rbuf != NULL) { - delete_ring_buffer(device->rbuf); - device->rbuf = NULL; - } - - delete_area(device->buffer_area); - delete_sem(device->sem_cb); - delete_sem(device->sem_lock); - free(device); -} - - -static void -write_key(hid_device_info *device, uint32 key, bool down) -{ - raw_key_info raw; - raw.be_keycode = key; - raw.is_keydown = down; - raw.timestamp = system_time(); - - ring_buffer_write(device->rbuf, (const uint8*)&raw, sizeof(raw_key_info)); -} - - -// here we don't need to follow a report descriptor for typical keyboards -// TODO : but we should for keypads for example because they aren't boot keyboard devices ! -// see hidparse.c -static void -interpret_kb_buffer(hid_device_info *device) -{ - uint8 modifiers = ((uint8*)device->buffer)[0]; - uint8 bits = device->last_buffer[0] ^ modifiers; - uint32 i, j; - - if (bits) { - for (j = 0; bits; j++, bits >>= 1) { - if (bits & 1) - write_key(device, modifier_table[j], (modifiers >> j) & 1); - } - } - - // key down - - for (i = 2; i < device->total_report_size; i++) { - if (((uint8*)device->buffer)[i] && ((uint8*)device->last_buffer)[i] != 0x1) { - bool found = false; - for (j = 2; j < device->total_report_size; j++) { - if (((uint8*)device->last_buffer)[j] - && ((uint8*)device->last_buffer)[j] == ((uint8*)device->buffer)[i]) { - found = true; - break; - } - } - - if (!found) { - uint32 key = key_table[((uint8*)device->buffer)[i]]; - if (key == KEY_Pause && modifiers & 1) - key = KEY_Break; - else if (key == 0xe && modifiers & 1) - key = KEY_SysRq; -#if HAIKU_TARGET_PLATFORM_HAIKU - else if (key == 0x0d) // ToDo: remove again - panic("keyboard requested halt.\n"); -#endif - else if (key == 0) { - // unmapped key - key = 0x200000 + ((uint8*)device->buffer)[i]; - } - - write_key(device, key, true); - - // repeat handling - device->repeat_timer.key = key; - device->repeat_timer.current_delay = device->repeat_delay; - } - } else - break; - } - - // key up - // TODO: merge this... - - for (i = 2; i < device->total_report_size; i++) { - if (((uint8*)device->last_buffer)[i] && ((uint8*)device->last_buffer)[i] != 0x1) { - bool found = false; - for (j = 2; j < device->total_report_size; j++) { - if (((uint8*)device->buffer)[j] - && ((uint8*)device->buffer)[j] == ((uint8*)device->last_buffer)[i]) { - found = true; - break; - } - } - - if (!found) { - uint32 key = key_table[((uint8*)device->last_buffer)[i]]; - if (key == KEY_Pause && modifiers & 1) - key = KEY_Break; - else if (key == 0xe && modifiers & 1) - key = KEY_SysRq; - else if (key == 0) { - // unmapped key - key = 0x200000 + ((uint8*)device->last_buffer)[i]; - } - - write_key(device, key, false); - - // cancel the repeats if they are for this key - if (device->repeat_timer.key == key) - device->repeat_timer.current_delay = B_INFINITE_TIMEOUT; - } - } else - break; - } -} - - -// TODO : here we don't follow a report descriptor but we actually should -// see hidparse.c -static void -set_leds(hid_device_info *device, uint8* data) -{ - status_t status; - size_t actual = 1; - uint8 leds = 0; - int report_id = 0; - if (data[0] == 1) - leds |= (1 << 0); - if (data[1] == 1) - leds |= (1 << 1); - if (data[2] == 1) - leds |= (1 << 2); - - status = usb->send_request (device->dev, - USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, - USB_REQUEST_HID_SET_REPORT, - 0x200 | report_id, device->ifno, actual, - &leds, actual, &actual); - DPRINTF_INFO((MY_ID "set_leds: leds=0x%02x, status=%d, len=%d\n", - leds, (int) status, (int)actual)); -} - - -static int -sign_extend(int value, int size) -{ - if (value & (1 << (size - 1))) - return value | (UINT_MAX << size); - - return value; -} - - -static void -interpret_mouse_buffer(hid_device_info *device) -{ - mouse_movement info; - uint8 *report = (uint8*)device->buffer; - uint32 i; - - memset(&info, 0, sizeof(info)); - for (i = 0; i < device->num_insns; i++) { - const report_insn *insn = &device->insns[i]; - int32 value = (((report[insn->byte_idx + 1] << 8) - | report[insn->byte_idx]) >> insn->bit_pos) - & ((1 << insn->num_bits) - 1); - - if (insn->usage_page == USAGE_PAGE_BUTTON) { - if ((insn->usage_id - 1) < MAX_BUTTONS) { - info.buttons |= (value & 1) << (insn->usage_id - 1); - } - } else if (insn->usage_page == USAGE_PAGE_GENERIC_DESKTOP) { - if (insn->is_phy_signed) - value = sign_extend(value, insn->num_bits); - - switch (insn->usage_id) { - case USAGE_ID_X: - info.xdelta = value; - break; - case USAGE_ID_Y: - info.ydelta = -value; - break; - case USAGE_ID_WHEEL: - info.wheel_ydelta = -value; - break; - } - } - } - - if (info.buttons != 0) { - if (device->last_buttons == 0) { - if (device->last_click_time + device->click_speed > device->timestamp) - device->click_count++; - else - device->click_count = 1; - } - - device->last_click_time = device->timestamp; - info.clicks = device->click_count; - } - - device->last_buttons = info.buttons; - - info.timestamp = device->timestamp; - ring_buffer_write(device->rbuf, (const uint8*)&info, sizeof(info)); -} - -// #pragma mark - interrupt transfers - - -/*! - callback: got a report, unblock input server thread in hid_device_control() -*/ -static void -usb_callback(void *cookie, status_t busStatus, - void *data, size_t actualLength) -{ - hid_device_info *device = cookie; - - device->actual_length = actualLength; - device->bus_status = busStatus; /* B_USB_STATUS_* */ - - // release the notification semaphore so the input_server - // thread which is blocking in hid_device_control can continue - // to run (see you in handle_interrupt_transfer()) - release_sem_etc(device->sem_cb, 1, B_DO_NOT_RESCHEDULE); -} - - -static status_t -schedule_interrupt_transfer(hid_device_info* device) -{ - status_t status = usb->queue_interrupt(device->ept->handle, device->buffer, - device->total_report_size, usb_callback, device); - if (status != B_OK) { - /* XXX probably endpoint stall */ - DPRINTF_ERR((MY_ID "queue_interrupt() error %d\n", (int)status)); - } - return status; -} - - -static status_t -handle_interrupt_transfer(hid_device_info* device) -{ - status_t status = device->bus_status; - - if (status != B_OK) { - /* request failed */ - DPRINTF_ERR((MY_ID "bus status %d\n", (int)device->bus_status)); - if (status == B_CANCELED) { - /* cancelled: device is unplugged */ - device->active = false; - return status; - } - - status = usb->clear_feature(device->ept->handle, USB_FEATURE_ENDPOINT_HALT); - if (status != B_OK) { - DPRINTF_ERR((MY_ID "clear_feature() error %d\n", (int)status)); - // probably the device was removed and we just didn't get the - // removed notification yet. - device->active = false; - return status; - } - - return device->bus_status; - } - - /* got a report */ -#if 0 - uint32 i; - char linbuf [256]; - uint8 *buffer = device->buffer; - - for (i = 0; i < device->total_report_size; i++) - sprintf (&linbuf[i*3], "%02X ", buffer [i]); - DPRINTF_INFO((MY_ID "input report: %s\n", linbuf)); -#endif - device->timestamp = system_time(); - - if (device->is_keyboard) { - interpret_kb_buffer(device); - memcpy(device->last_buffer, device->buffer, device->total_report_size); - } else - interpret_mouse_buffer(device); - - return B_OK; -} - - -// #pragma mark - device hooks - - -static status_t -hid_device_added(const usb_device *dev, void **cookie) -{ - hid_device_info *device; - const usb_device_descriptor *dev_desc; - const usb_configuration_info *conf; - const usb_interface_info *intf; - status_t status; - usb_hid_descriptor *hid_desc; - uint8 *rep_desc = NULL; - size_t desc_len; - decomp_item *items; - size_t num_items; - int fd, report_id; - uint16 ifno; - bool is_keyboard; - - assert(dev != NULL && cookie != NULL); - DPRINTF_INFO((MY_ID "device_added()\n")); - - dev_desc = usb->get_device_descriptor(dev); - - DPRINTF_INFO((MY_ID "vendor ID 0x%04X, product ID 0x%04X\n", - dev_desc->vendor_id, dev_desc->product_id)); - - if (dev_desc->vendor_id == USB_VENDOR_WACOM) - return B_ERROR; - - /* check interface class */ - - if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION)) == NULL) { - DPRINTF_ERR((MY_ID "cannot get default configuration\n")); - return B_ERROR; - } - - for (ifno = 0; ifno < conf->interface_count; ifno++) { - /* This is C; I can use "class" :-> */ - int class, subclass, protocol; - - intf = conf->interface[ifno].active; - class = intf->descr->interface_class; - subclass = intf->descr->interface_subclass; - protocol = intf->descr->interface_protocol; - DPRINTF_INFO((MY_ID "interface %d: class %d, subclass %d, protocol %d\n", - ifno, class, subclass, protocol)); - if (class == USB_HID_DEVICE_CLASS - && subclass == USB_HID_INTERFACE_BOOT_SUBCLASS) - break; - } - - if (ifno >= conf->interface_count) { - DPRINTF_INFO((MY_ID "Boot HID interface not found\n")); - return B_ERROR; - } - - /* read HID descriptor */ - - desc_len = sizeof(usb_hid_descriptor); - hid_desc = malloc(desc_len); - if (hid_desc == NULL) - return B_NO_MEMORY; - - status = usb->send_request(dev, - USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, - USB_REQUEST_GET_DESCRIPTOR, - USB_HID_DESCRIPTOR_HID << 8, ifno, desc_len, - hid_desc, desc_len, &desc_len); - DPRINTF_INFO((MY_ID "get_hid_desc: status=%d, len=%d\n", - (int)status, (int)desc_len)); - if (status != B_OK) - desc_len = 256; /* XXX */ - - /* read report descriptor */ - - desc_len = hid_desc->descriptor_info[0].descriptor_length; - free(hid_desc); - - rep_desc = malloc(desc_len); - if (rep_desc == NULL) - return B_NO_MEMORY; - - status = usb->send_request(dev, - USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD, - USB_REQUEST_GET_DESCRIPTOR, - USB_HID_DESCRIPTOR_REPORT << 8, ifno, desc_len, - rep_desc, desc_len, &desc_len); - DPRINTF_INFO((MY_ID "get_hid_rep_desc: status=%d, len=%d\n", - (int) status, (int)desc_len)); - if (status != B_OK) { - free(rep_desc); - return B_ERROR; - } - - /* save report descriptor for troubleshooting */ - - fd = open ("/tmp/rep_desc.bin", O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd >= 0) { - write(fd, rep_desc, desc_len); - close(fd); - } - - /* Generic Desktop : Keyboard or Mouse */ - - if (memcmp(rep_desc, "\x05\x01\x09\x06", 4) != 0 && - memcmp(rep_desc, "\x05\x01\x09\x02", 4) != 0) { - DPRINTF_INFO((MY_ID "not a keyboard or a mouse %08lx\n", *(uint32*)rep_desc)); - free(rep_desc); - return B_ERROR; - } - - /* configuration */ - - if ((status = usb->set_configuration (dev, conf)) != B_OK) { - DPRINTF_ERR((MY_ID "set_configuration() failed %d\n", (int)status)); - free(rep_desc); - return B_ERROR; - } - - is_keyboard = memcmp(rep_desc, "\x05\x01\x09\x06", 4) == 0; - - if ((device = create_device(dev, intf, ifno, is_keyboard)) == NULL) { - free(rep_desc); - return B_ERROR; - } - - /* decompose report descriptor */ - - num_items = desc_len; /* XXX */ - items = malloc(sizeof(decomp_item) * num_items); - if (items == NULL) { - delete_device(device); - free(rep_desc); - return B_NO_MEMORY; - } - - decompose_report_descriptor(rep_desc, desc_len, items, &num_items); - free(rep_desc); - - /* parse report descriptor */ - - device->num_insns = num_items; /* XXX */ - device->insns = malloc(sizeof (report_insn) * device->num_insns); - if (device->insns == NULL) { - delete_device(device); - free(items); - return B_NO_MEMORY; - } - - parse_report_descriptor(items, num_items, device->insns, - &device->num_insns, &device->total_report_size, &report_id); - free(items); - - device->insns = realloc(device->insns, sizeof (report_insn) - * device->num_insns); - if (device->insns == NULL) { - delete_device(device); - return B_NO_MEMORY; - } - - DPRINTF_INFO((MY_ID "%d items, %d insns, %d bytes\n", - (int)num_items, (int)device->num_insns, (int)device->total_report_size)); - - /* count axes, hats and buttons */ - - /*count_controls (device->insns, device->num_insns, - &device->num_axes, &device->num_hats, &device->num_buttons); - DPRINTF_INFO((MY_ID "%d axes, %d hats, %d buttons\n", - device->num_axes, device->num_hats, device->num_buttons));*/ - - /* get initial state */ - -#if 0 - /* ToDo: find out why some mice hang here */ - status = usb->send_request(dev, - USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, - USB_REQUEST_HID_GET_REPORT, - 0x0100 | report_id, ifno, device->total_report_size, - device->buffer, device->total_report_size, &actual); - if (status != B_OK) - DPRINTF_ERR((MY_ID "Get_Report failed %d\n", (int)status)); -#endif - device->timestamp = system_time (); - - DPRINTF_INFO((MY_ID "%08lx %08lx %08lx\n", *(((uint32*)device->buffer)), *(((uint32*)device->buffer)+1), *(((uint32*)device->buffer)+2))); - - device->ept = &intf->endpoint[0]; /* interrupt IN */ - - /* create a port */ - - add_device_info(device); - - *cookie = device; - DPRINTF_INFO((MY_ID "added %s\n", device->name)); - return B_OK; -} - - -static status_t -hid_device_removed(void *cookie) -{ - hid_device_info *device = cookie; - - assert(cookie != NULL); - - DPRINTF_INFO((MY_ID "device_removed(%s)\n", device->name)); - - usb->cancel_queued_transfers(device->ept->handle); - remove_device_info(device); - - if (device->open == 0) { - if (device->insns != NULL) - free(device->insns); - delete_device(device); - } else { - // If the input_server has opened us this will always be the case. - // We unpublish our node in the devfs which will notify the mouse - // add-on to unregister and release us. - DPRINTF_INFO((MY_ID "%s still open\n", device->name)); - device->active = false; - } - - return B_OK; -} - - -static status_t -hid_device_open(const char *name, uint32 flags, - driver_cookie **out_cookie) -{ - driver_cookie *cookie; - hid_device_info *device; - - assert (name != NULL); - assert (out_cookie != NULL); - DPRINTF_INFO((MY_ID "open(%s)\n", name)); - - if ((device = search_device_info (name)) == NULL) - return B_ENTRY_NOT_FOUND; - if ((cookie = malloc (sizeof (driver_cookie))) == NULL) - return B_NO_MEMORY; - - acquire_sem(device->sem_lock); - cookie->device = device; - cookie->next = device->open_fds; - device->open_fds = cookie; - device->open++; - release_sem(device->sem_lock); - - *out_cookie = cookie; - DPRINTF_INFO((MY_ID "device %s open (%d)\n", name, device->open)); - return B_OK; -} - - -static status_t -hid_device_read(driver_cookie *cookie, off_t position, - void *buf, size_t *num_bytes) -{ - return B_ERROR; -} - - -static status_t -hid_device_write(driver_cookie *cookie, off_t position, - const void *buf, size_t *num_bytes) -{ - return B_ERROR; -} - - -static status_t -hid_device_control(driver_cookie *cookie, uint32 op, void *arg, size_t length) -{ - status_t err = B_ERROR; - hid_device_info *device; - - assert (cookie != NULL); - device = cookie->device; - assert (device != NULL); - DPRINTF_INFO((MY_ID "ioctl(0x%x)\n", (int)op)); - - if (!device->active) - return B_ERROR; /* already unplugged */ - - if (device->is_keyboard) { - switch (op) { - case KB_READ: - while (ring_buffer_readable(device->rbuf) == 0) { - if (!device->transfer_scheduled) { - err = schedule_interrupt_transfer(device); - if (err != B_OK) - return err; - device->transfer_scheduled = true; - // NOTE: this thread is now blocking until - // the semaphore will be released from the - // call_back function - } - - err = acquire_sem_etc(device->sem_cb, 1, - B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, - device->repeat_timer.current_delay); - if (err == B_TIMED_OUT) { - // this case is for handling key repeats, it means - // no interrupt transfer has happened - write_key(device, device->repeat_timer.key, true); - // the next timeout is reduced to the repeat_rate - device->repeat_timer.current_delay = device->repeat_rate; - } else if (err == B_OK) { - // this case is for when an actual interrupt transfer - // happened, it is the only possible reason to be here - device->transfer_scheduled = false; - err = handle_interrupt_transfer(device); - if (err != B_OK) - return err; - } else if (err == B_INTERRUPTED) { - continue; - } else { - return err; - } - } - - // 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 - ring_buffer_user_read(device->rbuf, arg, sizeof(raw_key_info)); - return B_OK; - - case KB_SET_LEDS: - set_leds(device, (uint8 *)arg); - return B_OK; - - default: - /* not implemented */ - return B_ERROR; - } - } else { - switch (op) { - case MS_READ: - while (ring_buffer_readable(device->rbuf) == 0) { - if (!device->transfer_scheduled) { - err = schedule_interrupt_transfer(device); - if (err != B_OK) - return err; - device->transfer_scheduled = true; - // NOTE: this thread is now blocking until - // the semaphore will be released from the - // call_back function - } - - err = acquire_sem_etc(device->sem_cb, 1, B_CAN_INTERRUPT, 0LL); - if (err == B_INTERRUPTED) - continue; - else if (err != B_OK) - return err; - - device->transfer_scheduled = false; - err = handle_interrupt_transfer(device); - if (err != B_OK) - return err; - } - - ring_buffer_user_read(device->rbuf, arg, sizeof(mouse_movement)); - return B_OK; - - case MS_NUM_EVENTS: - return (int32)(ring_buffer_readable(device->rbuf) - / sizeof(mouse_movement)); - - case MS_SET_CLICKSPEED: -#ifdef __HAIKU__ - return user_memcpy(&device->click_speed, arg, sizeof(bigtime_t)); -#else - device->click_speed = *(bigtime_t *)arg; - return B_OK; -#endif - - default: - /* not implemented */ - return B_ERROR; - } - } - - /* shouldn't get here */ - return B_ERROR; -} - - -static status_t -hid_device_close(driver_cookie *cookie) -{ - hid_device_info *device; - - assert (cookie != NULL && cookie->device != NULL); - device = cookie->device; - DPRINTF_INFO((MY_ID "close(%s)\n", device->name)); - - /* detach the cookie from list */ - - acquire_sem(device->sem_lock); - if (device->open_fds == cookie) - device->open_fds = cookie->next; - else { - driver_cookie *p; - for (p = device->open_fds; p != NULL; p = p->next) { - if (p->next == cookie) { - p->next = cookie->next; - break; - } - } - } - --device->open; - release_sem(device->sem_lock); - - return B_OK; -} - - -static status_t -hid_device_free(driver_cookie *cookie) -{ - hid_device_info *device; - - assert(cookie != NULL && cookie->device != NULL); - device = cookie->device; - DPRINTF_INFO((MY_ID "free(%s)\n", device->name)); - - free(cookie); - if (device->open > 0) - DPRINTF_INFO((MY_ID "%d opens left\n", device->open)); - else if (!device->active) { - DPRINTF_INFO((MY_ID "removed %s\n", device->name)); - if (device->insns != NULL) - free(device->insns); - delete_device(device); - } - - return B_OK; -} - - -// #pragma mark - driver API - - -status_t -init_hardware(void) -{ - DPRINTF_INFO((MY_ID "init_hardware() " __DATE__ " " __TIME__ "\n")); - return B_OK; -} - - -status_t -init_driver(void) -{ - DPRINTF_INFO((MY_ID "init_driver() " __DATE__ " " __TIME__ "\n")); - - if (get_module(B_USB_MODULE_NAME, (module_info **)&usb) != B_OK) - return B_ERROR; - - gDeviceListLock = create_sem(1, "dev_list_lock"); - if (gDeviceListLock < B_OK) { - put_module(B_USB_MODULE_NAME); - return gDeviceListLock; - } - - usb->register_driver(kDriverName, sSupportedDevices, - SUPPORTED_DEVICES, NULL); - usb->install_notify(kDriverName, &sNotifyHooks); - DPRINTF_INFO((MY_ID "init_driver() OK\n")); - - return B_OK; -} - - -void -uninit_driver(void) -{ - DPRINTF_INFO((MY_ID "uninit_driver()\n")); - usb->uninstall_notify(kDriverName); - - delete_sem(gDeviceListLock); - put_module(B_USB_MODULE_NAME); - free_device_names(); -} - - -/*! - device names are generated dynamically -*/ -const char ** -publish_devices(void) -{ - DPRINTF_INFO((MY_ID "publish_devices()\n")); - - if (gDeviceListChanged) { - free_device_names(); - alloc_device_names(); - if (gDeviceNames != NULL) - rebuild_device_names(); - gDeviceListChanged = false; - } - return (const char **)gDeviceNames; -} - - -device_hooks * -find_device(const char *name) -{ - static device_hooks hooks = { - (device_open_hook)hid_device_open, - (device_close_hook)hid_device_close, - (device_free_hook)hid_device_free, - (device_control_hook)hid_device_control, - (device_read_hook)hid_device_read, - (device_write_hook)hid_device_write, - NULL - }; - - assert(name != NULL); - DPRINTF_INFO((MY_ID "find_device(%s)\n", name)); - - if (search_device_info(name) == NULL) - return NULL; - - return &hooks; -} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/hid.h b/src/add-ons/kernel/drivers/input/usb_hid/hid.h deleted file mode 100644 index 067e40c557..0000000000 --- a/src/add-ons/kernel/drivers/input/usb_hid/hid.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2004-2007, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Jérôme Duval - * - * Some portions of code are copyrighted by - * USB Joystick driver for BeOS R5 - * Copyright 2000 (C) ITO, Takayuki. All rights reserved - */ -#ifndef _HID_H_ -#define _HID_H_ - -#include "hidparse.h" -#include "ring_buffer.h" - -#include -#include -#include - -#if DEBUG - #define DPRINTF_INFO(x) dprintf x - #define DPRINTF_ERR(x) dprintf x -#else - #define DPRINTF_INFO(x) - #define DPRINTF_ERR(x) dprintf x -#endif - -/* driver specific definitions */ - -#define DRIVER_NAME "usb_hid" - -#define MY_ID "\033[34m" DRIVER_NAME ":\033[m " -#define MY_ERR "\033[31merror:\033[m " -#define MY_WARN "\033[31mwarning:\033[m " -#define assert(x) \ - ((x) ? 0 : dprintf (MY_ID "assertion failed at " __FILE__ ", line %d\n", __LINE__)) - -/* 0-origin */ -#define DEFAULT_CONFIGURATION 0 - -#define BUF_SIZ B_PAGE_SIZE - -struct driver_cookie; - -struct hid_repeat_timer { - bigtime_t current_delay; - uint32 key; -}; - -typedef struct hid_device_info { - /* list structure */ - struct hid_device_info *next; - - /* maintain device */ - sem_id sem_cb; - sem_id sem_lock; - area_id buffer_area; - void *buffer; - - uint8 last_buffer[32]; - const usb_device *dev; - uint16 ifno; - char name[30]; - - struct ring_buffer *rbuf; - - bool active; - int open; - struct driver_cookie *open_fds; - - /* workarea for transfer */ - int usbd_status, bus_status, cmd_status; - int actual_length; - const usb_endpoint_info *ept; - bool transfer_scheduled; - - report_insn *insns; - size_t num_insns; - size_t total_report_size; - int num_buttons, num_axes, num_hats; - bigtime_t timestamp; - uint flags; - bool is_keyboard; - - // double click (please move this into the input_server one day) - uint32 last_buttons; - uint32 click_count; - bigtime_t click_speed; - bigtime_t last_click_time; - - // key repeats - struct hid_repeat_timer repeat_timer; - bigtime_t repeat_delay; - bigtime_t repeat_rate; -} hid_device_info; - -/* hid.c */ - -extern usb_module_info* usb; -extern const char* my_driver_name; -extern const char* keyboard_base_name; -extern const char* mouse_base_name; - -hid_device_info* create_device(const usb_device *dev, - const usb_interface_info *ii, uint16 ifno, bool is_keyboard); -void delete_device(hid_device_info *device); - -/* devlist.c */ - -extern sem_id gDeviceListLock; -extern bool gDeviceListChanged; - -void add_device_info(hid_device_info *device); -void remove_device_info(hid_device_info *device); -hid_device_info *search_device_info(const char *name); - -extern char **gDeviceNames; - -void alloc_device_names(void); -void free_device_names(void); -void rebuild_device_names(void); - -#endif // _HID_H_ diff --git a/src/add-ons/kernel/drivers/input/usb_hid/hidparse.c b/src/add-ons/kernel/drivers/input/usb_hid/hidparse.c index a0d123b211..663bb28aa6 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/hidparse.c +++ b/src/add-ons/kernel/drivers/input/usb_hid/hidparse.c @@ -41,6 +41,16 @@ 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) diff --git a/src/add-ons/kernel/drivers/input/usb_hid/hidparse.h b/src/add-ons/kernel/drivers/input/usb_hid/hidparse.h index d4548f3f46..294277ebf3 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/hidparse.h +++ b/src/add-ons/kernel/drivers/input/usb_hid/hidparse.h @@ -149,6 +149,12 @@ typedef struct } 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, @@ -162,3 +168,7 @@ int parse_report_descriptor size_t *num_insns, size_t *total_report_size, int *first_report_id); + +#if defined(__cplusplus) +} +#endif diff --git a/src/add-ons/kernel/drivers/input/usb_hid/kernel_cpp.h b/src/add-ons/kernel/drivers/input/usb_hid/kernel_cpp.h new file mode 100644 index 0000000000..7d7b025872 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/kernel_cpp.h @@ -0,0 +1,45 @@ +#ifndef _KERNEL_CPP_H_ +#define _KERNEL_CPP_H_ + +#include + +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_