/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 by Christian Gmeiner * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include "usb_storage.h" #include "usbstack/utils.h" /*-------------------------------------------------------------------------*/ static struct usb_dcd_controller_ops* ops; struct usb_device_driver usb_storage_driver = { .name = "storage", .bind = usb_storage_driver_bind, .unbind = usb_storage_driver_unbind, .request = usb_storage_driver_request, .suspend = NULL, .resume = NULL, .speed = usb_storage_driver_speed, }; /*-------------------------------------------------------------------------*/ #define PROTO_BULK 0x50 // Bulk only #define SUBCL_SCSI 0x06 // Transparent SCSI /* Bulk-only class specific requests */ #define USB_BULK_RESET_REQUEST 0xff #define USB_BULK_GET_MAX_LUN_REQUEST 0xfe /*-------------------------------------------------------------------------*/ /* usb descriptors */ #define MANUFACTURER_STR_ID 1 #define PRODUCT_STR_ID 2 #define SERIAL_STR_ID 3 #define CONFIG_STR_ID 4 #define DATA_STR_ID 5 /* static strings, in UTF-8 */ static struct usb_string strings[] = { { MANUFACTURER_STR_ID, "RockBox" }, { PRODUCT_STR_ID, "RockBox Storage Driver" }, { SERIAL_STR_ID, "0" }, { CONFIG_STR_ID, "Storage Bulk" }, { DATA_STR_ID, "Storage Data" }, }; static struct usb_device_descriptor storage_device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .idVendor = 0xffff, .idProduct = 0x0001, .iManufacturer = MANUFACTURER_STR_ID, .iProduct = PRODUCT_STR_ID, .iSerialNumber = SERIAL_STR_ID, .bNumConfigurations = 1, }; static struct usb_config_descriptor storage_config_desc = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = CONFIG_STR_ID, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 250, /* 500mA in 2mA units */ }; static struct usb_interface_descriptor storage_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_MASS_STORAGE, .bInterfaceSubClass = SUBCL_SCSI, .bInterfaceProtocol = PROTO_BULK, .iInterface = DATA_STR_ID, }; static struct usb_endpoint_descriptor storage_fs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 64, }; static struct usb_endpoint_descriptor storage_fs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 64, }; static struct usb_qualifier_descriptor storage_qualifier_desc = { .bLength = sizeof(struct usb_qualifier_descriptor), .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = 0x0200, .bDeviceClass = 0, .bNumConfigurations = 1, }; struct usb_descriptor_header *storage_fs_function[] = { (struct usb_descriptor_header *) &storage_interface_desc, (struct usb_descriptor_header *) &storage_fs_bulk_in_desc, (struct usb_descriptor_header *) &storage_fs_bulk_out_desc, NULL, }; /* USB 2.0 */ static struct usb_endpoint_descriptor storage_hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 512, }; static struct usb_endpoint_descriptor storage_hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 512, }; struct usb_descriptor_header *storage_hs_function[] = { (struct usb_descriptor_header *) &storage_interface_desc, (struct usb_descriptor_header *) &storage_hs_bulk_in_desc, (struct usb_descriptor_header *) &storage_hs_bulk_out_desc, NULL, }; #define BUFFER_SIZE 100 uint8_t buf[BUFFER_SIZE]; struct usb_response res; /* helper functions */ static int config_buf(uint8_t *buf, uint8_t type, unsigned index); static int set_config(int config); static int set_interface_alt_setting(int interface_alt_setting); struct device { struct usb_ep* in; struct usb_ep* out; struct usb_ep* intr; uint32_t used_config; uint32_t used_interface_alt_setting; struct usb_descriptor_header** descriptors; }; static struct device dev; /*-------------------------------------------------------------------------*/ void usb_storage_driver_init(void) { logf("usb storage: register"); usb_device_driver_register(&usb_storage_driver); } /*-------------------------------------------------------------------------*/ /* device driver ops */ int usb_storage_driver_bind(void* controler_ops) { ops = controler_ops; /* serach and asign endpoints */ usb_ep_autoconfig_reset(); dev.in = usb_ep_autoconfig(&storage_fs_bulk_in_desc); if (!dev.in) { goto autoconf_fail; } dev.in->claimed = true; logf("usb storage: in: %s", dev.in->name); dev.out = usb_ep_autoconfig(&storage_fs_bulk_out_desc); if (!dev.out) { goto autoconf_fail; } dev.out->claimed = true; logf("usb storage: out: %s", dev.out->name); /* update device decsriptor */ storage_device_desc.bMaxPacketSize0 = ops->ep0->maxpacket; /* update hs descriptors as we asume that endpoints are the same for fs and hs */ storage_hs_bulk_in_desc.bEndpointAddress = storage_fs_bulk_in_desc.bEndpointAddress; storage_hs_bulk_out_desc.bEndpointAddress = storage_fs_bulk_out_desc.bEndpointAddress; return 0; autoconf_fail: logf("failed to find endpoints"); return -EOPNOTSUPP; } void usb_storage_driver_unbind(void) { /* disable endpoints... */ } int usb_storage_driver_request(struct usb_ctrlrequest* request) { int ret = -EOPNOTSUPP; logf("usb storage: request"); res.length = 0; res.buf = NULL; switch (request->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: switch (request->bRequest) { case USB_REQ_GET_DESCRIPTOR: switch (request->wValue >> 8) { case USB_DT_DEVICE: logf("usb storage: sending device desc"); ret = MIN(sizeof(struct usb_device_descriptor), request->wLength); res.buf = &storage_device_desc; break; case USB_DT_DEVICE_QUALIFIER: logf("usb storage: sending qualifier dec"); ret = MIN(sizeof(struct usb_qualifier_descriptor), request->wLength); res.buf = &storage_qualifier_desc; break; case USB_DT_OTHER_SPEED_CONFIG: case USB_DT_CONFIG: logf("usb storage: sending config desc"); ret = config_buf(buf, request->wValue >> 8, request->wValue & 0xff); if (ret >= 0) { logf("%d, vs %d", request->wLength, ret); ret = MIN(request->wLength, (uint16_t)ret); } res.buf = buf; break; case USB_DT_STRING: logf("usb storage: sending string desc"); ret = usb_stack_get_string(strings, request->wValue & 0xff, buf); ret = MIN(ret, request->wLength); res.buf = buf; break; } break; case USB_REQ_SET_CONFIGURATION: logf("usb storage: set configuration %d", request->wValue); ret = set_config(request->wValue); break; case USB_REQ_GET_CONFIGURATION: logf("usb storage: get configuration"); ret = 1; res.buf = &dev.used_config; break; case USB_REQ_GET_INTERFACE: logf("usb storage: get interface"); ret = 1; res.buf = &dev.used_interface_alt_setting; break; case USB_REQ_SET_INTERFACE: logf("usb storage: set interface"); ret = set_interface_alt_setting(request->wValue); break; } case USB_TYPE_CLASS: switch (request->bRequest) { case USB_BULK_RESET_REQUEST: logf("usb storage: bulk reset"); break; case USB_BULK_GET_MAX_LUN_REQUEST: logf("usb storage: get max lun"); /* we support no LUNs (Logical Unit Number) */ buf[0] = 0; ret = 1; break; } break; } if (ret >= 0) { res.length = ret; ret = ops->send(NULL, &res); } return ret; } void usb_storage_driver_speed(enum usb_device_speed speed) { switch (speed) { case USB_SPEED_HIGH: logf("usb storage: using highspeed"); dev.descriptors = storage_hs_function; break; default: logf("usb storage: using fullspeed"); dev.descriptors = storage_fs_function; break; } } /*-------------------------------------------------------------------------*/ /* S/GET CONFIGURATION helpers */ static int config_buf(uint8_t *buf, uint8_t type, unsigned index) { int len; (void)index; len = usb_stack_configdesc(&storage_config_desc, buf, BUFFER_SIZE, dev.descriptors); logf("result %d", len); if (len < 0) { return len; } ((struct usb_config_descriptor *)buf)->bDescriptorType = type; return len; } static int set_config(int config) { /* enable endpoints */ logf("setup %s", dev.in->name); ops->enable(dev.in, (struct usb_endpoint_descriptor*)dev.descriptors[1]); logf("setup %s", dev.out->name); ops->enable(dev.out, (struct usb_endpoint_descriptor*)dev.descriptors[2]); dev.used_config = config; /* setup buffers */ return 0; } static int set_interface_alt_setting(int interface_alt_setting) { dev.used_interface_alt_setting = interface_alt_setting; return 0; }