/* * Copyright 2006-2018, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz * Adrien Destugues */ #include "usb_raw.h" #include #include #include #include #include #include #include #include #include #include //#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" typedef struct { usb_device device; mutex lock; uint32 reference_count; char name[64]; void *link; sem_id notify; status_t status; size_t actual_length; } raw_device; int32 api_version = B_CUR_DRIVER_API_VERSION; static usb_module_info *gUSBModule = NULL; static raw_device *gDeviceList = NULL; static uint32 gDeviceCount = 0; static mutex gDeviceListLock; static char **gDeviceNames = NULL; static status_t usb_raw_device_added(usb_device newDevice, void **cookie) { TRACE((DRIVER_NAME": device_added(0x%08" B_PRIx32 ")\n", newDevice)); raw_device *device = (raw_device *)malloc(sizeof(raw_device)); mutex_init(&device->lock, "usb_raw device lock"); device->notify = create_sem(0, "usb_raw callback notify"); if (device->notify < B_OK) { mutex_destroy(&device->lock); free(device); return B_NO_MORE_SEMS; } char deviceName[64]; memcpy(deviceName, &newDevice, sizeof(usb_device)); if (gUSBModule->usb_ioctl('DNAM', deviceName, sizeof(deviceName)) >= B_OK) { snprintf(device->name, sizeof(device->name), "bus/usb/%s", deviceName); } else { snprintf(device->name, sizeof(device->name), "bus/usb/%08" B_PRIx32, newDevice); } device->device = newDevice; device->reference_count = 0; mutex_lock(&gDeviceListLock); device->link = (void *)gDeviceList; gDeviceList = device; gDeviceCount++; mutex_unlock(&gDeviceListLock); TRACE((DRIVER_NAME": new device: 0x%p\n", device)); *cookie = (void *)device; return B_OK; } static status_t usb_raw_device_removed(void *cookie) { TRACE((DRIVER_NAME": device_removed(0x%p)\n", cookie)); raw_device *device = (raw_device *)cookie; // cancel all pending transfers to make sure no one keeps waiting forever // in syscalls. const usb_configuration_info *configurationInfo = gUSBModule->get_configuration(device->device); if (configurationInfo != NULL) { struct usb_interface_info* interface = configurationInfo->interface->active; for (unsigned int i = 0; i < interface->endpoint_count; i++) gUSBModule->cancel_queued_transfers(interface->endpoint[i].handle); } mutex_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--; mutex_unlock(&gDeviceListLock); device->device = 0; if (device->reference_count == 0) { mutex_lock(&device->lock); mutex_destroy(&device->lock); delete_sem(device->notify); free(device); } return B_OK; } // //#pragma mark - // static const usb_configuration_info * usb_raw_get_configuration(raw_device *device, uint32 configIndex, status_t *status) { const usb_configuration_info *result = gUSBModule->get_nth_configuration( device->device, configIndex); if (result == NULL) { *status = B_USB_RAW_STATUS_INVALID_CONFIGURATION; return NULL; } return result; } static const usb_interface_info * usb_raw_get_interface(raw_device *device, uint32 configIndex, uint32 interfaceIndex, uint32 alternateIndex, status_t *status) { const usb_configuration_info *configurationInfo = usb_raw_get_configuration(device, configIndex, status); if (configurationInfo == NULL) return NULL; if (interfaceIndex >= configurationInfo->interface_count) { *status = B_USB_RAW_STATUS_INVALID_INTERFACE; return NULL; } const usb_interface_info *result = NULL; if (alternateIndex == B_USB_RAW_ACTIVE_ALTERNATE) result = configurationInfo->interface[interfaceIndex].active; else { const usb_interface_list *interfaceList = &configurationInfo->interface[interfaceIndex]; if (alternateIndex >= interfaceList->alt_count) { *status = B_USB_RAW_STATUS_INVALID_INTERFACE; return NULL; } result = &interfaceList->alt[alternateIndex]; } return result; } // //#pragma mark - // static status_t usb_raw_open(const char *name, uint32 flags, void **cookie) { TRACE((DRIVER_NAME": open()\n")); mutex_lock(&gDeviceListLock); raw_device *element = gDeviceList; while (element) { if (strcmp(name, element->name) == 0) { element->reference_count++; *cookie = element; mutex_unlock(&gDeviceListLock); return B_OK; } element = (raw_device *)element->link; } mutex_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")); mutex_lock(&gDeviceListLock); raw_device *device = (raw_device *)cookie; device->reference_count--; if (device->device == 0) { mutex_lock(&device->lock); mutex_destroy(&device->lock); delete_sem(device->notify); free(device); } mutex_unlock(&gDeviceListLock); return B_OK; } static void usb_raw_callback(void *cookie, status_t status, void *data, size_t actualLength) { TRACE((DRIVER_NAME": callback()\n")); raw_device *device = (raw_device *)cookie; switch (status) { case B_OK: device->status = B_USB_RAW_STATUS_SUCCESS; break; case B_TIMED_OUT: device->status = B_USB_RAW_STATUS_TIMEOUT; break; case B_CANCELED: device->status = B_USB_RAW_STATUS_ABORTED; break; case B_DEV_CRC_ERROR: device->status = B_USB_RAW_STATUS_CRC_ERROR; break; case B_DEV_STALLED: device->status = B_USB_RAW_STATUS_STALLED; break; default: device->status = B_USB_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; if (device->device == 0) return B_DEV_NOT_READY; usb_raw_command command; if (length < sizeof(command.version.status)) return B_BUFFER_OVERFLOW; if (!IS_USER_ADDRESS(buffer) || user_memcpy(&command, buffer, min_c(length, sizeof(command))) != B_OK) { return B_BAD_ADDRESS; } command.version.status = B_USB_RAW_STATUS_ABORTED; status_t status = B_DEV_INVALID_IOCTL; switch (op) { case B_USB_RAW_COMMAND_GET_VERSION: { command.version.status = B_USB_RAW_PROTOCOL_VERSION; status = B_OK; break; } case B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR: { if (length < sizeof(command.device)) return B_BUFFER_OVERFLOW; status = B_OK; const usb_device_descriptor *deviceDescriptor = gUSBModule->get_device_descriptor(device->device); if (deviceDescriptor == NULL) return B_OK; if (!IS_USER_ADDRESS(command.device.descriptor) || user_memcpy(command.device.descriptor, deviceDescriptor, sizeof(usb_device_descriptor)) != B_OK) { return B_BAD_ADDRESS; } command.device.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR: case B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR_ETC: { if (op == B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR_ETC && length < sizeof(command.config_etc)) return B_BUFFER_OVERFLOW; if (length < sizeof(command.config)) return B_BUFFER_OVERFLOW; status = B_OK; const usb_configuration_info *configurationInfo = usb_raw_get_configuration(device, command.config.config_index, &command.config.status); if (configurationInfo == NULL) break; size_t sizeToCopy = sizeof(usb_configuration_descriptor); if (op == B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR_ETC) { sizeToCopy = std::min(command.config_etc.length, (size_t)configurationInfo->descr->total_length); } if (!IS_USER_ADDRESS(command.config.descriptor) || user_memcpy(command.config.descriptor, configurationInfo->descr, sizeToCopy) != B_OK) { return B_BAD_ADDRESS; } command.config.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT: case B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX: { if (length < sizeof(command.alternate)) return B_BUFFER_OVERFLOW; status = B_OK; const usb_configuration_info *configurationInfo = usb_raw_get_configuration(device, command.alternate.config_index, &command.alternate.status); if (configurationInfo == NULL) break; if (command.alternate.interface_index >= configurationInfo->interface_count) { command.alternate.status = B_USB_RAW_STATUS_INVALID_INTERFACE; break; } const usb_interface_list *interfaceList = &configurationInfo->interface[ command.alternate.interface_index]; if (op == B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT) { command.alternate.alternate_info = interfaceList->alt_count; } else { for (size_t i = 0; i < interfaceList->alt_count; i++) { if (&interfaceList->alt[i] == interfaceList->active) { command.alternate.alternate_info = i; break; } } } command.alternate.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR: case B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC: { const usb_interface_info *interfaceInfo = NULL; status = B_OK; if (op == B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR) { if (length < sizeof(command.interface)) return B_BUFFER_OVERFLOW; interfaceInfo = usb_raw_get_interface(device, command.interface.config_index, command.interface.interface_index, B_USB_RAW_ACTIVE_ALTERNATE, &command.interface.status); } else { if (length < sizeof(command.interface_etc)) return B_BUFFER_OVERFLOW; interfaceInfo = usb_raw_get_interface(device, command.interface_etc.config_index, command.interface_etc.interface_index, command.interface_etc.alternate_index, &command.interface_etc.status); } if (interfaceInfo == NULL) break; if (!IS_USER_ADDRESS(command.interface.descriptor) || user_memcpy(command.interface.descriptor, interfaceInfo->descr, sizeof(usb_interface_descriptor)) != B_OK) { return B_BAD_ADDRESS; } command.interface.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR: case B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC: { uint32 endpointIndex = 0; const usb_interface_info *interfaceInfo = NULL; status = B_OK; if (op == B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR) { if (length < sizeof(command.endpoint)) return B_BUFFER_OVERFLOW; interfaceInfo = usb_raw_get_interface(device, command.endpoint.config_index, command.endpoint.interface_index, B_USB_RAW_ACTIVE_ALTERNATE, &command.endpoint.status); endpointIndex = command.endpoint.endpoint_index; } else { if (length < sizeof(command.endpoint_etc)) return B_BUFFER_OVERFLOW; interfaceInfo = usb_raw_get_interface(device, command.endpoint_etc.config_index, command.endpoint_etc.interface_index, command.endpoint_etc.alternate_index, &command.endpoint_etc.status); endpointIndex = command.endpoint_etc.endpoint_index; } if (interfaceInfo == NULL) break; if (endpointIndex >= interfaceInfo->endpoint_count) { command.endpoint.status = B_USB_RAW_STATUS_INVALID_ENDPOINT; break; } if (!IS_USER_ADDRESS(command.endpoint.descriptor) || user_memcpy(command.endpoint.descriptor, interfaceInfo->endpoint[endpointIndex].descr, sizeof(usb_endpoint_descriptor)) != B_OK) { return B_BAD_ADDRESS; } command.endpoint.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR: case B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR_ETC: { uint32 genericIndex = 0; size_t genericLength = 0; const usb_interface_info *interfaceInfo = NULL; status = B_OK; if (op == B_USB_RAW_COMMAND_GET_GENERIC_DESCRIPTOR) { if (length < sizeof(command.generic)) return B_BUFFER_OVERFLOW; interfaceInfo = usb_raw_get_interface(device, command.generic.config_index, command.generic.interface_index, B_USB_RAW_ACTIVE_ALTERNATE, &command.generic.status); genericIndex = command.generic.generic_index; genericLength = command.generic.length; } else { if (length < sizeof(command.generic_etc)) return B_BUFFER_OVERFLOW; interfaceInfo = usb_raw_get_interface(device, command.generic_etc.config_index, command.generic_etc.interface_index, command.generic_etc.alternate_index, &command.generic_etc.status); genericIndex = command.generic_etc.generic_index; genericLength = command.generic_etc.length; } if (interfaceInfo == NULL) break; if (genericIndex >= interfaceInfo->generic_count) { command.endpoint.status = B_USB_RAW_STATUS_INVALID_ENDPOINT; break; } usb_descriptor *descriptor = interfaceInfo->generic[genericIndex]; if (descriptor == NULL) break; if (!IS_USER_ADDRESS(command.generic.descriptor) || user_memcpy(command.generic.descriptor, descriptor, min_c(genericLength, descriptor->generic.length)) != B_OK) { return B_BAD_ADDRESS; } if (descriptor->generic.length > genericLength) command.generic.status = B_USB_RAW_STATUS_NO_MEMORY; else command.generic.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_GET_STRING_DESCRIPTOR: { if (length < sizeof(command.string)) return B_BUFFER_OVERFLOW; size_t actualLength = 0; uint8 temp[4]; status = B_OK; // We need to fetch the default language code, first. if (gUSBModule->get_descriptor(device->device, USB_DESCRIPTOR_STRING, 0, 0, temp, 4, &actualLength) < B_OK || actualLength != 4 || temp[1] != USB_DESCRIPTOR_STRING) { command.string.status = B_USB_RAW_STATUS_ABORTED; command.string.length = 0; break; } const uint16 langid = (temp[2] | (temp[3] << 8)); // Now fetch the string length. if (gUSBModule->get_descriptor(device->device, USB_DESCRIPTOR_STRING, command.string.string_index, langid, temp, 2, &actualLength) < B_OK || actualLength != 2 || temp[1] != USB_DESCRIPTOR_STRING) { command.string.status = B_USB_RAW_STATUS_ABORTED; command.string.length = 0; break; } uint8 stringLength = MIN(temp[0], command.string.length); char *string = (char *)malloc(stringLength); if (string == NULL) { command.string.status = B_USB_RAW_STATUS_ABORTED; command.string.length = 0; status = B_NO_MEMORY; break; } if (gUSBModule->get_descriptor(device->device, USB_DESCRIPTOR_STRING, command.string.string_index, langid, string, stringLength, &actualLength) < B_OK || actualLength != stringLength) { command.string.status = B_USB_RAW_STATUS_ABORTED; command.string.length = 0; free(string); break; } if (!IS_USER_ADDRESS(command.string.descriptor) || user_memcpy(command.string.descriptor, string, stringLength) != B_OK) { free(string); return B_BAD_ADDRESS; } command.string.status = B_USB_RAW_STATUS_SUCCESS; command.string.length = stringLength; free(string); break; } case B_USB_RAW_COMMAND_GET_DESCRIPTOR: { if (length < sizeof(command.descriptor)) return B_BUFFER_OVERFLOW; size_t actualLength = 0; uint8 firstTwoBytes[2]; status = B_OK; 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 = B_USB_RAW_STATUS_ABORTED; command.descriptor.length = 0; break; } uint8 descriptorLength = MIN(firstTwoBytes[0], command.descriptor.length); uint8 *descriptorBuffer = (uint8 *)malloc(descriptorLength); if (descriptorBuffer == NULL) { command.descriptor.status = B_USB_RAW_STATUS_ABORTED; command.descriptor.length = 0; status = B_NO_MEMORY; break; } if (gUSBModule->get_descriptor(device->device, command.descriptor.type, command.descriptor.index, command.descriptor.language_id, descriptorBuffer, descriptorLength, &actualLength) < B_OK || actualLength != descriptorLength) { command.descriptor.status = B_USB_RAW_STATUS_ABORTED; command.descriptor.length = 0; free(descriptorBuffer); break; } if (!IS_USER_ADDRESS(command.descriptor.data) || user_memcpy(command.descriptor.data, descriptorBuffer, descriptorLength) != B_OK) { free(descriptorBuffer); return B_BAD_ADDRESS; } command.descriptor.status = B_USB_RAW_STATUS_SUCCESS; command.descriptor.length = descriptorLength; free(descriptorBuffer); break; } case B_USB_RAW_COMMAND_SET_CONFIGURATION: { if (length < sizeof(command.config)) return B_BUFFER_OVERFLOW; status = B_OK; const usb_configuration_info *configurationInfo = usb_raw_get_configuration(device, command.config.config_index, &command.config.status); if (configurationInfo == NULL) break; if (gUSBModule->set_configuration(device->device, configurationInfo) < B_OK) { command.config.status = B_USB_RAW_STATUS_FAILED; break; } command.config.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_SET_ALT_INTERFACE: { if (length < sizeof(command.alternate)) return B_BUFFER_OVERFLOW; status = B_OK; const usb_configuration_info *configurationInfo = usb_raw_get_configuration(device, command.alternate.config_index, &command.alternate.status); if (configurationInfo == NULL) break; if (command.alternate.interface_index >= configurationInfo->interface_count) { command.alternate.status = B_USB_RAW_STATUS_INVALID_INTERFACE; break; } const usb_interface_list *interfaceList = &configurationInfo->interface[command.alternate.interface_index]; if (command.alternate.alternate_info >= interfaceList->alt_count) { command.alternate.status = B_USB_RAW_STATUS_INVALID_INTERFACE; break; } if (gUSBModule->set_alt_interface(device->device, &interfaceList->alt[command.alternate.alternate_info]) < B_OK) { command.alternate.status = B_USB_RAW_STATUS_FAILED; break; } command.alternate.status = B_USB_RAW_STATUS_SUCCESS; break; } case B_USB_RAW_COMMAND_CONTROL_TRANSFER: { if (length < sizeof(command.control)) return B_BUFFER_OVERFLOW; void *controlData = malloc(command.control.length); if (controlData == NULL) return B_NO_MEMORY; MemoryDeleter dataDeleter(controlData); bool inTransfer = (command.control.request_type & USB_ENDPOINT_ADDR_DIR_IN) != 0; if (!IS_USER_ADDRESS(command.control.data) || (!inTransfer && user_memcpy(controlData, command.control.data, command.control.length) != B_OK)) { return B_BAD_ADDRESS; } MutexLocker deviceLocker(device->lock); if (gUSBModule->queue_request(device->device, command.control.request_type, command.control.request, command.control.value, command.control.index, command.control.length, controlData, usb_raw_callback, device) < B_OK) { command.control.status = B_USB_RAW_STATUS_FAILED; command.control.length = 0; status = B_OK; break; } status = acquire_sem_etc(device->notify, 1, B_KILL_CAN_INTERRUPT, 0); if (status != B_OK) { gUSBModule->cancel_queued_requests(device->device); acquire_sem(device->notify); } command.control.status = device->status; command.control.length = device->actual_length; deviceLocker.Unlock(); if (command.control.status == B_OK) status = B_OK; if (inTransfer && user_memcpy(command.control.data, controlData, command.control.length) != B_OK) { status = B_BAD_ADDRESS; } break; } case B_USB_RAW_COMMAND_INTERRUPT_TRANSFER: case B_USB_RAW_COMMAND_BULK_TRANSFER: case B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER: { if (length < sizeof(command.transfer)) return B_BUFFER_OVERFLOW; status = B_OK; const usb_configuration_info *configurationInfo = gUSBModule->get_configuration(device->device); if (configurationInfo == NULL) { command.transfer.status = B_USB_RAW_STATUS_INVALID_CONFIGURATION; break; } if (command.transfer.interface >= configurationInfo->interface_count) { command.transfer.status = B_USB_RAW_STATUS_INVALID_INTERFACE; break; } const usb_interface_info *interfaceInfo = configurationInfo->interface[command.transfer.interface].active; if (interfaceInfo == NULL) { command.transfer.status = B_USB_RAW_STATUS_ABORTED; break; } if (command.transfer.endpoint >= interfaceInfo->endpoint_count) { command.transfer.status = B_USB_RAW_STATUS_INVALID_ENDPOINT; break; } const usb_endpoint_info *endpointInfo = &interfaceInfo->endpoint[command.transfer.endpoint]; if (!endpointInfo->handle) { command.transfer.status = B_USB_RAW_STATUS_INVALID_ENDPOINT; break; } size_t descriptorsSize = 0; usb_iso_packet_descriptor *packetDescriptors = NULL; void *transferData = NULL; MemoryDeleter descriptorsDeleter, dataDeleter; bool inTransfer = (endpointInfo->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) != 0; if (op == B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER) { if (length < sizeof(command.isochronous)) return B_BUFFER_OVERFLOW; descriptorsSize = sizeof(usb_iso_packet_descriptor) * command.isochronous.packet_count; packetDescriptors = (usb_iso_packet_descriptor *)malloc(descriptorsSize); if (packetDescriptors == NULL) { command.transfer.status = B_USB_RAW_STATUS_NO_MEMORY; command.transfer.length = 0; break; } descriptorsDeleter.SetTo(packetDescriptors); if (!IS_USER_ADDRESS(command.isochronous.data) || !IS_USER_ADDRESS(command.isochronous.packet_descriptors) || user_memcpy(packetDescriptors, command.isochronous.packet_descriptors, descriptorsSize) != B_OK) { return B_BAD_ADDRESS; } } else { transferData = malloc(command.transfer.length); if (transferData == NULL) { command.transfer.status = B_USB_RAW_STATUS_NO_MEMORY; command.transfer.length = 0; break; } dataDeleter.SetTo(transferData); if (!IS_USER_ADDRESS(command.transfer.data) || (!inTransfer && user_memcpy(transferData, command.transfer.data, command.transfer.length) != B_OK)) { return B_BAD_ADDRESS; } } status_t status; MutexLocker deviceLocker(device->lock); if (op == B_USB_RAW_COMMAND_INTERRUPT_TRANSFER) { status = gUSBModule->queue_interrupt(endpointInfo->handle, transferData, command.transfer.length, usb_raw_callback, device); } else if (op == B_USB_RAW_COMMAND_BULK_TRANSFER) { status = gUSBModule->queue_bulk(endpointInfo->handle, transferData, command.transfer.length, usb_raw_callback, device); } else { status = gUSBModule->queue_isochronous(endpointInfo->handle, command.isochronous.data, command.isochronous.length, packetDescriptors, command.isochronous.packet_count, NULL, 0, usb_raw_callback, device); } if (status < B_OK) { command.transfer.status = B_USB_RAW_STATUS_FAILED; command.transfer.length = 0; status = B_OK; break; } status = acquire_sem_etc(device->notify, 1, B_KILL_CAN_INTERRUPT, 0); if (status != B_OK) { gUSBModule->cancel_queued_transfers(endpointInfo->handle); acquire_sem(device->notify); } command.transfer.status = device->status; command.transfer.length = device->actual_length; deviceLocker.Unlock(); if (command.transfer.status == B_OK) status = B_OK; if (op == B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER) { if (user_memcpy(command.isochronous.packet_descriptors, packetDescriptors, descriptorsSize) != B_OK) { status = B_BAD_ADDRESS; } } else { if (inTransfer && user_memcpy(command.transfer.data, transferData, command.transfer.length) != B_OK) { status = B_BAD_ADDRESS; } } break; } } if (user_memcpy(buffer, &command, min_c(length, sizeof(command))) != B_OK) return B_BAD_ADDRESS; return status; } 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 }; gDeviceList = NULL; gDeviceCount = 0; mutex_init(&gDeviceListLock, "usb_raw device list lock"); TRACE((DRIVER_NAME": trying module %s\n", B_USB_MODULE_NAME)); status_t result = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule); if (result < B_OK) { TRACE((DRIVER_NAME": getting module failed 0x%08" B_PRIx32 "\n", result)); mutex_destroy(&gDeviceListLock); 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); mutex_lock(&gDeviceListLock); if (gDeviceNames) { for (int32 i = 1; gDeviceNames[i]; i++) free(gDeviceNames[i]); free(gDeviceNames); gDeviceNames = NULL; } mutex_destroy(&gDeviceListLock); put_module(B_USB_MODULE_NAME); } const char ** publish_devices() { TRACE((DRIVER_NAME": publish_devices()\n")); if (gDeviceNames) { for (int32 i = 0; gDeviceNames[i]; i++) free(gDeviceNames[i]); free(gDeviceNames); gDeviceNames = NULL; } int32 index = 0; gDeviceNames = (char **)malloc(sizeof(char *) * (gDeviceCount + 2)); if (gDeviceNames == NULL) return NULL; gDeviceNames[index++] = strdup(DEVICE_NAME); mutex_lock(&gDeviceListLock); raw_device *element = gDeviceList; while (element) { gDeviceNames[index++] = strdup(element->name); element = (raw_device *)element->link; } gDeviceNames[index++] = NULL; mutex_unlock(&gDeviceListLock); return (const char **)gDeviceNames; } 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; }