diff --git a/src/add-ons/kernel/drivers/bus/Jamfile b/src/add-ons/kernel/drivers/bus/Jamfile index 817b78c74d..8a9c4e591a 100644 --- a/src/add-ons/kernel/drivers/bus/Jamfile +++ b/src/add-ons/kernel/drivers/bus/Jamfile @@ -2,3 +2,4 @@ SubDir HAIKU_TOP src add-ons kernel drivers bus ; SubInclude HAIKU_TOP src add-ons kernel drivers bus pcmcia ; SubInclude HAIKU_TOP src add-ons kernel drivers bus scsi ; +SubInclude HAIKU_TOP src add-ons kernel drivers bus usb ; diff --git a/src/add-ons/kernel/drivers/bus/usb/Jamfile b/src/add-ons/kernel/drivers/bus/usb/Jamfile new file mode 100644 index 0000000000..9775542ea1 --- /dev/null +++ b/src/add-ons/kernel/drivers/bus/usb/Jamfile @@ -0,0 +1,5 @@ +SubDir HAIKU_TOP src add-ons kernel drivers bus usb ; + +KernelAddon usb_raw : kernel drivers bus usb : + usb_raw.cpp + ; diff --git a/src/add-ons/kernel/drivers/bus/usb/usb_raw.cpp b/src/add-ons/kernel/drivers/bus/usb/usb_raw.cpp new file mode 100644 index 0000000000..3e44b11bfb --- /dev/null +++ b/src/add-ons/kernel/drivers/bus/usb/usb_raw.cpp @@ -0,0 +1,661 @@ +/* + * Copyright 2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include +#include +#include "usb_raw.h" + + +#define TRACE_USB_RAW +#ifdef TRACE_USB_RAW +#define TRACE(x) dprintf x +#else +#define TRACE(x) /* nothing */ +#endif + + +#define DRIVER_NAME "usb_raw" +#define DEVICE_NAME "bus/usb/raw" +#define DRIVER_VERSION 0x0015 + +int32 api_version = B_CUR_DRIVER_API_VERSION; +usb_module_info *gUSBModule = NULL; +raw_device *gDeviceList = NULL; +uint32 gDeviceCount = 0; +benaphore gDeviceListLock; + +// ToDo: replace with proper device name getter (ioctl to bus manager) +uint8 gDeviceLevel; +uint8 gDeviceCounters[7]; + +static status_t +usb_raw_device_added(const usb_device *newDevice, void **cookie) +{ + TRACE((DRIVER_NAME": device_added()\n")); + raw_device *device = (raw_device *)malloc(sizeof(raw_device)); + + status_t result = benaphore_init(&device->lock, "usb_raw device lock"); + if (result < B_OK) { + free(device); + return result; + } + + device->notify = create_sem(0, "usb_raw callback notify"); + if (device->notify < B_OK) { + benaphore_destroy(&device->lock); + free(device); + return B_NO_MORE_SEMS; + } + + // ToDo: replace with proper device name getter (ioctl to bus manager) + char devicePathString[32]; + char *devicePath = &devicePathString[0]; + memset(devicePath, 0, sizeof(devicePath)); + + const usb_device_descriptor *deviceDescriptor = + gUSBModule->get_device_descriptor(newDevice); + if (deviceDescriptor->device_class == 0x09 + && deviceDescriptor->vendor_id == 0x0000 + && deviceDescriptor->product_id == 0x000) { + // root hub + memset(gDeviceCounters + 1, 0, sizeof(gDeviceCounters) - 1); + gDeviceLevel = 0; + } + + for (uint8 i = 0; i <= gDeviceLevel; i++) + devicePath += sprintf(devicePath, "/%d", gDeviceCounters[i]); + + gDeviceCounters[gDeviceLevel]++; + if (deviceDescriptor->device_class == 0x09) { + sprintf(device->name, "bus/usb%s/hub", devicePathString); + gDeviceLevel++; + } else { + sprintf(device->name, "bus/usb%s", devicePathString); + } + + device->device = newDevice; + + benaphore_lock(&gDeviceListLock); + device->link = (void *)gDeviceList; + gDeviceList = device; + gDeviceCount++; + benaphore_unlock(&gDeviceListLock); + + *cookie = (void *)device; + return B_OK; +} + + +static status_t +usb_raw_device_removed(void *cookie) +{ + TRACE((DRIVER_NAME": device_removed()\n")); + raw_device *device = (raw_device *)cookie; + + benaphore_lock(&gDeviceListLock); + if (gDeviceList == device) { + gDeviceList = (raw_device *)device->link; + } else { + raw_device *element = gDeviceList; + while (element) { + if (element->link == device) { + element->link = device->link; + break; + } + + element = (raw_device *)element->link; + } + } + gDeviceCount--; + benaphore_unlock(&gDeviceListLock); + + benaphore_lock(&device->lock); + benaphore_destroy(&device->lock); + delete_sem(device->notify); + free(device); + return B_OK; +} + + +// +//#pragma mark - +// + + +static status_t +usb_raw_open(const char *name, uint32 flags, void **cookie) +{ + TRACE((DRIVER_NAME": open()\n")); + benaphore_lock(&gDeviceListLock); + raw_device *element = gDeviceList; + while (element) { + if (strcmp(name, element->name) == 0) { + *cookie = element; + benaphore_unlock(&gDeviceListLock); + return B_OK; + } + + element = (raw_device *)element->link; + } + + benaphore_unlock(&gDeviceListLock); + return B_NAME_NOT_FOUND; +} + + +static status_t +usb_raw_close(void *cookie) +{ + TRACE((DRIVER_NAME": close()\n")); + return B_OK; +} + + +static status_t +usb_raw_free(void *cookie) +{ + TRACE((DRIVER_NAME": free()\n")); + return B_OK; +} + + +static void +usb_raw_callback(void *cookie, uint32 status, void *data, size_t actualLength) +{ + TRACE((DRIVER_NAME": callback()\n")); + raw_device *device = (raw_device *)cookie; + + switch (status) { + case B_USB_STATUS_SUCCESS: + device->status = RAW_STATUS_SUCCESS; + break; + case B_USB_STATUS_DEVICE_CRC_ERROR: + device->status = RAW_STATUS_CRC_ERROR; + break; + case B_USB_STATUS_DEVICE_TIMEOUT: + device->status = RAW_STATUS_TIMEOUT; + break; + case B_USB_STATUS_DEVICE_STALLED: + device->status = RAW_STATUS_STALLED; + break; + case B_USB_STATUS_IRP_CANCELLED_BY_REQUEST: + device->status = RAW_STATUS_ABORTED; + break; + default: + device->status = RAW_STATUS_FAILED; + break; + } + + device->actual_length = actualLength; + release_sem(device->notify); +} + + +static status_t +usb_raw_ioctl(void *cookie, uint32 op, void *buffer, size_t length) +{ + TRACE((DRIVER_NAME": ioctl\n")); + raw_device *device = (raw_device *)cookie; + raw_command *command = (raw_command *)buffer; + + switch (op) { + case RAW_COMMAND_GET_VERSION: { + command->version.status = DRIVER_VERSION; + return B_OK; + } + + case RAW_COMMAND_GET_DEVICE_DESCRIPTOR: { + const usb_device_descriptor *deviceDescriptor = + gUSBModule->get_device_descriptor(device->device); + if (!deviceDescriptor) { + command->device.status = RAW_STATUS_ABORTED; + return B_OK; + } + + memcpy(command->device.descriptor, deviceDescriptor, + sizeof(usb_device_descriptor)); + command->device.status = RAW_STATUS_SUCCESS; + return B_OK; + } + + case RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR: { + const usb_configuration_info *configurationInfo = + gUSBModule->get_nth_configuration(device->device, + command->config.config_index); + if (!configurationInfo) { + command->config.status = RAW_STATUS_INVALID_CONFIGURATION; + return B_OK; + } + + memcpy(command->config.descriptor, configurationInfo->descr, + sizeof(usb_configuration_descriptor)); + command->config.status = RAW_STATUS_SUCCESS; + return B_OK; + } + + case RAW_COMMAND_GET_INTERFACE_DESCRIPTOR: { + const usb_configuration_info *configurationInfo = + gUSBModule->get_nth_configuration(device->device, + command->interface.config_index); + if (!configurationInfo) { + command->interface.status = RAW_STATUS_INVALID_CONFIGURATION; + return B_OK; + } + + if (command->interface.interface_index >= configurationInfo->interface_count) { + command->interface.status = RAW_STATUS_INVALID_INTERFACE; + return B_OK; + } + + const usb_interface_info *interfaceInfo = + configurationInfo->interface[command->endpoint.interface_index].active; + if (!interfaceInfo) { + command->interface.status = RAW_STATUS_ABORTED; + return B_OK; + } + + memcpy(command->interface.descriptor, interfaceInfo->descr, + sizeof(usb_interface_descriptor)); + command->interface.status = RAW_STATUS_SUCCESS; + return B_OK; + } + + case RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR: { + const usb_configuration_info *configurationInfo = + gUSBModule->get_nth_configuration(device->device, + command->endpoint.config_index); + if (!configurationInfo) { + command->endpoint.status = RAW_STATUS_INVALID_CONFIGURATION; + return B_OK; + } + + if (command->endpoint.interface_index >= configurationInfo->interface_count) { + command->endpoint.status = RAW_STATUS_INVALID_INTERFACE; + return B_OK; + } + + const usb_interface_info *interfaceInfo = + configurationInfo->interface[command->endpoint.interface_index].active; + if (!interfaceInfo) { + command->endpoint.status = RAW_STATUS_ABORTED; + return B_OK; + } + + if (command->endpoint.endpoint_index >= interfaceInfo->endpoint_count) { + command->endpoint.status = RAW_STATUS_INVALID_ENDPOINT; + return B_OK; + } + + memcpy(command->endpoint.descriptor, + interfaceInfo->endpoint[command->endpoint.endpoint_index].descr, + sizeof(usb_endpoint_descriptor)); + command->endpoint.status = RAW_STATUS_SUCCESS; + return B_OK; + } + + case RAW_COMMAND_GET_GENERIC_DESCRIPTOR: { + const usb_configuration_info *configurationInfo = + gUSBModule->get_nth_configuration(device->device, + command->generic.config_index); + if (!configurationInfo) { + command->generic.status = RAW_STATUS_INVALID_CONFIGURATION; + return B_OK; + } + + if (command->generic.interface_index >= configurationInfo->interface_count) { + command->generic.status = RAW_STATUS_INVALID_INTERFACE; + return B_OK; + } + + const usb_interface_info *interfaceInfo = + configurationInfo->interface[command->generic.interface_index].active; + if (!interfaceInfo) { + command->generic.status = RAW_STATUS_ABORTED; + return B_OK; + } + + if (command->generic.generic_index >= interfaceInfo->generic_count) { + // ToDo: add RAW_STATUS_INVALID_GENERIC + command->generic.status = RAW_STATUS_INVALID_ENDPOINT; + return B_OK; + } + + usb_descriptor *descriptor = interfaceInfo->generic[command->generic.generic_index]; + if (!descriptor) { + command->generic.status = RAW_STATUS_ABORTED; + return B_OK; + } + + if (descriptor->generic.length > command->generic.length) { + command->generic.status = RAW_STATUS_NO_MEMORY; + return B_OK; + } + + memcpy(command->generic.descriptor, descriptor, + descriptor->generic.length); + command->generic.status = RAW_STATUS_SUCCESS; + return B_OK; + } + + case RAW_COMMAND_GET_STRING_DESCRIPTOR: { + size_t actualLength = 0; + uint8 firstTwoBytes[2]; + + if (gUSBModule->get_descriptor(device->device, + USB_DESCRIPTOR_STRING, command->string.string_index, 0, + firstTwoBytes, 2, &actualLength) < B_OK + || actualLength != 2 + || firstTwoBytes[1] != USB_DESCRIPTOR_STRING) { + command->string.status = RAW_STATUS_ABORTED; + command->string.length = 0; + return B_OK; + } + + uint8 stringLength = MIN(firstTwoBytes[0], command->string.length); + char *string = (char *)malloc(stringLength); + if (!string) { + command->string.status = RAW_STATUS_ABORTED; + command->string.length = 0; + return B_NO_MEMORY; + } + + if (gUSBModule->get_descriptor(device->device, + USB_DESCRIPTOR_STRING, command->string.string_index, 0, + string, stringLength, &actualLength) < B_OK + || actualLength != stringLength) { + command->string.status = RAW_STATUS_ABORTED; + command->string.length = 0; + free(string); + return B_OK; + } + + memcpy(command->string.descriptor, string, stringLength); + command->string.status = RAW_STATUS_SUCCESS; + command->string.length = stringLength; + free(string); + return B_OK; + } + + case RAW_COMMAND_GET_DESCRIPTOR: { + size_t actualLength = 0; + uint8 firstTwoBytes[2]; + + if (gUSBModule->get_descriptor(device->device, + command->descriptor.type, command->descriptor.index, + command->descriptor.language_id, firstTwoBytes, 2, + &actualLength) < B_OK + || actualLength != 2 + || firstTwoBytes[1] != command->descriptor.type) { + command->descriptor.status = RAW_STATUS_ABORTED; + command->descriptor.length = 0; + return B_OK; + } + + uint8 length = MIN(firstTwoBytes[0], command->descriptor.length); + uint8 *buffer = (uint8 *)malloc(length); + if (!buffer) { + command->descriptor.status = RAW_STATUS_ABORTED; + command->descriptor.length = 0; + return B_NO_MEMORY; + } + + if (gUSBModule->get_descriptor(device->device, + command->descriptor.type, command->descriptor.index, + command->descriptor.language_id, buffer, length, + &actualLength) < B_OK + || actualLength != length) { + command->descriptor.status = RAW_STATUS_ABORTED; + command->descriptor.length = 0; + free(buffer); + return B_OK; + } + + memcpy(command->descriptor.data, buffer, length); + command->descriptor.status = RAW_STATUS_SUCCESS; + command->descriptor.length = length; + free(buffer); + return B_OK; + } + + case RAW_COMMAND_SET_CONFIGURATION: { + const usb_configuration_info *configurationInfo = + gUSBModule->get_nth_configuration(device->device, + command->config.config_index); + if (!configurationInfo) { + command->config.status = RAW_STATUS_INVALID_CONFIGURATION; + return B_OK; + } + + if (gUSBModule->set_configuration(device->device, + configurationInfo) < B_OK) { + command->config.status = RAW_STATUS_FAILED; + return B_OK; + } + + command->config.status = RAW_STATUS_SUCCESS; + return B_OK; + } + + case RAW_COMMAND_CONTROL_TRANSFER: { + if (command->control.length > B_PAGE_SIZE) { + command->control.status = RAW_STATUS_FAILED; + return B_OK; + } + + benaphore_lock(&device->lock); + memcpy(device->buffer, command->control.data, command->control.length); + if (gUSBModule->queue_request(device->device, + command->control.request_type, command->control.request, + command->control.value, command->control.index, + command->control.length, device->buffer, command->control.length, + usb_raw_callback, device) < B_OK) { + command->control.status = RAW_STATUS_FAILED; + command->control.length = 0; + benaphore_unlock(&device->lock); + return B_OK; + } + + acquire_sem(device->notify); + memcpy(command->control.data, device->buffer, command->control.length); + command->control.status = device->status; + command->control.length = device->actual_length; + benaphore_unlock(&device->lock); + return B_OK; + } + + case RAW_COMMAND_INTERRUPT_TRANSFER: + case RAW_COMMAND_BULK_TRANSFER: { + if (command->transfer.length > B_PAGE_SIZE) { + command->transfer.status = RAW_STATUS_FAILED; + return B_OK; + } + + const usb_configuration_info *configurationInfo = + gUSBModule->get_configuration(device->device); + if (!configurationInfo) { + command->transfer.status = RAW_STATUS_INVALID_CONFIGURATION; + return B_OK; + } + + if (command->transfer.interface >= configurationInfo->interface_count) { + command->transfer.status = RAW_STATUS_INVALID_INTERFACE; + return B_OK; + } + + const usb_interface_info *interfaceInfo = + configurationInfo->interface[command->transfer.interface].active; + if (!interfaceInfo) { + command->transfer.status = RAW_STATUS_ABORTED; + return B_OK; + } + + if (command->transfer.endpoint >= interfaceInfo->endpoint_count) { + command->transfer.status = RAW_STATUS_INVALID_ENDPOINT; + return B_OK; + } + + const usb_endpoint_info *endpointInfo = + &interfaceInfo->endpoint[command->transfer.endpoint]; + if (!endpointInfo->handle) { + command->transfer.status = RAW_STATUS_INVALID_ENDPOINT; + return B_OK; + } + + benaphore_lock(&device->lock); + memcpy(device->buffer, command->transfer.data, command->transfer.length); + + status_t status; + if (op == RAW_COMMAND_INTERRUPT_TRANSFER) { + status = gUSBModule->queue_interrupt(endpointInfo->handle, + device->buffer, command->transfer.length, usb_raw_callback, + device); + } else { + status = gUSBModule->queue_bulk(endpointInfo->handle, + device->buffer, command->transfer.length, usb_raw_callback, + device); + } + + if (status < B_OK) { + command->transfer.status = RAW_STATUS_FAILED; + command->transfer.length = 0; + benaphore_unlock(&device->lock); + return B_OK; + } + + acquire_sem(device->notify); + memcpy(command->transfer.data, device->buffer, command->transfer.length); + command->transfer.status = device->status; + command->transfer.length = device->actual_length; + benaphore_unlock(&device->lock); + return B_OK; + } + } + + return B_DEV_INVALID_IOCTL; +} + + +static status_t +usb_raw_read(void *cookie, off_t position, void *buffer, size_t *length) +{ + TRACE((DRIVER_NAME": read()\n")); + *length = 0; + return B_OK; +} + + +static status_t +usb_raw_write(void *cookie, off_t position, const void *buffer, size_t *length) +{ + TRACE((DRIVER_NAME": write()\n")); + *length = 0; + return B_OK; +} + + +// +//#pragma mark - +// + + +status_t +init_hardware() +{ + TRACE((DRIVER_NAME": init_hardware()\n")); + return B_OK; +} + + +status_t +init_driver() +{ + TRACE((DRIVER_NAME": init_driver()\n")); + static usb_notify_hooks notifyHooks = { + &usb_raw_device_added, + &usb_raw_device_removed + }; + + gDeviceLevel = 0; + memset(gDeviceCounters, 0, sizeof(gDeviceCounters)); + + gDeviceList = NULL; + gDeviceCount = 0; + status_t result = benaphore_init(&gDeviceListLock, "usb_raw device list lock"); + if (result < B_OK) { + TRACE((DRIVER_NAME": failed to create device list lock\n")); + return result; + } + + TRACE((DRIVER_NAME": trying module %s\n", B_USB_MODULE_NAME)); + result = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule); + if (result < B_OK) { + TRACE((DRIVER_NAME": getting module failed 0x%08x\n", result)); + return result; + } + + gUSBModule->register_driver(DRIVER_NAME, NULL, 0, NULL); + gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); + return B_OK; +} + + +void +uninit_driver() +{ + TRACE((DRIVER_NAME": uninit_driver()\n")); + gUSBModule->uninstall_notify(DRIVER_NAME); + benaphore_lock(&gDeviceListLock); + benaphore_destroy(&gDeviceListLock); + put_module(B_USB_MODULE_NAME); +} + + +const char ** +publish_devices(void) +{ + TRACE((DRIVER_NAME": publish_devices()\n")); + benaphore_lock(&gDeviceListLock); + const char **deviceNames = (const char **)malloc(sizeof(char *) * (gDeviceCount + 2)); + + uint8 index = 0; + deviceNames[index++] = DEVICE_NAME; + + raw_device *element = gDeviceList; + while (element) { + deviceNames[index++] = element->name; + element = (raw_device *)element->link; + } + + deviceNames[index++] = NULL; + benaphore_unlock(&gDeviceListLock); + return deviceNames; +} + + +device_hooks * +find_device(const char *name) +{ + TRACE((DRIVER_NAME": find_device()\n")); + static device_hooks hooks = { + &usb_raw_open, + &usb_raw_close, + &usb_raw_free, + &usb_raw_ioctl, + &usb_raw_read, + &usb_raw_write, + NULL, + NULL, + NULL, + NULL + }; + + return &hooks; +} diff --git a/src/add-ons/kernel/drivers/bus/usb/usb_raw.h b/src/add-ons/kernel/drivers/bus/usb/usb_raw.h new file mode 100644 index 0000000000..d855ac7426 --- /dev/null +++ b/src/add-ons/kernel/drivers/bus/usb/usb_raw.h @@ -0,0 +1,147 @@ +/* + * Copyright 2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef _USB_RAW_H_ +#define _USB_RAW_H_ + + +#include +#include + + +typedef enum { + RAW_COMMAND_GET_VERSION = 0x1000, + + RAW_COMMAND_GET_DEVICE_DESCRIPTOR = 0x2000, + RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, + RAW_COMMAND_GET_INTERFACE_DESCRIPTOR, + RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR, + RAW_COMMAND_GET_STRING_DESCRIPTOR, + RAW_COMMAND_GET_GENERIC_DESCRIPTOR, + + RAW_COMMAND_SET_CONFIGURATION = 0x3000, + RAW_COMMAND_SET_FEATURE, + RAW_COMMAND_CLEAR_FEATURE, + RAW_COMMAND_GET_STATUS, + RAW_COMMAND_GET_DESCRIPTOR, + + RAW_COMMAND_CONTROL_TRANSFER = 0x4000, + RAW_COMMAND_INTERRUPT_TRANSFER, + RAW_COMMAND_BULK_TRANSFER +} raw_command_id; + + +typedef enum { + RAW_STATUS_SUCCESS = 0, + + RAW_STATUS_FAILED, + RAW_STATUS_ABORTED, + RAW_STATUS_STALLED, + RAW_STATUS_CRC_ERROR, + RAW_STATUS_TIMEOUT, + + RAW_STATUS_INVALID_CONFIGURATION, + RAW_STATUS_INVALID_INTERFACE, + RAW_STATUS_INVALID_ENDPOINT, + RAW_STATUS_INVALID_STRING, + + RAW_STATUS_NO_MEMORY +} raw_command_status; + + +typedef union { + struct { + status_t status; + } version; + + struct { + status_t status; + usb_device_descriptor *descriptor; + } device; + + struct { + status_t status; + usb_configuration_descriptor *descriptor; + uint32 config_index; + } config; + + struct { + status_t status; + usb_interface_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + } interface; + + struct { + status_t status; + usb_endpoint_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 endpoint_index; + } endpoint; + + struct { + status_t status; + usb_string_descriptor *descriptor; + uint32 string_index; + size_t length; + } string; + + struct { + status_t status; + usb_descriptor *descriptor; + uint32 config_index; + uint32 interface_index; + uint32 generic_index; + size_t length; + } generic; + + struct { + status_t status; + uint8 type; + uint8 index; + uint16 language_id; + void *data; + size_t length; + } descriptor; + + struct { + status_t status; + uint8 request_type; + uint8 request; + uint16 value; + uint16 index; + uint16 length; + void *data; + } control; + + struct { + status_t status; + uint32 interface; + uint32 endpoint; + void *data; + size_t length; + } transfer; +} raw_command; + + +typedef struct { + const usb_device *device; + benaphore lock; + + char name[32]; + void *link; + + sem_id notify; + status_t status; + size_t actual_length; + + uint8 buffer[B_PAGE_SIZE]; +} raw_device; + +#endif // _USB_RAW_H_