/* * Copyright 2010, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Jérôme Duval, korli@users.berlios.de */ /* Devices and messages reference: usb-modeswitch-data-20100826 */ #include #include #include #include #include #include #include #include #define DRIVER_NAME "usb_modeswitch" #define TRACE_USB_MODESWITCH 1 #ifdef TRACE_USB_MODESWITCH #define TRACE(x...) dprintf(DRIVER_NAME": "x) #else #define TRACE(x...) /* nothing */ #endif #define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME": "x) #define ENTER() TRACE("%s", __FUNCTION__) enum msgType { MSG_NONE = 0, MSG_HUAWEI_1, MSG_HUAWEI_2, MSG_HUAWEI_3, MSG_NOKIA_1, MSG_OLIVETTI_1, MSG_OLIVETTI_2, MSG_OPTION_1, MSG_ATHEROS_1, }; unsigned char kDevicesMsg[][31] = { { /* MSG_HUAWEI_1 */ 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_HUAWEI_2 */ 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0a, 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_HUAWEI_3 */ 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_NOKIA_1 */ 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_OLIVETTI_1 */ 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_OLIVETTI_2 */ 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x06, 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_OPTION_1 */ 0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { /* MSG_ATHEROS_1 */ 0x55, 0x53, 0x42, 0x43, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; #define HUAWEI_VENDOR 0x12d1 #define NOKIA_VENDOR 0x0421 #define NOVATEL_VENDOR 0x1410 #define ZYDAS_VENDOR 0x0ace #define ZTE_VENDOR 0x19d2 #define OLIVETTI_VENDOR 0x0b3c #define OPTION_VENDOR 0x0af0 #define ATHEROS_VENDOR 0x0cf3 static const struct { usb_support_descriptor desc; msgType type; } kDevices[] = { {{ 0, 0, 0, HUAWEI_VENDOR, 0x1446}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x14ad}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x14c1}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x1520}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x1521}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x1523}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x1557}, MSG_HUAWEI_1}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x1031}, MSG_HUAWEI_2}, {{ 0, 0, 0, HUAWEI_VENDOR, 0x101e}, MSG_HUAWEI_3}, {{ 0, 0, 0, NOKIA_VENDOR, 0x060c}, MSG_NOKIA_1}, {{ 0, 0, 0, NOKIA_VENDOR, 0x0610}, MSG_NOKIA_1}, {{ 0, 0, 0, NOVATEL_VENDOR, 0x5010}, MSG_NOKIA_1}, {{ 0, 0, 0, NOVATEL_VENDOR, 0x5020}, MSG_NOKIA_1}, {{ 0, 0, 0, NOVATEL_VENDOR, 0x5030}, MSG_NOKIA_1}, {{ 0, 0, 0, NOVATEL_VENDOR, 0x5031}, MSG_NOKIA_1}, {{ 0, 0, 0, NOVATEL_VENDOR, 0x5041}, MSG_NOKIA_1}, {{ 0, 0, 0, ZYDAS_VENDOR, 0x2011}, MSG_NOKIA_1}, {{ 0, 0, 0, ZYDAS_VENDOR, 0x20ff}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x0026}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x0083}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x0101}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x0115}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x1001}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x1007}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x1009}, MSG_NOKIA_1}, {{ 0, 0, 0, ZTE_VENDOR, 0x1013}, MSG_NOKIA_1}, {{ 0, 0, 0, OLIVETTI_VENDOR, 0xc700}, MSG_OLIVETTI_1}, {{ 0, 0, 0, OLIVETTI_VENDOR, 0xf000}, MSG_OLIVETTI_2}, {{ 0, 0, 0, OPTION_VENDOR, 0x6711}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6731}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6751}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6771}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6791}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6811}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6911}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6951}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x6971}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7011}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7031}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7051}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7111}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7211}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7251}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7271}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7301}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7311}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7361}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7381}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7401}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7501}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7601}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7701}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7801}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x7901}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x8200}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x8201}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x8300}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x8302}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x8304}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0x8400}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xc031}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xc100}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xc031}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd013}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd031}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd033}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd035}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd055}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd057}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd058}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd155}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd157}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd255}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd257}, MSG_OPTION_1}, {{ 0, 0, 0, OPTION_VENDOR, 0xd357}, MSG_OPTION_1}, {{ 0, 0, 0, ATHEROS_VENDOR, 0x20ff}, MSG_ATHEROS_1}, }; static uint32 kDevicesCount = sizeof(kDevices) / sizeof(kDevices[0]); typedef struct _my_device { usb_device device; bool removed; mutex lock; struct _my_device *link; // device state usb_pipe bulk_in; usb_pipe bulk_out; uint8 interface; uint8 alternate_setting; // used to store callback information sem_id notify; status_t status; size_t actual_length; msgType type; } my_device; int32 api_version = B_CUR_DRIVER_API_VERSION; static usb_module_info *gUSBModule = NULL; static my_device *gDeviceList = NULL; static uint32 gDeviceCount = 0; static mutex gDeviceListLock; // //#pragma mark - Device Allocation Helper Functions // static void my_free_device(my_device *device) { mutex_lock(&device->lock); mutex_destroy(&device->lock); delete_sem(device->notify); free(device); } // //#pragma mark - Bulk-only Functions // static void my_callback(void *cookie, status_t status, void *data, size_t actualLength) { my_device *device = (my_device *)cookie; device->status = status; device->actual_length = actualLength; release_sem(device->notify); } static status_t my_transfer_data(my_device *device, bool directionIn, void *data, size_t dataLength) { status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in : device->bulk_out, data, dataLength, my_callback, device); if (result != B_OK) { TRACE_ALWAYS("failed to queue data transfer\n"); return result; } do { bigtime_t timeout = directionIn ? 100000 : 100000; result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, timeout); if (result == B_TIMED_OUT) { // Cancel the transfer and collect the sem that should now be // released through the callback on cancel. Handling of device // reset is done in usb_printer_operation() when it detects that // the transfer failed. gUSBModule->cancel_queued_transfers(directionIn ? device->bulk_in : device->bulk_out); acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0); } } while (result == B_INTERRUPTED); if (result != B_OK) { TRACE_ALWAYS("acquire_sem failed while waiting for data transfer\n"); return result; } return B_OK; } enum msgType my_get_msg_type(const usb_device_descriptor *desc) { for (uint32 i = 0; i < kDevicesCount; i++) { if (kDevices[i].desc.dev_class != 0x0 && kDevices[i].desc.dev_class != desc->device_class) continue; if (kDevices[i].desc.dev_subclass != 0x0 && kDevices[i].desc.dev_subclass != desc->device_subclass) continue; if (kDevices[i].desc.dev_protocol != 0x0 && kDevices[i].desc.dev_protocol != desc->device_protocol) continue; if (kDevices[i].desc.vendor != 0x0 && kDevices[i].desc.vendor != desc->vendor_id) continue; if (kDevices[i].desc.product != 0x0 && kDevices[i].desc.product != desc->product_id) continue; return kDevices[i].type; } return MSG_NONE; } status_t my_modeswitch(my_device* device) { if (device->type == MSG_NONE) return B_OK; status_t err = my_transfer_data(device, false, kDevicesMsg[device->type], sizeof(kDevicesMsg[device->type])); if (err != B_OK) { TRACE_ALWAYS("inquire message failed\n"); return err; } TRACE("device switched: %p\n", device); char data[36]; err = my_transfer_data(device, true, data, sizeof(data)); if (err != B_OK) { TRACE_ALWAYS("inquire response failed\n"); return err; } TRACE("device switched: %p %.8s %.16s %.4s\n", device, data + 8, data + 16, data + 32); return B_OK; } // //#pragma mark - Device Attach/Detach Notifications and Callback // static status_t my_device_added(usb_device newDevice, void **cookie) { TRACE("device_added(0x%08lx)\n", newDevice); my_device *device = (my_device *)malloc(sizeof(my_device)); device->device = newDevice; device->removed = false; device->interface = 0xff; device->alternate_setting = 0; // scan through the interfaces to find our bulk-only data interface const usb_configuration_info *configuration = gUSBModule->get_configuration(newDevice); if (configuration == NULL) { free(device); return B_ERROR; } for (size_t i = 0; i < configuration->interface_count; i++) { usb_interface_info *interface = configuration->interface[i].active; if (interface == NULL) continue; if (true) { bool hasIn = false; bool hasOut = false; for (size_t j = 0; j < interface->endpoint_count; j++) { usb_endpoint_info *endpoint = &interface->endpoint[j]; if (endpoint == NULL || endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK) continue; if (!hasIn && (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)) { device->bulk_in = endpoint->handle; hasIn = true; } else if (!hasOut && (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) == 0) { device->bulk_out = endpoint->handle; hasOut = true; } if (hasIn && hasOut) break; } if (!(hasIn && hasOut)) continue; device->interface = interface->descr->interface_number; device->alternate_setting = interface->descr->alternate_setting; break; } } if (device->interface == 0xff) { TRACE_ALWAYS("no valid interface found\n"); free(device); return B_ERROR; } const usb_device_descriptor *descriptor = gUSBModule->get_device_descriptor(newDevice); if (descriptor == NULL) { free(device); return B_ERROR; } device->type = my_get_msg_type(descriptor); mutex_init(&device->lock, DRIVER_NAME " device lock"); device->notify = create_sem(0, DRIVER_NAME " callback notify"); if (device->notify < B_OK) { mutex_destroy(&device->lock); free(device); return device->notify; } mutex_lock(&gDeviceListLock); device->link = gDeviceList; gDeviceList = device; mutex_unlock(&gDeviceListLock); *cookie = device; return my_modeswitch(device); } static status_t my_device_removed(void *cookie) { TRACE("device_removed(0x%08lx)\n", (uint32)cookie); my_device *device = (my_device *)cookie; mutex_lock(&gDeviceListLock); if (gDeviceList == device) { gDeviceList = device->link; } else { my_device *element = gDeviceList; while (element) { if (element->link == device) { element->link = device->link; break; } element = element->link; } } gDeviceCount--; device->removed = true; gUSBModule->cancel_queued_transfers(device->bulk_in); gUSBModule->cancel_queued_transfers(device->bulk_out); my_free_device(device); mutex_unlock(&gDeviceListLock); return B_OK; } // //#pragma mark - Driver Entry Points // status_t init_hardware() { TRACE("init_hardware()\n"); return B_OK; } status_t init_driver() { TRACE("init_driver()\n"); static usb_notify_hooks notifyHooks = { &my_device_added, &my_device_removed }; gDeviceList = NULL; gDeviceCount = 0; mutex_init(&gDeviceListLock, DRIVER_NAME " device list lock"); TRACE("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_ALWAYS("getting module failed 0x%08lx\n", result); mutex_destroy(&gDeviceListLock); return result; } size_t descriptorsSize = kDevicesCount * sizeof(usb_support_descriptor); usb_support_descriptor *supportedDevices = (usb_support_descriptor *)malloc(descriptorsSize); if (supportedDevices == NULL) { TRACE_ALWAYS("descriptor allocation failed\n"); put_module(B_USB_MODULE_NAME); mutex_destroy(&gDeviceListLock); return result; } for (uint32 i = 0; i < kDevicesCount; i++) supportedDevices[i] = kDevices[i].desc; gUSBModule->register_driver(DRIVER_NAME, supportedDevices, kDevicesCount, NULL); gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); free(supportedDevices); return B_OK; } void uninit_driver() { TRACE("uninit_driver()\n"); gUSBModule->uninstall_notify(DRIVER_NAME); mutex_lock(&gDeviceListLock); mutex_destroy(&gDeviceListLock); put_module(B_USB_MODULE_NAME); } const char ** publish_devices() { TRACE("publish_devices()\n"); return NULL; } device_hooks * find_device(const char *name) { TRACE("find_device()\n"); return NULL; }