diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h index bfd174f7..2bd26451 100644 --- a/arch/arm/mach-msm/include/mach/msm_hsusb.h +++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h @@ -47,6 +47,10 @@ struct msm_hsusb_platform_data { /* 1 : uart, 0 : usb */ void (*usb_uart_switch)(int); void (*config_usb_id_gpios)(bool enable); + void (*usb_hub_enable)(bool); + void (*serial_debug_gpios)(int); + int (*china_ac_detect)(void); + void (*disable_usb_charger)(void); /* val, reg pairs terminated by -1 */ int *phy_init_seq; @@ -76,8 +80,11 @@ struct msm_hsusb_platform_data { #endif char *serial_number; int usb_id_pin_gpio; + int dock_pin_gpio; + int id_pin_irq; bool enable_car_kit_detect; __u8 accessory_detect; + bool dock_detect; }; int usb_get_connect_type(void); diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index f2a3f3ee..908b8a78 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -53,59 +53,6 @@ MODULE_VERSION("1.0"); static const char longname[] = "Gadget Android"; -enum { - USB_FUNCTION_UMS = 0, - USB_FUNCTION_ADB = 1, - USB_FUNCTION_RNDIS, - USB_FUNCTION_DIAG, - USB_FUNCTION_SERIAL, - USB_FUNCTION_PROJECTOR, - USB_FUNCTION_FSYNC, - USB_FUNCTION_MTP, - USB_FUNCTION_MODEM, -}; - -#ifdef CONFIG_USB_ANDROID_MTP -#define MS_VENDOR_CODE 0x0b -#define FEATURE_DESC_SIZE 64 -#define PID_MTP 0x0c93 -#define PID_MTP_ADB 0x0ca8 -struct ms_comp_feature_descriptor { - __le32 dwLength; - __le16 bcdVersion; - __le16 wIndex; - __u8 bCount; - __u8 resv1[7]; - /* for MTP */ - __u8 bFirstInterfaceNumber; - __u8 resv2; - __u8 compatibleID[8]; - __u8 subCompatibleID[8]; - __u8 resv3[6]; - /* for adb */ - __u8 bFirstInterfaceNumber2; - __u8 resv4; - __u8 compatibleID2[8]; - __u8 subCompatibleID2[8]; - __u8 resv5[6]; -} __attribute__ ((packed)); - - -static struct ms_comp_feature_descriptor ms_comp_desc = { - .dwLength = __constant_cpu_to_le32(FEATURE_DESC_SIZE), - .bcdVersion = __constant_cpu_to_le16(0x0100), - .wIndex = __constant_cpu_to_le16(0x0004), - .bCount = 0x02, - /* for MTP */ - .bFirstInterfaceNumber = 0, - .resv2 = 1, - .compatibleID = "MTP", - /* for adb */ - .bFirstInterfaceNumber2 = 1, - .resv4 = 1, -}; -#endif - /* Default vendor and product IDs, overridden by platform data */ #define VENDOR_ID 0x18D1 #define PRODUCT_ID 0x0001 @@ -162,17 +109,6 @@ static struct usb_device_descriptor device_desc = { static struct list_head _functions = LIST_HEAD_INIT(_functions); static int _registered_function_count = 0; -static int get_product_id(struct android_dev *dev); - -void android_usb_set_connected(int connected) -{ - if (_android_dev && _android_dev->cdev && _android_dev->cdev->gadget) { - if (connected) - usb_gadget_connect(_android_dev->cdev->gadget); - else - usb_gadget_disconnect(_android_dev->cdev->gadget); - } -} static struct android_usb_function *get_function(const char *name) { @@ -188,7 +124,6 @@ static void bind_functions(struct android_dev *dev) { struct android_usb_function *f; char **functions = dev->functions; - int product_id; int i; for (i = 0; i < dev->num_functions; i++) { @@ -199,14 +134,9 @@ static void bind_functions(struct android_dev *dev) else printk(KERN_ERR "function %s not found in bind_functions\n", name); } - product_id = get_product_id(dev); - printk(KERN_INFO "usb: product_id=0x%x\n", product_id); - device_desc.idProduct = __constant_cpu_to_le16(product_id); - if (dev->cdev) - dev->cdev->desc.idProduct = device_desc.idProduct; } -static int __init android_bind_config(struct usb_configuration *c) +static int android_bind_config(struct usb_configuration *c) { struct android_dev *dev = _android_dev; @@ -237,46 +167,7 @@ static int android_setup_config(struct usb_configuration *c, { int i; int ret = -EOPNOTSUPP; -#ifdef CONFIG_USB_ANDROID_MTP - struct usb_composite_dev *cdev = c->cdev; - struct usb_request *req = cdev->req; - struct android_dev *dev = _android_dev; - int product_id; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_length = le16_to_cpu(ctrl->wLength); - /* vendor request to GET OS feature descriptor */ - if (((ctrl->bRequestType << 8) | ctrl->bRequest) == - (((USB_DIR_IN | USB_TYPE_VENDOR) << 8) | MS_VENDOR_CODE)) { - printk(KERN_DEBUG "usb: get OS feature descriptor\n"); - if (w_index != 0x0004 || w_length > FEATURE_DESC_SIZE) - return ret; - - product_id = get_product_id(dev); - printk(KERN_INFO "mtp: product_id = 0x%x\n", product_id); - if (product_id == PID_MTP) { - ms_comp_desc.bCount = 1; - ms_comp_desc.dwLength = - __constant_cpu_to_le32(40); - ret = w_length; - } else if (product_id == PID_MTP_ADB) { - ms_comp_desc.bCount = 2; - ms_comp_desc.dwLength = - __constant_cpu_to_le32(64); - ret = w_length; - } - /* respond with data transfer or status phase? */ - if (ret >= 0) { - memcpy(req->buf, (char *)&ms_comp_desc, w_length); - req->zero = 0; - req->length = ret; - ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (ret < 0) - ERROR(cdev, "get feature desc err %d\n", ret); - return ret; - } - } -#endif for (i = 0; i < android_config_driver.next_interface_id; i++) { if (android_config_driver.interface[i]->setup) { ret = android_config_driver.interface[i]->setup( @@ -307,7 +198,7 @@ static int product_matches_functions(struct android_usb_product *p) { struct usb_function *f; list_for_each_entry(f, &android_config_driver.functions, list) { - if (product_has_function(p, f) == !!f->hidden) + if (product_has_function(p, f) == !!f->disabled) return 0; } return 1; @@ -329,7 +220,7 @@ static int get_product_id(struct android_dev *dev) return dev->product_id; } -static int __init android_bind(struct usb_composite_dev *cdev) +static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct usb_gadget *gadget = cdev->gadget; @@ -358,9 +249,6 @@ static int __init android_bind(struct usb_composite_dev *cdev) strings_dev[STRING_SERIAL_IDX].id = id; device_desc.iSerialNumber = id; - if (gadget->ops->wakeup) - android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - /* register our configuration */ ret = usb_add_config(cdev, &android_config_driver); if (ret) { @@ -412,84 +300,9 @@ void android_register_function(struct android_usb_function *f) /* bind our functions if they have all registered * and the main driver has bound. */ - if (dev->config && _registered_function_count == dev->num_functions) + if (dev && dev->config && _registered_function_count == dev->num_functions) bind_functions(dev); } -int android_show_function(char *buf) -{ - unsigned length = 0; - struct usb_function *f; - list_for_each_entry(f, &android_config_driver.functions, list) { - length += sprintf(buf + length, "%s:%s\n", f->name, - (f->hidden)?"disable":"enable"); - } - return length; -} - -int android_switch_function(unsigned func) -{ - struct usb_function *f; - struct android_dev *dev = _android_dev; - int product_id; - printk(KERN_INFO "%s: %u\n", __func__, func); - list_for_each_entry(f, &android_config_driver.functions, list) { - if ((func & (1 << USB_FUNCTION_UMS)) && - !strcmp(f->name, "usb_mass_storage")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_ADB)) && - !strcmp(f->name, "adb")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_RNDIS)) && - !strcmp(f->name, "rndis")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_DIAG)) && - !strcmp(f->name, "diag")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_MODEM)) && - !strcmp(f->name, "modem")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_SERIAL)) && - !strcmp(f->name, "serial")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_MTP)) && - !strcmp(f->name, "mtp")) - f->hidden = 0; - /* also enable adb with MTP function */ - else if ((func & (1 << USB_FUNCTION_MTP)) && - !strcmp(f->name, "adb")) - f->hidden = 0; - else if ((func & (1 << USB_FUNCTION_PROJECTOR)) && - !strcmp(f->name, "projector")) - f->hidden = 0; - else - f->hidden = 1; - } - product_id = get_product_id(dev); - device_desc.idProduct = __constant_cpu_to_le16(product_id); - if (dev->cdev) - dev->cdev->desc.idProduct = device_desc.idProduct; - - /* We need to specify the COMM class in the device descriptor - * if we are using RNDIS. - */ - if (func & (1 << USB_FUNCTION_RNDIS)) - dev->cdev->desc.bDeviceClass = USB_CLASS_COMM; - else - dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE; - -#ifdef CONFIG_USB_GADGET_MSM_72K - msm_hsusb_request_reset(); -#else - /* force reenumeration */ - if (dev->cdev && dev->cdev->gadget && - dev->cdev->gadget->speed != USB_SPEED_UNKNOWN) { - usb_gadget_disconnect(dev->cdev->gadget); - msleep(10); - usb_gadget_connect(dev->cdev->gadget); - } -#endif - return 0; -} void android_enable_function(struct usb_function *f, int enable) { @@ -497,12 +310,12 @@ void android_enable_function(struct usb_function *f, int enable) int disable = !enable; int product_id; - if (!!f->hidden != disable) { - f->hidden = disable; + if (!!f->disabled != disable) { + usb_function_set_enabled(f, !disable); #ifdef CONFIG_USB_ANDROID_RNDIS if (!strcmp(f->name, "rndis")) { - struct usb_function *func; + struct usb_function *func; /* We need to specify the COMM class in the device descriptor * if we are using RNDIS. @@ -517,12 +330,12 @@ void android_enable_function(struct usb_function *f, int enable) dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE; /* Windows does not support other interfaces when RNDIS is enabled, - * so we disable UMS when RNDIS is on. + * so we disable UMS and MTP when RNDIS is on. */ list_for_each_entry(func, &android_config_driver.functions, list) { - if (!strcmp(func->name, "usb_mass_storage")) { - func->hidden = enable; - break; + if (!strcmp(func->name, "usb_mass_storage") + || !strcmp(func->name, "mtp")) { + usb_function_set_enabled(func, !enable); } } } @@ -532,20 +345,10 @@ void android_enable_function(struct usb_function *f, int enable) device_desc.idProduct = __constant_cpu_to_le16(product_id); if (dev->cdev) dev->cdev->desc.idProduct = device_desc.idProduct; - -#ifdef CONFIG_USB_GADGET_MSM_72K - msm_hsusb_request_reset(); -#else - /* force reenumeration */ - if (dev->cdev && dev->cdev->gadget && - dev->cdev->gadget->speed != USB_SPEED_UNKNOWN) { - usb_gadget_disconnect(dev->cdev->gadget); - msleep(10); - usb_gadget_connect(dev->cdev->gadget); - } -#endif + usb_composite_force_reset(dev->cdev); } } + void android_set_serialno(char *serialno) { strings_dev[STRING_SERIAL_IDX].s = serialno; @@ -557,8 +360,7 @@ int android_get_model_id(void) return dev->product_id; } - -static int __init android_probe(struct platform_device *pdev) +static int android_probe(struct platform_device *pdev) { struct android_usb_platform_data *pdata = pdev->dev.platform_data; struct android_dev *dev = _android_dev; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index aeec3900..4b21f63d 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -36,7 +36,7 @@ */ /* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 512 +#define USB_BUFSIZ 1024 static struct usb_composite_driver *composite; @@ -75,7 +75,7 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_function *f = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", !f->hidden); + return sprintf(buf, "%d\n", !f->disabled); } static ssize_t enable_store( @@ -90,13 +90,36 @@ static ssize_t enable_store( if (driver->enable_function) driver->enable_function(f, value); else - f->hidden = !value; + usb_function_set_enabled(f, value); return size; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); +void usb_function_set_enabled(struct usb_function *f, int enabled) +{ + f->disabled = !enabled; + kobject_uevent(&f->dev->kobj, KOBJ_CHANGE); +} + + +void usb_composite_force_reset(struct usb_composite_dev *cdev) +{ + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + /* force reenumeration */ + if (cdev && cdev->gadget && cdev->gadget->speed != USB_SPEED_UNKNOWN) { + spin_unlock_irqrestore(&cdev->lock, flags); + + usb_gadget_disconnect(cdev->gadget); + msleep(10); + usb_gadget_connect(cdev->gadget); + } else { + spin_unlock_irqrestore(&cdev->lock, flags); + } +} /** * usb_add_function() - add a function to a configuration @@ -112,7 +135,7 @@ static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); * This function returns the value of the function's bind(), which is * zero for success else a negative errno value. */ -int __init usb_add_function(struct usb_configuration *config, +int usb_add_function(struct usb_configuration *config, struct usb_function *function) { struct usb_composite_dev *cdev = config->cdev; @@ -257,7 +280,7 @@ int usb_function_activate(struct usb_function *function) * Returns the interface ID which was allocated; or -ENODEV if no * more interface IDs can be allocated. */ -int __init usb_interface_id(struct usb_configuration *config, +int usb_interface_id(struct usb_configuration *config, struct usb_function *function) { unsigned id = config->next_interface_id; @@ -270,34 +293,6 @@ int __init usb_interface_id(struct usb_configuration *config, return -ENODEV; } -static struct usb_function *get_function_by_intf(struct usb_composite_dev *cdev, - int intf) -{ - struct usb_configuration *config = cdev->config; - enum usb_device_speed speed = cdev->gadget->speed; - struct usb_function *f; - struct usb_descriptor_header *descriptor; - struct usb_interface_descriptor *intf_desc; - int i; - for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { - f = config->interface[i]; - if (!f) - return NULL; - if (f->hidden) - continue; - - if (speed == USB_SPEED_HIGH) - descriptor = *(f->hs_descriptors); - else - descriptor = *(f->descriptors); - intf_desc = (struct usb_interface_descriptor *)descriptor; - if (intf_desc->bDescriptorType == USB_DT_INTERFACE && - intf_desc->bInterfaceNumber == intf) - return f; - } - return NULL; -} - static int config_buf(struct usb_configuration *config, enum usb_device_speed speed, void *buf, u8 type) { @@ -339,7 +334,7 @@ static int config_buf(struct usb_configuration *config, descriptors = f->hs_descriptors; else descriptors = f->descriptors; - if (f->hidden || !descriptors || descriptors[0] == NULL) + if (f->disabled || !descriptors || descriptors[0] == NULL) continue; status = usb_descriptor_fillbuf(next, len, (const struct usb_descriptor_header **) descriptors); @@ -351,9 +346,11 @@ static int config_buf(struct usb_configuration *config, while ((descriptor = *descriptors++) != NULL) { intf = (struct usb_interface_descriptor *)dest; if (intf->bDescriptorType == USB_DT_INTERFACE) { - intf->bInterfaceNumber = interfaceCount; - intf = (struct usb_interface_descriptor *)descriptor; - intf->bInterfaceNumber = interfaceCount++; + /* don't increment bInterfaceNumber for alternate settings */ + if (intf->bAlternateSetting == 0) + intf->bInterfaceNumber = interfaceCount++; + else + intf->bInterfaceNumber = interfaceCount - 1; } dest += intf->bLength; } @@ -460,6 +457,8 @@ static void reset_config(struct usb_composite_dev *cdev) list_for_each_entry(f, &cdev->config->functions, list) { if (f->disable) f->disable(f); + + bitmap_zero(f->endpoints, 32); } cdev->config = NULL; } @@ -505,9 +504,36 @@ static int set_config(struct usb_composite_dev *cdev, /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { struct usb_function *f = c->interface[tmp]; + struct usb_descriptor_header **descriptors; if (!f) break; + if (f->disabled) + continue; + + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + if (gadget->speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + + for (; *descriptors; ++descriptors) { + struct usb_endpoint_descriptor *ep; + int addr; + + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + set_bit(addr, f->endpoints); + } result = f->set_alt(f, tmp, 0); if (result < 0) { @@ -523,6 +549,8 @@ static int set_config(struct usb_composite_dev *cdev, power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; done: usb_gadget_vbus_draw(gadget, power); + + schedule_work(&cdev->switch_work); return result; } @@ -540,7 +568,7 @@ done: * assigns global resources including string IDs, and per-configuration * resources such as interface IDs and endpoints. */ -int __init usb_add_config(struct usb_composite_dev *cdev, +int usb_add_config(struct usb_composite_dev *cdev, struct usb_configuration *config) { int status = -EINVAL; @@ -737,7 +765,7 @@ static int get_string(struct usb_composite_dev *cdev, * ensure that for example different functions don't wrongly assign * different meanings to the same identifier. */ -int __init usb_string_id(struct usb_composite_dev *cdev) +int usb_string_id(struct usb_composite_dev *cdev) { if (cdev->next_string_id < 254) { /* string id 0 is reserved */ @@ -756,6 +784,7 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) "setup complete --> %d, %d/%d\n", req->status, req->actual, req->length); } + /* * The setup() callback implements all the ep0 functionality that's * not handled lower down, in hardware or the hardware driver(like @@ -774,6 +803,15 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; + u8 endp; + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + if (!cdev->connected) { + cdev->connected = 1; + schedule_work(&cdev->switch_work); + } + spin_unlock_irqrestore(&cdev->lock, flags); /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion @@ -817,6 +855,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); + + /* Allow functions to handle USB_DT_STRING. + * This is required for MTP. + */ + if (value < 0) { + struct usb_configuration *cfg; + list_for_each_entry(cfg, &cdev->configs, list) { + if (cfg && cfg->setup) { + value = cfg->setup(cfg, ctrl); + if (value >= 0) + break; + } + } + } + if (value >= 0) value = min(w_length, (u16) value); break; @@ -857,7 +910,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; - f = get_function_by_intf(cdev, intf); + f = cdev->config->interface[intf]; if (!f) break; if (w_value && !f->set_alt) @@ -869,7 +922,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; - f = get_function_by_intf(cdev, intf); + f = cdev->config->interface[intf]; if (!f) break; /* lots of interfaces only need altsetting zero... */ @@ -886,26 +939,36 @@ unknown: ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); - /* functions always handle their interfaces ... punt other - * recipients (endpoint, other, WUSB, ...) to the current + /* functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current * configuration code. * * REVISIT it could make sense to let the composite device * take such requests too, if that's ever needed: to work * in config 0, etc. */ - if ((ctrl->bRequestType & USB_RECIP_MASK) - == USB_RECIP_INTERFACE) { + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: if (cdev->config == NULL) return value; - f = get_function_by_intf(cdev, intf); - if (f && f->setup) - value = f->setup(f, ctrl); - else + f = cdev->config->interface[intf]; + break; + + case USB_RECIP_ENDPOINT: + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + if (&f->list == &cdev->config->functions) f = NULL; + break; } - if (value < 0 && !f) { + + if (f && f->setup) + value = f->setup(f, ctrl); + else { struct usb_configuration *c; c = cdev->config; @@ -963,13 +1026,27 @@ static void composite_disconnect(struct usb_gadget *gadget) spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) reset_config(cdev); + + cdev->connected = 0; + schedule_work(&cdev->switch_work); spin_unlock_irqrestore(&cdev->lock, flags); } /*-------------------------------------------------------------------------*/ -static void /* __init_or_exit */ -composite_unbind(struct usb_gadget *gadget) +static ssize_t composite_show_suspended(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + return sprintf(buf, "%d\n", cdev->suspended); +} + +static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL); + +static void composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); @@ -1012,13 +1089,16 @@ composite_unbind(struct usb_gadget *gadget) kfree(cdev->req->buf); usb_ep_free_request(gadget->ep0, cdev->req); } + + switch_dev_unregister(&cdev->sw_connected); + switch_dev_unregister(&cdev->sw_config); kfree(cdev); set_gadget_data(gadget, NULL); + device_remove_file(&gadget->dev, &dev_attr_suspended); composite = NULL; } -static void __init -string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +static void string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) { struct usb_string *str = tab->strings; @@ -1030,8 +1110,7 @@ string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) } } -static void __init -string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +static void string_override(struct usb_gadget_strings **tab, u8 id, const char *s) { while (*tab) { string_override_one(*tab, id, s); @@ -1039,7 +1118,30 @@ string_override(struct usb_gadget_strings **tab, u8 id, const char *s) } } -static int __init composite_bind(struct usb_gadget *gadget) +static void composite_switch_work(struct work_struct *data) +{ + struct usb_composite_dev *cdev = + container_of(data, struct usb_composite_dev, switch_work); + struct usb_configuration *config = cdev->config; + int connected; + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->connected != cdev->sw_connected.state) { + connected = cdev->connected; + spin_unlock_irqrestore(&cdev->lock, flags); + switch_set_state(&cdev->sw_connected, connected); + } else { + spin_unlock_irqrestore(&cdev->lock, flags); + } + + if (config) + switch_set_state(&cdev->sw_config, config->bConfigurationValue); + else + switch_set_state(&cdev->sw_config, 0); +} + +static int composite_bind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; int status = -ENOMEM; @@ -1074,6 +1176,14 @@ static int __init composite_bind(struct usb_gadget *gadget) */ usb_ep_autoconfig_reset(cdev->gadget); + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + /* composite gadget needs to assign strings for whole device (like * serial number), register function drivers, potentially update * power state and consumption, etc @@ -1082,17 +1192,19 @@ static int __init composite_bind(struct usb_gadget *gadget) if (status < 0) goto fail; + cdev->sw_connected.name = "usb_connected"; + status = switch_dev_register(&cdev->sw_connected); + if (status < 0) + goto fail; + cdev->sw_config.name = "usb_configuration"; + status = switch_dev_register(&cdev->sw_config); + if (status < 0) + goto fail; + INIT_WORK(&cdev->switch_work, composite_switch_work); + cdev->desc = *composite->dev; cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - /* standardized runtime overrides for device ID data */ - if (idVendor) - cdev->desc.idVendor = cpu_to_le16(idVendor); - if (idProduct) - cdev->desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) - cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); - /* strings can't be assigned before bind() allocates the * releavnt identifiers */ @@ -1106,6 +1218,10 @@ static int __init composite_bind(struct usb_gadget *gadget) string_override(composite->strings, cdev->desc.iSerialNumber, iSerialNumber); + status = device_create_file(&gadget->dev, &dev_attr_suspended); + if (status) + goto fail; + INFO(cdev, "%s ready\n", composite->name); return 0; @@ -1116,8 +1232,7 @@ fail: /*-------------------------------------------------------------------------*/ -static void -composite_suspend(struct usb_gadget *gadget) +static void composite_suspend(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_function *f; @@ -1134,10 +1249,11 @@ composite_suspend(struct usb_gadget *gadget) } if (composite->suspend) composite->suspend(cdev); + + cdev->suspended = 1; } -static void -composite_resume(struct usb_gadget *gadget) +static void composite_resume(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_function *f; @@ -1154,6 +1270,24 @@ composite_resume(struct usb_gadget *gadget) f->resume(f); } } + + cdev->suspended = 0; +} + +static int composite_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct usb_function *f = dev_get_drvdata(dev); + + if (!f) { + /* this happens when the device is first created */ + return 0; + } + + if (add_uevent_var(env, "FUNCTION=%s", f->name)) + return -ENOMEM; + if (add_uevent_var(env, "ENABLED=%d", !f->disabled)) + return -ENOMEM; + return 0; } /*-------------------------------------------------------------------------*/ @@ -1162,7 +1296,7 @@ static struct usb_gadget_driver composite_driver = { .speed = USB_SPEED_HIGH, .bind = composite_bind, - .unbind = __exit_p(composite_unbind), + .unbind = composite_unbind, .setup = composite_setup, .disconnect = composite_disconnect, @@ -1190,7 +1324,7 @@ static struct usb_gadget_driver composite_driver = { * while it was binding. That would usually be done in order to wait for * some userspace participation. */ -int __init usb_composite_register(struct usb_composite_driver *driver) +int usb_composite_register(struct usb_composite_driver *driver) { if (!driver || !driver->dev || !driver->bind || composite) return -EINVAL; @@ -1204,6 +1338,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver) driver->class = class_create(THIS_MODULE, "usb_composite"); if (IS_ERR(driver->class)) return PTR_ERR(driver->class); + driver->class->dev_uevent = composite_uevent; return usb_gadget_register_driver(&composite_driver); } @@ -1215,7 +1350,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver) * This function is used to unregister drivers using the composite * driver framework. */ -void __exit usb_composite_unregister(struct usb_composite_driver *driver) +void usb_composite_unregister(struct usb_composite_driver *driver) { if (composite != driver) return; diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index d963d7f7..ca82e7b8 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -71,7 +71,6 @@ static void TRACE(const char *tag, const void *_data, int len, int decode) #endif #define HDLC_MAX 4096 -#define SMD_MAX 8192 #define TX_REQ_BUF_SZ 8192 #define RX_REQ_BUF_SZ 8192 @@ -88,8 +87,6 @@ struct diag_context struct usb_ep *in; struct list_head tx_req_idle; struct list_head rx_req_idle; - struct list_head rx_arm9_idle; - struct list_head rx_arm9_done; spinlock_t req_lock; #if ROUTE_TO_USERSPACE struct mutex user_lock; @@ -106,11 +103,12 @@ struct diag_context unsigned char id_table[ID_TABLE_SZ]; #endif smd_channel_t *ch; - struct mutex smd_lock; int in_busy; +#ifdef CONFIG_ARCH_QSD8X50 + smd_channel_t *ch_dsp; + int in_busy_dsp; +#endif int online; - int error; - int init_done; /* assembly buffer for USB->A9 HDLC frames */ unsigned char hdlc_buf[HDLC_MAX]; @@ -121,22 +119,6 @@ struct diag_context u64 rx_count; /* from smd */ int function_enable; - -#if defined(CONFIG_MSM_N_WAY_SMD) - smd_channel_t *chqdsp; - struct list_head tx_qdsp_idle; -#endif - /* for slate test */ - int is2ARM11; - struct mutex diag2arm9_lock; - struct mutex diag2arm9_read_lock; - struct mutex diag2arm9_write_lock; - bool diag2arm9_opened; - unsigned char toARM9_buf[SMD_MAX]; - unsigned read_arm9_count; - unsigned char *read_arm9_buf; - wait_queue_head_t read_arm9_wq; - struct usb_request *read_arm9_req; }; static struct usb_interface_descriptor diag_interface_desc = { @@ -193,23 +175,6 @@ static struct usb_descriptor_header *hs_diag_descs[] = { NULL, }; -/* string descriptors: */ - -static struct usb_string diag_string_defs[] = { - [0].s = "HTC DIAG", - { } /* end of list */ -}; - -static struct usb_gadget_strings diag_string_table = { - .language = 0x0409, /* en-us */ - .strings = diag_string_defs, -}; - -static struct usb_gadget_strings *diag_strings[] = { - &diag_string_table, - NULL, -}; - static struct diag_context _context; static inline struct diag_context *func_to_dev(struct usb_function *f) @@ -218,42 +183,11 @@ static inline struct diag_context *func_to_dev(struct usb_function *f) } static void smd_try_to_send(struct diag_context *ctxt); -static void smd_diag_notify(void *priv, unsigned event); - -static void diag_queue_out(struct diag_context *ctxt); -#if defined(CONFIG_MSM_N_WAY_SMD) -static void diag_qdsp_complete_in(struct usb_ep *ept, - struct usb_request *req); +#ifdef CONFIG_ARCH_QSD8X50 +static void dsp_try_to_send(struct diag_context *ctxt); #endif -static struct usb_request *diag_req_new(unsigned len) -{ - struct usb_request *req; - if (len > SMD_MAX) - return NULL; - req = kmalloc(sizeof(struct usb_request), GFP_KERNEL); - if (!req) - return NULL; - req->buf = kmalloc(len, GFP_KERNEL); - if (!req->buf) { - kfree(req); - return NULL; - } - return req; -} - -static void diag_req_free(struct usb_request *req) -{ - if (!req) - return; - - if (req->buf) { - kfree(req->buf); - req->buf = 0; - } - kfree(req); - req = 0; -} +static void diag_queue_out(struct diag_context *ctxt); /* add a request to the tail of a list */ static void req_put(struct diag_context *ctxt, struct list_head *head, @@ -293,82 +227,29 @@ static void reqs_free(struct diag_context *ctxt, struct usb_ep *ep, } } -static void smd_diag_enable(char *src, int enable) -{ - struct diag_context *ctxt = &_context; - - printk(KERN_INFO "smd_try_open(%s): %d\n", src, enable); - if (!ctxt->init_done) - return; - - mutex_lock(&ctxt->smd_lock); - if (enable) { - if (!ctxt->ch) - smd_open("SMD_DIAG", &ctxt->ch, ctxt, smd_diag_notify); - } else { - if (ctxt->ch) { - smd_close(ctxt->ch); - ctxt->ch = NULL; - } - } - mutex_unlock(&ctxt->smd_lock); - -} - - #if ROUTE_TO_USERSPACE #define USB_DIAG_IOC_MAGIC 0xFF -#define USB_DIAG_FUNC_IOC_ENABLE_SET _IOW(USB_DIAG_IOC_MAGIC, 1, int) -#define USB_DIAG_FUNC_IOC_ENABLE_GET _IOR(USB_DIAG_IOC_MAGIC, 2, int) -#define USB_DIAG_FUNC_IOC_REGISTER_SET _IOW(USB_DIAG_IOC_MAGIC, 3, char *) -#define USB_DIAG_FUNC_IOC_AMR_SET _IOW(USB_DIAG_IOC_MAGIC, 4, int) +#define USB_DIAG_FUNC_IOC_REGISTER_SET _IOW(USB_DIAG_IOC_MAGIC, 3, char *) static long diag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct diag_context *ctxt = &_context; void __user *argp = (void __user *)arg; - int tmp_value; unsigned long flags; unsigned char temp_id_table[ID_TABLE_SZ]; - printk(KERN_INFO "diag:diag_ioctl() cmd=%d\n", cmd); - - if (_IOC_TYPE(cmd) != USB_DIAG_IOC_MAGIC) - return -ENOTTY; - - switch (cmd) { - case USB_DIAG_FUNC_IOC_ENABLE_SET: - if (copy_from_user(&tmp_value, argp, sizeof(int))) - return -EFAULT; - printk(KERN_INFO "diag: enable %d\n", tmp_value); - android_enable_function(&_context.function, tmp_value); - smd_diag_enable("diag_ioctl", tmp_value); - /* force diag_read to return error when disable diag */ - if (tmp_value == 0) - ctxt->error = 1; - wake_up(&ctxt->read_wq); - break; - case USB_DIAG_FUNC_IOC_ENABLE_GET: - tmp_value = !_context.function.hidden; - if (copy_to_user(argp, &tmp_value, sizeof(tmp_value))) - return -EFAULT; - break; - - case USB_DIAG_FUNC_IOC_REGISTER_SET: - if (copy_from_user(temp_id_table, (unsigned char *)argp, ID_TABLE_SZ)) - return -EFAULT; - spin_lock_irqsave(&ctxt->req_lock, flags); - memcpy(ctxt->id_table, temp_id_table, ID_TABLE_SZ); - spin_unlock_irqrestore(&ctxt->req_lock, flags); - break; - case USB_DIAG_FUNC_IOC_AMR_SET: - if (copy_from_user(&ctxt->is2ARM11, argp, sizeof(int))) - return -EFAULT; - printk(KERN_INFO "diag: is2ARM11 %d\n", ctxt->is2ARM11); - break; - default: - return -ENOTTY; + if (cmd != USB_DIAG_FUNC_IOC_REGISTER_SET) { + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + return -EINVAL; } + + if (copy_from_user(temp_id_table, (unsigned char *)argp, ID_TABLE_SZ)) + return -EFAULT; + + spin_lock_irqsave(&ctxt->req_lock, flags); + memcpy(ctxt->id_table, temp_id_table, ID_TABLE_SZ); + spin_unlock_irqrestore(&ctxt->req_lock, flags); + return 0; } @@ -379,13 +260,6 @@ static ssize_t diag_read(struct file *fp, char __user *buf, struct usb_request *req = 0; int ret = 0; - /* we will block until we're online */ - if (!ctxt->online) { - ret = wait_event_interruptible(ctxt->read_wq, (ctxt->online || ctxt->error)); - if (ret < 0 || ctxt->error) - return -EFAULT; - } - mutex_lock(&ctxt->user_lock); if (ctxt->user_read_len && ctxt->user_readp) { @@ -411,7 +285,7 @@ static ssize_t diag_read(struct file *fp, char __user *buf, goto end; } if (!ctxt->online) { - /* pr_err("%s: offline\n", __func__); */ + pr_err("%s: offline\n", __func__); ret = -EIO; goto end; } @@ -483,8 +357,6 @@ static ssize_t diag_write(struct file *fp, const char __user *buf, } ret = req->length; - /* zero this so we don't put it back to idle queue */ - req = 0; } end: @@ -516,8 +388,6 @@ static int diag_open(struct inode *ip, struct file *fp) } } ctxt->opened = true; - /* clear the error latch */ - ctxt->error = 0; done: mutex_unlock(&ctxt->user_lock); @@ -557,174 +427,6 @@ static struct miscdevice diag_device_fops = { }; #endif -static int diag2arm9_open(struct inode *ip, struct file *fp) -{ - struct diag_context *ctxt = &_context; - struct usb_request *req; - int rc = 0; - int n; - printk(KERN_INFO "%s\n", __func__); - mutex_lock(&ctxt->diag2arm9_lock); - if (ctxt->diag2arm9_opened) { - pr_err("%s: already opened\n", __func__); - rc = -EBUSY; - goto done; - } - /* clear pending data if any */ - while ((req = req_get(ctxt, &ctxt->rx_arm9_done))) - diag_req_free(req); - - for (n = 0; n < 4; n++) { - req = diag_req_new(SMD_MAX); - if (!req) { - while ((req = req_get(ctxt, &ctxt->rx_arm9_idle))) - diag_req_free(req); - rc = -EFAULT; - goto done; - } - req_put(ctxt, &ctxt->rx_arm9_idle, req); - } - ctxt->read_arm9_count = 0; - ctxt->read_arm9_buf = 0; - ctxt->read_arm9_req = 0; - ctxt->diag2arm9_opened = true; - - smd_diag_enable("diag2arm9_open", 1); -done: - mutex_unlock(&ctxt->diag2arm9_lock); - return rc; -} - -static int diag2arm9_release(struct inode *ip, struct file *fp) -{ - struct diag_context *ctxt = &_context; - struct usb_request *req; - - printk(KERN_INFO "%s\n", __func__); - mutex_lock(&ctxt->diag2arm9_lock); - ctxt->diag2arm9_opened = false; - wake_up(&ctxt->read_arm9_wq); - mutex_lock(&ctxt->diag2arm9_read_lock); - while ((req = req_get(ctxt, &ctxt->rx_arm9_idle))) - diag_req_free(req); - while ((req = req_get(ctxt, &ctxt->rx_arm9_done))) - diag_req_free(req); - if (ctxt->read_arm9_req) - diag_req_free(ctxt->read_arm9_req); - mutex_unlock(&ctxt->diag2arm9_read_lock); - - /************************************* - * If smd is closed, it will lead to slate can't be tested. - * slate will open it for one time - * but close it for several times and never open - *************************************/ - /*smd_diag_enable("diag2arm9_release", 0);*/ - mutex_unlock(&ctxt->diag2arm9_lock); - - return 0; -} - -static ssize_t diag2arm9_write(struct file *fp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct diag_context *ctxt = &_context; - int r = count; - int writed = 0; - - mutex_lock(&ctxt->diag2arm9_write_lock); - - while (count > 0) { - writed = count > SMD_MAX ? SMD_MAX : count; - if (copy_from_user(ctxt->toARM9_buf, buf, writed)) { - r = -EFAULT; - break; - } - if (ctxt->ch == NULL) { - printk(KERN_ERR "%s: ctxt->ch == NULL", __func__); - r = -EFAULT; - break; - } else if (ctxt->toARM9_buf == NULL) { - printk(KERN_ERR "%s: ctxt->toARM9_buf == NULL", __func__); - r = -EFAULT; - break; - } - smd_write(ctxt->ch, ctxt->toARM9_buf, writed); - buf += writed; - count -= writed; - } - - mutex_unlock(&ctxt->diag2arm9_write_lock); - return r; -} - -static ssize_t diag2arm9_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos) -{ - struct diag_context *ctxt = &_context; - struct usb_request *req; - int r = 0, xfer; - int ret; - - mutex_lock(&ctxt->diag2arm9_read_lock); - - /* if we have data pending, give it to userspace */ - if (ctxt->read_arm9_count > 0) - req = ctxt->read_arm9_req; - else { -retry: - /* get data from done queue */ - req = 0; - ret = wait_event_interruptible(ctxt->read_arm9_wq, - ((req = req_get(ctxt, &ctxt->rx_arm9_done)) || - !ctxt->diag2arm9_opened)); - if (!ctxt->diag2arm9_opened) { - if (req) - req_put(ctxt, &ctxt->rx_arm9_idle, req); - goto done; - } - if (ret < 0 || req == 0) - goto done; - - if (req->actual == 0) { - req_put(ctxt, &ctxt->rx_arm9_idle, req); - goto retry; - } - ctxt->read_arm9_req = req; - ctxt->read_arm9_count = req->actual; - ctxt->read_arm9_buf = req->buf; - } - xfer = (ctxt->read_arm9_count < count) ? ctxt->read_arm9_count : count; - if (copy_to_user(buf, ctxt->read_arm9_buf, xfer)) { - printk(KERN_INFO "diag: copy_to_user fail\n"); - r = -EFAULT; - goto done; - } - ctxt->read_arm9_buf += xfer; - ctxt->read_arm9_count -= xfer; - r += xfer; - /* if we've emptied the buffer, release the request */ - if (ctxt->read_arm9_count == 0) { - req_put(ctxt, &ctxt->rx_arm9_idle, ctxt->read_arm9_req); - ctxt->read_arm9_req = 0; - } -done: - mutex_unlock(&ctxt->diag2arm9_read_lock); - return r; -} -static struct file_operations diag2arm9_fops = { - .owner = THIS_MODULE, - .open = diag2arm9_open, - .release = diag2arm9_release, - .write = diag2arm9_write, - .read = diag2arm9_read, -}; - -static struct miscdevice diag2arm9_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "diag_arm9", - .fops = &diag2arm9_fops, -}; - static void diag_in_complete(struct usb_ep *ept, struct usb_request *req) { struct diag_context *ctxt = req->context; @@ -744,7 +446,19 @@ static void diag_in_complete(struct usb_ep *ept, struct usb_request *req) smd_try_to_send(ctxt); } -#if !NO_HDLC +#ifdef CONFIG_ARCH_QSD8X50 +static void diag_dsp_in_complete(struct usb_ep *ept, struct usb_request *req) +{ + struct diag_context *ctxt = req->context; + + ctxt->in_busy_dsp = 0; + req_put(ctxt, &ctxt->tx_req_idle, req); + dsp_try_to_send(ctxt); + wake_up(&ctxt->write_wq); +} +#endif + +#if 0 static void diag_process_hdlc(struct diag_context *ctxt, void *_data, unsigned len) { unsigned char *data = _data; @@ -871,27 +585,8 @@ again: int r = smd_read_avail(ctxt->ch); if (r > TX_REQ_BUF_SZ) { - printk(KERN_ERR "The SMD data is too large to send!!\n"); return; } - if (r > 0 && ctxt->is2ARM11) { - /* to arm11 user space */ - struct usb_request *req; - if (!ctxt->diag2arm9_opened) - return; - req = req_get(ctxt, &ctxt->rx_arm9_idle); - if (!req) { - printk(KERN_ERR "There is no enough request to ARM11!!\n"); - return; - } - smd_read(ctxt->ch, req->buf, r); - req->actual = r; - req_put(ctxt, &ctxt->rx_arm9_done, req); - wake_up(&ctxt->read_arm9_wq); - return; - } - if (!ctxt->online) - return; if (r > 0) { struct usb_request *req; req = req_get(ctxt, &ctxt->tx_req_idle); @@ -931,6 +626,55 @@ static void smd_diag_notify(void *priv, unsigned event) smd_try_to_send(ctxt); } +#ifdef CONFIG_ARCH_QSD8X50 +static void dsp_try_to_send(struct diag_context *ctxt) +{ +again: + if (ctxt->ch_dsp && (!ctxt->in_busy_dsp)) { + int r = smd_read_avail(ctxt->ch_dsp); + + if (r > TX_REQ_BUF_SZ) { + return; + } + if (r > 0) { + struct usb_request *req; + req = req_get(ctxt, &ctxt->tx_req_idle); + if (!req) { + pr_err("%s: tx req queue is out of buffers\n", + __func__); + return; + } + smd_read(ctxt->ch_dsp, req->buf, r); + + if (!ctxt->online) { +// printk("$$$ discard %d\n", r); + req_put(ctxt, &ctxt->tx_req_idle, req); + goto again; + } + req->complete = diag_dsp_in_complete; + req->context = ctxt; + req->length = r; + + TRACE("Q6>", req->buf, r, 1); + ctxt->in_busy_dsp = 1; + r = usb_ep_queue(ctxt->in, req, GFP_ATOMIC); + if (r < 0) { + pr_err("%s: usb_ep_queue failed: %d\n", + __func__, r); + req_put(ctxt, &ctxt->tx_req_idle, req); + ctxt->in_busy_dsp = 0; + } + } + } +} + +static void dsp_diag_notify(void *priv, unsigned event) +{ + struct diag_context *ctxt = priv; + dsp_try_to_send(ctxt); +} +#endif + static int __init create_bulk_endpoints(struct diag_context *ctxt, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) @@ -945,14 +689,12 @@ static int __init create_bulk_endpoints(struct diag_context *ctxt, DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); return -ENODEV; } - ep->driver_data = ctxt; /* claim the endpoint */ ctxt->in = ep; ep = usb_ep_autoconfig(cdev->gadget, out_desc); if (!ep) { return -ENODEV; } - ep->driver_data = ctxt; /* claim the endpoint */ ctxt->out = ep; ctxt->tx_count = ctxt->rx_count = 0; @@ -991,30 +733,8 @@ static int __init create_bulk_endpoints(struct diag_context *ctxt, req_put(ctxt, &ctxt->tx_req_idle, req); } -#if defined(CONFIG_MSM_N_WAY_SMD) - for (n = 0; n < TX_REQ_NUM; n++) { - req = usb_ep_alloc_request(ctxt->in, GFP_KERNEL); - if (!req) { - DBG(cdev, "%s: usb_ep_alloc_request out of memory\n", - __func__); - goto qdsp_tx_fail; - } - req->buf = kmalloc(TX_REQ_BUF_SZ, GFP_KERNEL); - if (!req->buf) { - DBG(cdev, "%s: kmalloc out of memory\n", __func__); - goto qdsp_tx_fail; - } - req->context = ctxt; - req->complete = diag_qdsp_complete_in; - req_put(ctxt, &ctxt->tx_qdsp_idle, req); - } -#endif - return 0; -#if defined(CONFIG_MSM_N_WAY_SMD) -qdsp_tx_fail: - reqs_free(ctxt, ctxt->in, &ctxt->tx_qdsp_idle); -#endif + tx_fail: reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle); rx_fail: @@ -1055,7 +775,6 @@ diag_function_bind(struct usb_configuration *c, struct usb_function *f) #if ROUTE_TO_USERSPACE misc_register(&diag_device_fops); #endif - misc_register(&diag2arm9_device); return 0; } @@ -1070,7 +789,6 @@ diag_function_unbind(struct usb_configuration *c, struct usb_function *f) #if ROUTE_TO_USERSPACE misc_deregister(&diag_device_fops); #endif - misc_deregister(&diag2arm9_device); ctxt->tx_count = ctxt->rx_count = 0; } @@ -1099,7 +817,7 @@ static int diag_function_set_alt(struct usb_function *f, usb_ep_disable(ctxt->in); return ret; } - ctxt->online = !ctxt->function.hidden; + ctxt->online = 1; #if ROUTE_TO_USERSPACE /* recycle unhandled rx reqs to user if any */ @@ -1107,10 +825,12 @@ static int diag_function_set_alt(struct usb_function *f, req_put(ctxt, &ctxt->rx_req_idle, req); #endif - if (ctxt->online) { - diag_queue_out(ctxt); - smd_try_to_send(ctxt); - } + diag_queue_out(ctxt); + smd_try_to_send(ctxt); +#ifdef CONFIG_ARCH_QSD8X50 + dsp_try_to_send(ctxt); +#endif + #if ROUTE_TO_USERSPACE wake_up(&ctxt->read_wq); wake_up(&ctxt->write_wq); @@ -1132,78 +852,12 @@ static void diag_function_disable(struct usb_function *f) usb_ep_disable(ctxt->out); } -#if defined(CONFIG_MSM_N_WAY_SMD) -static void diag_qdsp_send(struct diag_context *ctxt) -{ - int ret, r; - struct usb_request *req; - if (ctxt->chqdsp && ctxt->online) { - r = smd_read_avail(ctxt->chqdsp); - if (r > SMD_MAX || r <= 0) - return; - - req = req_get(ctxt, &ctxt->tx_qdsp_idle); - if (!req) - return; - - smd_read(ctxt->chqdsp, req->buf, r); - req->length = r; - - ret = usb_ep_queue(ctxt->in, req, GFP_ATOMIC); - if (ret < 0) { - printk(KERN_INFO "diag: failed to queue qdsp req %d\n", - ret); - req_put(ctxt, &ctxt->tx_qdsp_idle, req); - } - } -} - -static void diag_qdsp_complete_in(struct usb_ep *ept, - struct usb_request *req) -{ - struct diag_context *ctxt = req->context; - - req_put(ctxt, &ctxt->tx_qdsp_idle, req); - diag_qdsp_send(ctxt); - -#if ROUTE_TO_USERSPACE - wake_up(&ctxt->write_wq); -#endif -} - - -static void diag_qdsp_notify(void *priv, unsigned event) -{ - struct diag_context *ctxt = priv; - diag_qdsp_send(ctxt); -} - -static int msm_diag_probe(struct platform_device *pdev) -{ - struct diag_context *ctxt = &_context; - printk(KERN_INFO "diag:msm_diag_probe(), pdev->id=0x%x\n", pdev->id); - - if (pdev->id == 1) - smd_open("DSP_DIAG", &ctxt->chqdsp, ctxt, diag_qdsp_notify); - return 0; -} - -static struct platform_driver msm_smd_qdsp_ch1_driver = { - .probe = msm_diag_probe, - .driver = { - .name = "DSP_DIAG", - .owner = THIS_MODULE, - }, -}; -#endif - static int diag_set_enabled(const char *val, struct kernel_param *kp) { int enabled = simple_strtol(val, NULL, 0); if (_context.cdev) android_enable_function(&_context.function, enabled); _context.function_enable = !!enabled; - smd_diag_enable("diag_set_enabled", enabled); return 0; } @@ -1218,7 +872,7 @@ module_param_call(tx_rx_count, NULL, diag_get_tx_rx_count, NULL, 0444); static int diag_get_enabled(char *buffer, struct kernel_param *kp) { - buffer[0] = '0' + !_context.function.hidden; + buffer[0] = '0' + !_context.function.disabled; return 1; } module_param_call(enabled, diag_set_enabled, diag_get_enabled, NULL, 0664); @@ -1231,15 +885,20 @@ int diag_bind_config(struct usb_configuration *c) printk(KERN_INFO "diag_bind_config\n"); - ret = usb_string_id(c->cdev); - if (ret < 0) + ret = smd_open("SMD_DIAG", &ctxt->ch, ctxt, smd_diag_notify); + if (ret) return ret; - diag_string_defs[0].id = ret; - diag_interface_desc.iInterface = ret; + +#ifdef CONFIG_ARCH_QSD8X50 + ret = smd_open("DSP_DIAG", &ctxt->ch_dsp, ctxt, dsp_diag_notify); + if (ret) { + pr_err("%s: smd_open failed (DSP_DIAG)\n", __func__); + return ret; + } +#endif ctxt->cdev = c->cdev; ctxt->function.name = "diag"; - ctxt->function.strings = diag_strings; ctxt->function.descriptors = fs_diag_descs; ctxt->function.hs_descriptors = hs_diag_descs; ctxt->function.bind = diag_function_bind; @@ -1247,9 +906,7 @@ int diag_bind_config(struct usb_configuration *c) ctxt->function.set_alt = diag_function_set_alt; ctxt->function.disable = diag_function_disable; - ctxt->function.hidden = !_context.function_enable; - if (!ctxt->function.hidden) - smd_diag_enable("diag_bind_config", 1); + ctxt->function.disabled = !_context.function_enable; return usb_add_function(c, &ctxt->function); } @@ -1267,26 +924,12 @@ static int __init init(void) spin_lock_init(&ctxt->req_lock); INIT_LIST_HEAD(&ctxt->rx_req_idle); INIT_LIST_HEAD(&ctxt->tx_req_idle); - INIT_LIST_HEAD(&ctxt->rx_arm9_idle); - INIT_LIST_HEAD(&ctxt->rx_arm9_done); #if ROUTE_TO_USERSPACE mutex_init(&ctxt->user_lock); INIT_LIST_HEAD(&ctxt->rx_req_user); init_waitqueue_head(&ctxt->read_wq); init_waitqueue_head(&ctxt->write_wq); #endif - init_waitqueue_head(&ctxt->read_arm9_wq); - mutex_init(&ctxt->diag2arm9_lock); - mutex_init(&ctxt->diag2arm9_read_lock); - mutex_init(&ctxt->diag2arm9_write_lock); - mutex_init(&ctxt->smd_lock); - ctxt->is2ARM11 = 0; - -#if defined(CONFIG_MSM_N_WAY_SMD) - INIT_LIST_HEAD(&ctxt->tx_qdsp_idle); - platform_driver_register(&msm_smd_qdsp_ch1_driver); -#endif - ctxt->init_done = 1; android_register_function(&diag_function); return 0; diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 39fc2f17..a0b0774b 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -31,13 +31,11 @@ #include #include -#include #define BULK_BUFFER_SIZE 4096 /* number of tx requests to allocate */ #define TX_REQ_MAX 4 -#define RX_REQ_MAX 32 static const char shortname[] = "android_adb"; @@ -57,18 +55,11 @@ struct adb_dev { atomic_t open_excl; struct list_head tx_idle; - struct list_head rx_idle; - struct list_head rx_done; wait_queue_head_t read_wq; wait_queue_head_t write_wq; - - /* the request we're currently reading from */ - struct usb_request *read_req; - unsigned char *read_buf; - unsigned read_count; - - int maxsize; + struct usb_request *rx_req; + int rx_done; }; static struct usb_interface_descriptor adb_interface_desc = { @@ -125,22 +116,6 @@ static struct usb_descriptor_header *hs_adb_descs[] = { NULL, }; -/* string descriptors: */ - -static struct usb_string adb_string_defs[] = { - [0].s = "ADB", - { } /* end of list */ -}; - -static struct usb_gadget_strings adb_string_table = { - .language = 0x0409, /* en-us */ - .strings = adb_string_defs, -}; - -static struct usb_gadget_strings *adb_strings[] = { - &adb_string_table, - NULL, -}; /* temporary variable used between adb_open() and adb_gadget_bind() */ static struct adb_dev *_adb_dev; @@ -236,11 +211,9 @@ static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) { struct adb_dev *dev = _adb_dev; - if (req->status != 0) { + dev->rx_done = 1; + if (req->status != 0) dev->error = 1; - req_put(dev, &dev->rx_idle, req); - } else - req_put(dev, &dev->rx_done, req); wake_up(&dev->read_wq); } @@ -275,13 +248,11 @@ static int __init create_bulk_endpoints(struct adb_dev *dev, dev->ep_out = ep; /* now allocate requests for our endpoints */ - for (i = 0; i < RX_REQ_MAX; i++) { - req = adb_request_new(dev->ep_out, 512); - if (!req) - goto fail; - req->complete = adb_complete_out; - req_put(dev, &dev->rx_idle, req); - } + req = adb_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_out; + dev->rx_req = req; for (i = 0; i < TX_REQ_MAX; i++) { req = adb_request_new(dev->ep_in, BULK_BUFFER_SIZE); @@ -329,71 +300,41 @@ static ssize_t adb_read(struct file *fp, char __user *buf, r = -EIO; goto done; } - while (count > 0) { - if (dev->error) { - r = -EIO; - break; - } - /* if we have idle read requests, get them queued */ - while ((req = req_get(dev, &dev->rx_idle))) { requeue_req: - req->length = dev->maxsize?dev->maxsize:512; - ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); - if (ret < 0) { - printk(KERN_INFO "adb_read: failed to queue req (%d)\n", ret); - r = -EIO; - dev->error = 1; - req_put(dev, &dev->rx_idle, req); - goto done; - } - } - - /* if we have data pending, give it to userspace */ - if (dev->read_count > 0) { - xfer = (dev->read_count < count) ? dev->read_count : count; - - if (copy_to_user(buf, dev->read_buf, xfer)) { - r = -EFAULT; - break; - } - dev->read_buf += xfer; - dev->read_count -= xfer; - buf += xfer; - count -= xfer; - - /* if we've emptied the buffer, release the request */ - if (dev->read_count == 0) { - req_put(dev, &dev->rx_idle, dev->read_req); - dev->read_req = 0; - } - continue; - } - - /* wait for a request to complete */ - req = 0; - ret = wait_event_interruptible(dev->read_wq, - ((req = req_get(dev, &dev->rx_done)) || dev->error)); - - if (req != 0) { - /* if we got a 0-len one we need to put it back into - ** service. if we made it the current read req we'd - ** be stuck forever - */ - if (req->actual == 0) - goto requeue_req; - - dev->read_req = req; - dev->read_count = req->actual; - dev->read_buf = req->buf; - } - - if (ret < 0) { - r = ret; - break; - } + /* queue a request */ + req = dev->rx_req; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); + if (ret < 0) { + DBG(cdev, "adb_read: failed to queue req %p (%d)\n", req, ret); + r = -EIO; + dev->error = 1; + goto done; + } else { + DBG(cdev, "rx %p queue\n", req); } + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + dev->error = 1; + r = ret; + goto done; + } + if (!dev->error) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + DBG(cdev, "rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + done: _unlock(&dev->read_excl); DBG(cdev, "adb_read returning %d\n", r); @@ -580,10 +521,7 @@ adb_function_unbind(struct usb_configuration *c, struct usb_function *f) spin_lock_irq(&dev->lock); - while ((req = req_get(dev, &dev->rx_done))) - adb_request_free(req, dev->ep_out); - while ((req = req_get(dev, &dev->rx_idle))) - adb_request_free(req, dev->ep_out); + adb_request_free(dev->rx_req, dev->ep_out); while ((req = req_get(dev, &dev->tx_idle))) adb_request_free(req, dev->ep_in); @@ -603,7 +541,6 @@ static int adb_function_set_alt(struct usb_function *f, struct adb_dev *dev = func_to_dev(f); struct usb_composite_dev *cdev = f->config->cdev; int ret; - struct usb_request *req; DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt); ret = usb_ep_enable(dev->ep_in, @@ -620,17 +557,7 @@ static int adb_function_set_alt(struct usb_function *f, usb_ep_disable(dev->ep_in); return ret; } - if (cdev->gadget->speed == USB_SPEED_FULL) - dev->maxsize = 64; - else - dev->maxsize = 512; - printk(KERN_INFO "%s: maxsize = %d\n", __func__, dev->maxsize); - - /* retire any completed rx requests from previous session */ - while ((req = req_get(dev, &dev->rx_done))) - req_put(dev, &dev->rx_idle, req); - - dev->online = !dev->function.hidden; + dev->online = 1; /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); @@ -645,7 +572,6 @@ static void adb_function_disable(struct usb_function *f) DBG(cdev, "adb_function_disable\n"); dev->online = 0; dev->error = 1; - dev->maxsize = 0; usb_ep_disable(dev->ep_in); usb_ep_disable(dev->ep_out); @@ -676,27 +602,18 @@ static int adb_bind_config(struct usb_configuration *c) atomic_set(&dev->write_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); - INIT_LIST_HEAD(&dev->rx_idle); - INIT_LIST_HEAD(&dev->rx_done); - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - adb_string_defs[0].id = ret; - adb_interface_desc.iInterface = ret; dev->cdev = c->cdev; dev->function.name = "adb"; - dev->function.strings = adb_strings; dev->function.descriptors = fs_adb_descs; dev->function.hs_descriptors = hs_adb_descs; dev->function.bind = adb_function_bind; dev->function.unbind = adb_function_unbind; dev->function.set_alt = adb_function_set_alt; dev->function.disable = adb_function_disable; - dev->maxsize = 512; - if (board_mfg_mode() != 2) - dev->function.hidden = 1; + /* start disabled */ + dev->function.disabled = 1; /* _adb_dev must be set before calling usb_gadget_register_driver */ _adb_dev = dev; diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 17b5c400..33166787 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1,15 +1,9 @@ /* - * drivers/usb/gadget/f_mass_storage.c + * f_mass_storage.c -- Mass Storage USB Composite Function * - * Function Driver for USB Mass Storage - * - * Copyright (C) 2008 Google, Inc. - * Author: Mike Lockwood - * - * Based heavily on the file_storage gadget driver in - * drivers/usb/gadget/file_storage.c and licensed under the same terms: - * - * Copyright (C) 2003-2007 Alan Stern + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,7 +37,237 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* #define DEBUG */ + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs). Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only. Moreover, the + * function can indicate that LUN is removable and/or CD-ROM. (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * + * lun_name_format A printf-like format for names of the LUN + * devices. This determines how the + * directory in sysfs will be named. + * Unless you are using several MSFs in + * a single gadget (as opposed to single + * MSF in many configurations) you may + * leave it as NULL (in which case + * "lun%d" will be used). In the format + * you can use "%d" to index LUNs for + * MSF's with more than one LUN. (Beware + * that there is only one integer given + * as an argument for the format and + * specifying invalid format may cause + * unspecified behaviour.) + * thread_name Name of the kernel thread process used by the + * MSF. You can safely set it to NULL + * (in which case default "file-storage" + * will be used). + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters. If gadget using it + * decides to use it, the following module parameters will be + * available: + * + * file=filename[,filename...] + * Names of the files or block devices used for + * backing storage. + * ro=b[,b...] Default false, boolean for read-only access. + * removable=b[,b...] + * Default true, boolean for removable media. + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive. + * luns=N Default N = number of filenames, number of + * LUNs to support. + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints. + * + * The module parameters may be prefixed with some string. You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed. The memory requirement amounts to two 16K buffers, size + * configurable by a parameter. Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * + * The pathnames of the backing files and the ro settings are + * available in the attribute files "file" and "ro" in the lun (or + * to be more precise in a directory which name comes from + * "lun_name_format" option!) subdirectory of the gadget's sysfs + * directory. If the "removable" option is set, writing to these + * files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the + * ro setting are not allowed when the medium is loaded or if CD-ROM + * emulation is being used. + * + * When a LUN receive an "eject" SCSI request (Start/Stop Unit), + * if the LUN is removable, the backing file is released to simulate + * ejection. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at . + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * . + */ + + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + + /* #define VERBOSE_DEBUG */ /* #define DUMP_MSGS */ @@ -63,563 +287,263 @@ #include #include #include -#include #include #include -#include -#include -#include -#include #include -#include -#include +#include #include "gadget_chips.h" +#ifdef CONFIG_USB_ANDROID_MASS_STORAGE +#include +#include -#define BULK_BUFFER_SIZE 16384 +#define FUNCTION_NAME "usb_mass_storage" +#endif -/*-------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------*/ -#define DRIVER_NAME "usb_mass_storage" -#define MAX_LUNS 8 +#define FSG_DRIVER_DESC "Mass Storage Function" +#define FSG_DRIVER_VERSION "2009/09/11" -static const char shortname[] = DRIVER_NAME; +static const char fsg_string_interface[] = "Mass Storage"; -#ifdef DEBUG -#define LDBG(lun, fmt, args...) \ - dev_dbg(&(lun)->dev , fmt , ## args) -#define MDBG(fmt,args...) \ - printk(KERN_DEBUG DRIVER_NAME ": " fmt , ## args) -#else -#define LDBG(lun, fmt, args...) \ - do { } while (0) -#define MDBG(fmt,args...) \ - do { } while (0) -#undef VERBOSE_DEBUG -#undef DUMP_MSGS -#endif /* DEBUG */ -#ifdef VERBOSE_DEBUG -#define VLDBG LDBG -#else -#define VLDBG(lun, fmt, args...) \ - do { } while (0) -#endif /* VERBOSE_DEBUG */ +#define FSG_NO_INTR_EP 1 +#define FSG_NO_DEVICE_STRINGS 1 +#define FSG_NO_OTG 1 +#define FSG_NO_INTR_EP 1 -#define LERROR(lun, fmt, args...) \ - dev_err(&(lun)->dev , fmt , ## args) -#define LWARN(lun, fmt, args...) \ - dev_warn(&(lun)->dev , fmt , ## args) -#define LINFO(lun, fmt, args...) \ - dev_info(&(lun)->dev , fmt , ## args) - -#define MINFO(fmt,args...) \ - printk(KERN_INFO DRIVER_NAME ": " fmt , ## args) - -#undef DBG -#undef VDBG -#undef ERROR -#undef WARNING -#undef INFO -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->cdev->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->cdev->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->cdev->gadget->dev , fmt , ## args) -#define WARNING(d, fmt, args...) \ - dev_warn(&(d)->cdev->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->cdev->gadget->dev , fmt , ## args) +#include "storage_common.c" /*-------------------------------------------------------------------------*/ -/* Bulk-only data structures */ - -/* Command Block Wrapper */ -struct bulk_cb_wrap { - __le32 Signature; /* Contains 'USBC' */ - u32 Tag; /* Unique per command id */ - __le32 DataTransferLength; /* Size of the data */ - u8 Flags; /* Direction in bit 7 */ - u8 Lun; /* LUN (normally 0) */ - u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ - u8 CDB[16]; /* Command Data Block */ -}; - -#define USB_BULK_CB_WRAP_LEN 31 -#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ -#define USB_BULK_IN_FLAG 0x80 - -/* Command Status Wrapper */ -struct bulk_cs_wrap { - __le32 Signature; /* Should = 'USBS' */ - u32 Tag; /* Same as original command */ - __le32 Residue; /* Amount not transferred */ - u8 Status; /* See below */ -}; - -#define USB_BULK_CS_WRAP_LEN 13 -#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ -#define USB_STATUS_PASS 0 -#define USB_STATUS_FAIL 1 -#define USB_STATUS_PHASE_ERROR 2 - -/* Bulk-only class specific requests */ -#define USB_BULK_RESET_REQUEST 0xff -#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe - -/* Length of a SCSI Command Data Block */ -#define MAX_COMMAND_SIZE 16 - -/* SCSI commands that we recognize */ -#define SC_FORMAT_UNIT 0x04 -#define SC_INQUIRY 0x12 -#define SC_MODE_SELECT_6 0x15 -#define SC_MODE_SELECT_10 0x55 -#define SC_MODE_SENSE_6 0x1a -#define SC_MODE_SENSE_10 0x5a -#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e -#define SC_READ_6 0x08 -#define SC_READ_10 0x28 -#define SC_READ_12 0xa8 -#define SC_READ_CAPACITY 0x25 -#define SC_READ_FORMAT_CAPACITIES 0x23 -#define SC_READ_HEADER 0x44 -#define SC_READ_TOC 0x43 -#define SC_RELEASE 0x17 -#define SC_REQUEST_SENSE 0x03 -#define SC_RESERVE 0x16 -#define SC_SEND_DIAGNOSTIC 0x1d -#define SC_START_STOP_UNIT 0x1b -#define SC_SYNCHRONIZE_CACHE 0x35 -#define SC_TEST_UNIT_READY 0x00 -#define SC_VERIFY 0x2f -#define SC_WRITE_6 0x0a -#define SC_WRITE_10 0x2a -#define SC_WRITE_12 0xaa - -/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ -#define SS_NO_SENSE 0 -#define SS_COMMUNICATION_FAILURE 0x040800 -#define SS_INVALID_COMMAND 0x052000 -#define SS_INVALID_FIELD_IN_CDB 0x052400 -#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 -#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 -#define SS_MEDIUM_NOT_PRESENT 0x023a00 -#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 -#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 -#define SS_RESET_OCCURRED 0x062900 -#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 -#define SS_UNRECOVERED_READ_ERROR 0x031100 -#define SS_WRITE_ERROR 0x030c02 -#define SS_WRITE_PROTECTED 0x072700 - -#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ -#define ASC(x) ((u8) ((x) >> 8)) -#define ASCQ(x) ((u8) (x)) +struct fsg_dev; -/*-------------------------------------------------------------------------*/ - -struct lun { - struct file *filp; - loff_t file_length; - loff_t num_sectors; - - unsigned int ro : 1; - unsigned int prevent_medium_removal : 1; - unsigned int registered : 1; - unsigned int info_valid : 1; - unsigned int cdrom : 1; - - u32 sense_data; - u32 sense_data_info; - u32 unit_attention_data; - - struct device dev; -}; - -#define backing_file_is_open(curlun) ((curlun)->filp != NULL) - - -static struct lun *dev_to_lun(struct device *dev) -{ - return container_of(dev, struct lun, dev); -} - -/* Big enough to hold our biggest descriptor */ -#define EP0_BUFSIZE 256 - -/* Number of buffers we will use. 2 is enough for double-buffering */ -#define NUM_BUFFERS 4 - -enum fsg_buffer_state { - BUF_STATE_EMPTY = 0, - BUF_STATE_FULL, - BUF_STATE_BUSY -}; - -struct fsg_buffhd { - void *buf; - enum fsg_buffer_state state; - struct fsg_buffhd *next; - - /* The NetChip 2280 is faster, and handles some protocol faults - * better, if we don't submit any short bulk-out read requests. - * So we will record the intended request length here. */ - unsigned int bulk_out_intended_length; - - struct usb_request *inreq; - int inreq_busy; - struct usb_request *outreq; - int outreq_busy; -}; - -enum fsg_state { - /* This one isn't used anywhere */ - FSG_STATE_COMMAND_PHASE = -10, - - FSG_STATE_DATA_PHASE, - FSG_STATE_STATUS_PHASE, - - FSG_STATE_IDLE = 0, - FSG_STATE_ABORT_BULK_OUT, - FSG_STATE_RESET, - FSG_STATE_CONFIG_CHANGE, - FSG_STATE_EXIT, - FSG_STATE_TERMINATED -}; - -enum data_direction { - DATA_DIR_UNKNOWN = 0, - DATA_DIR_FROM_HOST, - DATA_DIR_TO_HOST, - DATA_DIR_NONE -}; - -struct fsg_dev { - struct usb_function function; - struct usb_composite_dev *cdev; - - /* optional "usb_mass_storage" platform device */ - struct platform_device *pdev; - - /* lock protects: state and all the req_busy's */ - spinlock_t lock; +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct fsg_dev *fsg, *new_fsg; + wait_queue_head_t fsg_wait; /* filesem protects: backing files in use */ struct rw_semaphore filesem; - /* reference counting: wait until all LUNs are released */ - struct kref ref; + /* lock protects: state, all the req_busy's */ + spinlock_t lock; - unsigned int bulk_out_maxpacket; - enum fsg_state state; /* For exception handling */ - - u8 config, new_config; - u8 ums_state; - - unsigned int running : 1; - unsigned int bulk_in_enabled : 1; - unsigned int bulk_out_enabled : 1; - unsigned int phase_error : 1; - unsigned int short_packet_received : 1; - unsigned int bad_lun_okay : 1; - - unsigned long atomic_bitflags; -#define REGISTERED 0 -#define CLEAR_BULK_HALTS 1 -#define SUSPENDED 2 - - struct usb_ep *bulk_in; - struct usb_ep *bulk_out; + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + const char *ep0req_name; struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[NUM_BUFFERS]; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + unsigned int lun; + struct fsg_lun *luns; + struct fsg_lun *curlun; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; int thread_wakeup_needed; struct completion thread_notifier; struct task_struct *thread_task; - int cmnd_size; - u8 cmnd[MAX_COMMAND_SIZE]; - enum data_direction data_dir; - u32 data_size; - u32 data_size_from_cmnd; - u32 tag; - unsigned int lun; - u32 residue; - u32 usb_amount_left; + /* Callback function to call when thread exits. */ + int (*thread_exits)(struct fsg_common *common); + /* Gadget's private data. */ + void *private_data; - unsigned int nluns; - struct lun *luns; - struct lun *curlun; + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; - u32 buf_size; - const char *vendor; - const char *product; - int release; - - struct switch_dev sdev; - - struct wake_lock wake_lock; - int cdrom_lun; + struct kref ref; }; -static inline struct fsg_dev *func_to_dev(struct usb_function *f) + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + } luns[FSG_MAX_LUNS]; + + const char *lun_name_format; + const char *thread_name; + + /* Callback function to call when thread exits. If no + * callback is set or it returns value lower then zero MSF + * will force eject all LUNs it operates on (including those + * marked as non-removable or with prevent_medium_removal flag + * set). */ + int (*thread_exits)(struct fsg_common *common); + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; + + char can_stall; + +#ifdef CONFIG_USB_ANDROID_MASS_STORAGE + struct platform_device *pdev; +#endif +}; + + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; + struct switch_dev sdev; +}; + + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) { return container_of(f, struct fsg_dev, function); } -static int exception_in_progress(struct fsg_dev *fsg) + +typedef void (*fsg_routine_t)(struct fsg_dev *); +static int send_status(struct fsg_common *common); + +static int exception_in_progress(struct fsg_common *common) { - return (fsg->state > FSG_STATE_IDLE); + return common->state > FSG_STATE_IDLE; } /* Make bulk-out requests be divisible by the maxpacket size */ -static void set_bulk_out_req_length(struct fsg_dev *fsg, +static void set_bulk_out_req_length(struct fsg_common *common, struct fsg_buffhd *bh, unsigned int length) { unsigned int rem; bh->bulk_out_intended_length = length; - rem = length % fsg->bulk_out_maxpacket; + rem = length % common->bulk_out_maxpacket; if (rem > 0) - length += fsg->bulk_out_maxpacket - rem; + length += common->bulk_out_maxpacket - rem; bh->outreq->length = length; } -static struct fsg_dev *the_fsg; - -static void close_backing_file(struct fsg_dev *fsg, struct lun *curlun); -static void close_all_backing_files(struct fsg_dev *fsg); -static void fsg_set_ums_state(int connect_status); - -static struct t_usb_status_notifier connect_status_notifier = { - .name = "connect_status", - .func = fsg_set_ums_state, -}; - /*-------------------------------------------------------------------------*/ -static void fsg_set_ums_state(int connect_status) +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) { - if (!the_fsg) - return; - printk(KERN_INFO "%s: %d\n", __func__, connect_status); - /* USB connected */ - if (connect_status == 1) { - if (!the_fsg->ums_state) { - the_fsg->ums_state = 1; - printk(KERN_INFO "ums: set state 1\n"); - switch_set_state(&the_fsg->sdev, 1); - } - } else { - if (the_fsg->ums_state) { - the_fsg->ums_state = 0; - printk(KERN_INFO "ums: set state 0\n"); - switch_set_state(&the_fsg->sdev, 0); - } - } + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); } -#ifdef DUMP_MSGS - -static void dump_msg(struct fsg_dev *fsg, const char *label, - const u8 *buf, unsigned int length) -{ - if (length < 512) { - DBG(fsg, "%s, length %u:\n", label, length); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, - 16, 1, buf, length, 0); - } -} - -static void dump_cdb(struct fsg_dev *fsg) -{} - -#else - -static void dump_msg(struct fsg_dev *fsg, const char *label, - const u8 *buf, unsigned int length) -{} - -#ifdef VERBOSE_DEBUG - -static void dump_cdb(struct fsg_dev *fsg) -{ - print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, - 16, 1, fsg->cmnd, fsg->cmnd_size, 0); -} - -#else - -static void dump_cdb(struct fsg_dev *fsg) -{} - -#endif /* VERBOSE_DEBUG */ -#endif /* DUMP_MSGS */ - - -/*-------------------------------------------------------------------------*/ - -/* Routines for unaligned data access */ - -static u16 get_be16(u8 *buf) -{ - return ((u16) buf[0] << 8) | ((u16) buf[1]); -} - -static u32 get_be32(u8 *buf) -{ - return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | - ((u32) buf[2] << 8) | ((u32) buf[3]); -} - -static void put_be16(u8 *buf, u16 val) -{ - buf[0] = val >> 8; - buf[1] = val; -} - -static void put_be32(u8 *buf, u32 val) -{ - buf[0] = val >> 24; - buf[1] = val >> 16; - buf[2] = val >> 8; - buf[3] = val & 0xff; -} - -/*-------------------------------------------------------------------------*/ - -/* - * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Also the (static) config and interface - * descriptors are adjusted during fsg_bind(). - */ - -/* There is only one interface. */ - -static struct usb_interface_descriptor -intf_desc = { - .bLength = sizeof intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - - .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = US_SC_SCSI, - .bInterfaceProtocol = US_PR_BULK, -}; - -/* Three full-speed endpoint descriptors: bulk-in, bulk-out, - * and interrupt-in. */ - -static struct usb_endpoint_descriptor -fs_bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - /* wMaxPacketSize set by autoconfiguration */ -}; - -static struct usb_endpoint_descriptor -fs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - /* wMaxPacketSize set by autoconfiguration */ -}; - -static struct usb_descriptor_header *fs_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_bulk_in_desc, - (struct usb_descriptor_header *) &fs_bulk_out_desc, - NULL, -}; -#define FS_FUNCTION_PRE_EP_ENTRIES 2 - - -static struct usb_endpoint_descriptor -hs_bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor -hs_bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - - /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), - .bInterval = 1, /* NAK every 1 uframe */ -}; - - -static struct usb_descriptor_header *hs_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_bulk_in_desc, - (struct usb_descriptor_header *) &hs_bulk_out_desc, - NULL, -}; - -/* Maxpacket and other transfer characteristics vary by speed. */ -static struct usb_endpoint_descriptor * -ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs) -{ - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} /*-------------------------------------------------------------------------*/ /* These routines may be called in process context or in_irq */ /* Caller must hold fsg->lock */ -static void wakeup_thread(struct fsg_dev *fsg) +static void wakeup_thread(struct fsg_common *common) { /* Tell the main thread that something has happened */ - fsg->thread_wakeup_needed = 1; - if (fsg->thread_task) - wake_up_process(fsg->thread_task); + common->thread_wakeup_needed = 1; + if (common->thread_task) + wake_up_process(common->thread_task); } -static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) { unsigned long flags; - DBG(fsg, "raise_exception %d\n", (int)new_state); /* Do nothing if a higher-priority exception is already in progress. * If a lower-or-equal priority exception is in progress, preempt it * and notify the main thread by sending it a signal. */ - spin_lock_irqsave(&fsg->lock, flags); - if (fsg->state <= new_state) { - fsg->state = new_state; - if (fsg->thread_task) + spin_lock_irqsave(&common->lock, flags); + if (common->state <= new_state) { + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + if (common->thread_task) send_sig_info(SIGUSR1, SEND_SIG_FORCED, - fsg->thread_task); + common->thread_task); } - spin_unlock_irqrestore(&fsg->lock, flags); + spin_unlock_irqrestore(&common->lock, flags); } +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + /*-------------------------------------------------------------------------*/ /* Bulk and interrupt endpoint completion handlers. @@ -627,107 +551,103 @@ static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = ep->driver_data; + struct fsg_common *common = ep->driver_data; struct fsg_buffhd *bh = req->context; - unsigned long flags; if (req->status || req->actual != req->length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, + DBG(common, "%s --> %d, %u/%u\n", __func__, req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ smp_wmb(); - spin_lock_irqsave(&fsg->lock, flags); + spin_lock(&common->lock); bh->inreq_busy = 0; bh->state = BUF_STATE_EMPTY; - wakeup_thread(fsg); - spin_unlock_irqrestore(&fsg->lock, flags); + wakeup_thread(common); + spin_unlock(&common->lock); } static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = ep->driver_data; + struct fsg_common *common = ep->driver_data; struct fsg_buffhd *bh = req->context; - unsigned long flags; - dump_msg(fsg, "bulk-out", req->buf, req->actual); + dump_msg(common, "bulk-out", req->buf, req->actual); if (req->status || req->actual != bh->bulk_out_intended_length) - DBG(fsg, "%s --> %d, %u/%u\n", __func__, + DBG(common, "%s --> %d, %u/%u\n", __func__, req->status, req->actual, bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ smp_wmb(); - spin_lock_irqsave(&fsg->lock, flags); + spin_lock(&common->lock); bh->outreq_busy = 0; bh->state = BUF_STATE_FULL; - wakeup_thread(fsg); - spin_unlock_irqrestore(&fsg->lock, flags); + wakeup_thread(common); + spin_unlock(&common->lock); } -static int fsg_function_setup(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { - struct fsg_dev *fsg = func_to_dev(f); - struct usb_composite_dev *cdev = fsg->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); - DBG(fsg, "fsg_function_setup\n"); - if (w_index != intf_desc.bInterfaceNumber) - return value; - /* Handle Bulk-only class-specific requests */ - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { - DBG(fsg, "USB_TYPE_CLASS\n"); - switch (ctrl->bRequest) { - case USB_BULK_RESET_REQUEST: - if (ctrl->bRequestType != (USB_DIR_OUT | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_value != 0) { - value = -EDOM; - break; - } + if (!fsg_is_set(fsg->common)) + return -EOPNOTSUPP; - /* Raise an exception to stop the current operation - * and reinitialize our state. */ - DBG(fsg, "bulk reset request\n"); - value = 0; - break; + switch (ctrl->bRequest) { - case USB_BULK_GET_MAX_LUN_REQUEST: - if (ctrl->bRequestType != (USB_DIR_IN | - USB_TYPE_CLASS | USB_RECIP_INTERFACE)) - break; - if (w_value != 0) { - value = -EDOM; - break; - } - VDBG(fsg, "get max LUN\n"); - *(u8 *)cdev->req->buf = fsg->nluns - 1; - value = 1; + case USB_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; - } + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_RESET); + return DELAYED_STATUS; + + case USB_BULK_GET_MAX_LUN_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->common->nluns - 1; + + /* Respond with data/status */ + req->length = min((u16)1, w_length); + fsg->common->ep0req_name = + ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out"; + return ep0_queue(fsg->common); } - if (value == -EOPNOTSUPP) - VDBG(fsg, - "unknown class-specific control req " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - le16_to_cpu(ctrl->wValue), w_index, w_length); - if (value >= 0) { - req->length = value; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) - req->status = 0; - } - return value; + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + le16_to_cpu(ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; } + /*-------------------------------------------------------------------------*/ /* All the following routines run in process context */ @@ -739,16 +659,14 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, enum fsg_buffer_state *state) { int rc; - unsigned long flags; - DBG(fsg, "start_transfer req: %p, req->buf: %p\n", req, req->buf); if (ep == fsg->bulk_in) dump_msg(fsg, "bulk-in", req->buf, req->length); - spin_lock_irqsave(&fsg->lock, flags); + spin_lock_irq(&fsg->common->lock); *pbusy = 1; *state = BUF_STATE_BUSY; - spin_unlock_irqrestore(&fsg->lock, flags); + spin_unlock_irq(&fsg->common->lock); rc = usb_ep_queue(ep, req, GFP_KERNEL); if (rc != 0) { *pbusy = 0; @@ -760,14 +678,23 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, * submissions if DMA is enabled. */ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) - WARN(fsg, "error in submission: %s --> %d\n", - (ep == fsg->bulk_in ? "bulk-in" : "bulk-out"), - rc); + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); } } +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ + if (fsg_is_set(common)) \ + start_transfer((common)->fsg, (common)->fsg->ep_name, \ + req, pbusy, state); \ + else -static int sleep_thread(struct fsg_dev *fsg) +#define START_TRANSFER(common, ep_name, req, pbusy, state) \ + START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + + + +static int sleep_thread(struct fsg_common *common) { int rc = 0; @@ -779,21 +706,21 @@ static int sleep_thread(struct fsg_dev *fsg) rc = -EINTR; break; } - if (fsg->thread_wakeup_needed) + if (common->thread_wakeup_needed) break; schedule(); } __set_current_state(TASK_RUNNING); - fsg->thread_wakeup_needed = 0; + common->thread_wakeup_needed = 0; return rc; } /*-------------------------------------------------------------------------*/ -static int do_read(struct fsg_dev *fsg) +static int do_read(struct fsg_common *common) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; u32 lba; struct fsg_buffhd *bh; int rc; @@ -805,15 +732,15 @@ static int do_read(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - if (fsg->cmnd[0] == SC_READ_6) - lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); + if (common->cmnd[0] == SC_READ_6) + lba = get_unaligned_be24(&common->cmnd[1]); else { - lba = get_be32(&fsg->cmnd[2]); + lba = get_unaligned_be32(&common->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = don't read from the * cache), but we don't implement them. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { + if ((common->cmnd[1] & ~0x18) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -825,7 +752,7 @@ static int do_read(struct fsg_dev *fsg) file_offset = ((loff_t) lba) << 9; /* Carry out the file reads */ - amount_left = fsg->data_size_from_cmnd; + amount_left = common->data_size_from_cmnd; if (unlikely(amount_left == 0)) return -EIO; /* No default reply */ @@ -839,8 +766,7 @@ static int do_read(struct fsg_dev *fsg) * the next page. * If this means reading 0 then we were asked to read past * the end of file. */ - amount = min((unsigned int) amount_left, - (unsigned int)fsg->buf_size); + amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t) amount, curlun->file_length - file_offset); partial_page = file_offset & (PAGE_CACHE_SIZE - 1); @@ -849,9 +775,9 @@ static int do_read(struct fsg_dev *fsg) partial_page); /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -890,7 +816,7 @@ static int do_read(struct fsg_dev *fsg) } file_offset += nread; amount_left -= nread; - fsg->residue -= nread; + common->residue -= nread; bh->inreq->length = nread; bh->state = BUF_STATE_FULL; @@ -906,9 +832,13 @@ static int do_read(struct fsg_dev *fsg) break; /* No more left to read */ /* Send this buffer and go read some more */ - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; } return -EIO; /* No default reply */ @@ -917,9 +847,9 @@ static int do_read(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int do_write(struct fsg_dev *fsg) +static int do_write(struct fsg_common *common) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; u32 lba; struct fsg_buffhd *bh; int get_some_more; @@ -934,25 +864,30 @@ static int do_write(struct fsg_dev *fsg) curlun->sense_data = SS_WRITE_PROTECTED; return -EINVAL; } + spin_lock(&curlun->filp->f_lock); curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */ + spin_unlock(&curlun->filp->f_lock); /* Get the starting Logical Block Address and check that it's * not too big */ - if (fsg->cmnd[0] == SC_WRITE_6) - lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); + if (common->cmnd[0] == SC_WRITE_6) + lba = get_unaligned_be24(&common->cmnd[1]); else { - lba = get_be32(&fsg->cmnd[2]); + lba = get_unaligned_be32(&common->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = write directly to the * medium). We don't implement DPO; we implement FUA by * performing synchronous output. */ - if ((fsg->cmnd[1] & ~0x18) != 0) { + if (common->cmnd[1] & ~0x18) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - if (fsg->cmnd[1] & 0x08) /* FUA */ + if (common->cmnd[1] & 0x08) { /* FUA */ + spin_lock(&curlun->filp->f_lock); curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); + } } if (lba >= curlun->num_sectors) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; @@ -962,12 +897,13 @@ static int do_write(struct fsg_dev *fsg) /* Carry out the file writes */ get_some_more = 1; file_offset = usb_offset = ((loff_t) lba) << 9; - amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; while (amount_left_to_write > 0) { /* Queue a request for more data from the host */ - bh = fsg->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: @@ -979,7 +915,7 @@ static int do_write(struct fsg_dev *fsg) * If this means getting 0, then we were asked * to write past the end of file. * Finally, round down to a block boundary. */ - amount = min(amount_left_to_req, (u32)fsg->buf_size); + amount = min(amount_left_to_req, FSG_BUFLEN); amount = min((loff_t) amount, curlun->file_length - usb_offset); partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); @@ -1006,28 +942,32 @@ static int do_write(struct fsg_dev *fsg) /* Get the next buffer */ usb_offset += amount; - fsg->usb_amount_left -= amount; + common->usb_amount_left -= amount; amount_left_to_req -= amount; if (amount_left_to_req == 0) get_some_more = 0; /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; continue; } /* Write the received data to the backing file */ - bh = fsg->next_buffhd_to_drain; + bh = common->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; /* We stopped early */ if (bh->state == BUF_STATE_FULL) { smp_rmb(); - fsg->next_buffhd_to_drain = bh->next; + common->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; /* Did something go wrong with the transfer? */ @@ -1066,11 +1006,11 @@ static int do_write(struct fsg_dev *fsg) LDBG(curlun, "partial file write: %d/%u\n", (int) nwritten, amount); nwritten -= (nwritten & 511); - /* Round down to a block */ + /* Round down to a block */ } file_offset += nwritten; amount_left_to_write -= nwritten; - fsg->residue -= nwritten; + common->residue -= nwritten; /* If an error occurred, report it and its position */ if (nwritten < amount) { @@ -1082,14 +1022,14 @@ static int do_write(struct fsg_dev *fsg) /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { - fsg->short_packet_received = 1; + common->short_packet_received = 1; break; } continue; } /* Wait for something to happen */ - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -1100,50 +1040,14 @@ static int do_write(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -/* Sync the file data, don't bother with the metadata. - * The caller must own fsg->filesem. - * This code was copied from fs/buffer.c:sys_fdatasync(). */ -static int fsync_sub(struct lun *curlun) +static int do_synchronize_cache(struct fsg_common *common) { - struct file *filp = curlun->filp; - struct inode *inode; - int rc, err; - - if (curlun->ro || !filp) - return 0; - if (!filp->f_op->fsync) - return -EINVAL; - - inode = filp->f_path.dentry->d_inode; - mutex_lock(&inode->i_mutex); - rc = filemap_fdatawrite(inode->i_mapping); - err = filp->f_op->fsync(filp, filp->f_path.dentry, 1); - if (!rc) - rc = err; - err = filemap_fdatawait(inode->i_mapping); - if (!rc) - rc = err; - mutex_unlock(&inode->i_mutex); - VLDBG(curlun, "fdatasync -> %d\n", rc); - return rc; -} - -static void fsync_all(struct fsg_dev *fsg) -{ - int i; - - for (i = 0; i < fsg->nluns; ++i) - fsync_sub(&fsg->luns[i]); -} - -static int do_synchronize_cache(struct fsg_dev *fsg) -{ - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; int rc; /* We ignore the requested LBA and write out all file's * dirty data buffers. */ - rc = fsync_sub(curlun); + rc = fsg_lun_fsync_sub(curlun); if (rc) curlun->sense_data = SS_WRITE_ERROR; return 0; @@ -1152,22 +1056,22 @@ static int do_synchronize_cache(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static void invalidate_sub(struct lun *curlun) +static void invalidate_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; struct inode *inode = filp->f_path.dentry->d_inode; unsigned long rc; rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); - VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); + VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc); } -static int do_verify(struct fsg_dev *fsg) +static int do_verify(struct fsg_common *common) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; u32 lba; u32 verification_length; - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; loff_t file_offset, file_offset_tmp; u32 amount_left; unsigned int amount; @@ -1175,7 +1079,7 @@ static int do_verify(struct fsg_dev *fsg) /* Get the starting Logical Block Address and check that it's * not too big */ - lba = get_be32(&fsg->cmnd[2]); + lba = get_unaligned_be32(&common->cmnd[2]); if (lba >= curlun->num_sectors) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; @@ -1183,12 +1087,12 @@ static int do_verify(struct fsg_dev *fsg) /* We allow DPO (Disable Page Out = don't save data in the * cache) but we don't implement it. */ - if ((fsg->cmnd[1] & ~0x10) != 0) { + if (common->cmnd[1] & ~0x10) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - verification_length = get_be16(&fsg->cmnd[7]); + verification_length = get_unaligned_be16(&common->cmnd[7]); if (unlikely(verification_length == 0)) return -EIO; /* No default reply */ @@ -1197,7 +1101,7 @@ static int do_verify(struct fsg_dev *fsg) file_offset = ((loff_t) lba) << 9; /* Write out all the dirty buffers before invalidating them */ - fsync_sub(curlun); + fsg_lun_fsync_sub(curlun); if (signal_pending(current)) return -EINTR; @@ -1214,8 +1118,7 @@ static int do_verify(struct fsg_dev *fsg) * And don't try to read past the end of the file. * If this means reading 0 then we were asked to read * past the end of file. */ - amount = min((unsigned int) amount_left, - (unsigned int)fsg->buf_size); + amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t) amount, curlun->file_length - file_offset); if (amount == 0) { @@ -1261,47 +1164,35 @@ static int do_verify(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) { + struct fsg_lun *curlun = common->curlun; u8 *buf = (u8 *) bh->buf; - u32 ret = 36; - if (!fsg->curlun) { /* Unsupported LUNs are okay */ - fsg->bad_lun_okay = 1; + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; memset(buf, 0, 36); buf[0] = 0x7f; /* Unsupported, no device-type */ buf[4] = 31; /* Additional length */ return 36; } - memset(buf, 0, 8); /* Non-removable, direct-access device */ - - if (fsg->curlun->cdrom) - buf[0] = 0x05; - else - buf[1] = 0x80; /* set removable bit */ + buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; buf[2] = 2; /* ANSI SCSI level 2 */ buf[3] = 2; /* SCSI-2 INQUIRY data format */ buf[4] = 31; /* Additional length */ - /* No special options */ - sprintf(buf + 8, "%-8s%-16s%04x", fsg->vendor, - fsg->product, fsg->release); - if (fsg->data_size_from_cmnd == 56) { - int model_id = android_get_model_id(); - u16 *us; - memset(buf + 36, 0, 20); - buf[4] = 51; /* Additional length */ - us = (unsigned short *)(buf + 36); - *us = __constant_cpu_to_le16(model_id); - ret = 56; - } - return ret; + buf[5] = 0; /* No special options */ + buf[6] = 0; + buf[7] = 0; + memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); + return 36; } -static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; u8 *buf = (u8 *) bh->buf; u32 sd, sdinfo; int valid; @@ -1329,7 +1220,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) #endif if (!curlun) { /* Unsupported LUNs are okay */ - fsg->bad_lun_okay = 1; + common->bad_lun_okay = 1; sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; sdinfo = 0; valid = 0; @@ -1345,7 +1236,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) memset(buf, 0, 18); buf[0] = valid | 0x70; /* Valid, current error */ buf[2] = SK(sd); - put_be32(&buf[3], sdinfo); /* Sense information */ + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ buf[7] = 18 - 8; /* Additional sense length */ buf[12] = ASC(sd); buf[13] = ASCQ(sd); @@ -1353,11 +1244,11 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } -static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; - u32 lba = get_be32(&fsg->cmnd[2]); - int pmi = fsg->cmnd[8]; + struct fsg_lun *curlun = common->curlun; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; u8 *buf = (u8 *) bh->buf; /* Check the PMI and LBA fields */ @@ -1366,38 +1257,21 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) return -EINVAL; } - put_be32(&buf[0], curlun->num_sectors - 1); /* Max logical block */ - put_be32(&buf[4], 512); /* Block length */ + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(512, &buf[4]); /* Block length */ return 8; } -static void store_cdrom_address(u8 *dest, int msf, u32 addr) -{ - if (msf) { - /* Convert to Minutes-Seconds-Frames */ - addr >>= 2; /* Convert to 2048-byte frames */ - addr += 2*75; /* Lead-in occupies 2 seconds */ - dest[3] = addr % 75; /* Frames */ - addr /= 75; - dest[2] = addr % 60; /* Seconds */ - addr /= 60; - dest[1] = addr; /* Minutes */ - dest[0] = 0; /* Reserved */ - } else { - /* Absolute sector */ - put_be32(dest, addr); - } -} -static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - u32 lba = get_be32(&fsg->cmnd[2]); + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); u8 *buf = (u8 *) bh->buf; - if ((fsg->cmnd[1] & ~0x02) != 0) { - /* Mask away MSF */ + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -1412,37 +1286,39 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) return 8; } -static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - struct lun *curlun = fsg->curlun; - int msf = fsg->cmnd[1] & 0x02; - int start_track = fsg->cmnd[6]; - u8 *buf = (u8 *) bh->buf; - if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ - start_track > 1) { - curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - return -EINVAL; +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; } memset(buf, 0, 20); - buf[1] = (20-2); /* TOC data length */ - buf[2] = 1; /* First track number */ - buf[3] = 1; /* Last track number */ - buf[5] = 0x16; /* Data track, copying allowed */ - buf[6] = 0x01; /* Only track is number 1 */ + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ store_cdrom_address(&buf[8], msf, 0); - buf[13] = 0x16; /* Lead-out track is data */ - buf[14] = 0xAA; /* Lead-out track number */ + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ store_cdrom_address(&buf[16], msf, curlun->num_sectors); return 20; } -static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; - int mscmnd = fsg->cmnd[0]; + struct fsg_lun *curlun = common->curlun; + int mscmnd = common->cmnd[0]; u8 *buf = (u8 *) bh->buf; u8 *buf0 = buf; int pc, page_code; @@ -1450,12 +1326,12 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) int valid_page = 0; int len, limit; - if ((fsg->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - pc = fsg->cmnd[2] >> 6; - page_code = fsg->cmnd[2] & 0x3f; + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; if (pc == 3) { curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; return -EINVAL; @@ -1475,14 +1351,11 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) } else { /* SC_MODE_SENSE_10 */ buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ buf += 8; - limit = 65535; + limit = 65535; /* Should really be FSG_BUFLEN */ } /* No block descriptors */ - /* Disabled to workaround USB reset problems with a Vista host. - */ -#if 0 /* The mode pages, in numerical order. The only page we support * is the Caching page. */ if (page_code == 0x08 || all_pages) { @@ -1495,17 +1368,16 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) buf[2] = 0x04; /* Write cache enable, */ /* Read cache not disabled */ /* No cache retention priorities */ - put_be16(&buf[4], 0xffff); /* Don't disable prefetch */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ /* Minimum prefetch = 0 */ - put_be16(&buf[8], 0xffff); /* Maximum prefetch */ - /* Maximum prefetch ceiling */ - put_be16(&buf[10], 0xffff); + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ } buf += 12; } -#else - valid_page = 1; -#endif /* Check that a valid page was requested and the mode data length * isn't too long. */ @@ -1519,178 +1391,242 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) if (mscmnd == SC_MODE_SENSE_6) buf0[0] = len - 1; else - put_be16(buf0, len - 2); + put_unaligned_be16(len - 2, buf0); return len; } -static int do_start_stop(struct fsg_dev *fsg) + +static int do_start_stop(struct fsg_common *common) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; int loej, start; - /* int immed = fsg->cmnd[1] & 0x01; */ - loej = fsg->cmnd[4] & 0x02; - start = fsg->cmnd[4] & 0x01; - - if (loej) { - /* eject request from the host */ - if (backing_file_is_open(curlun)) { - close_backing_file(fsg, curlun); - curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; - } + if (!curlun) { + return -EINVAL; + } else if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; } + loej = common->cmnd[4] & 0x02; + start = common->cmnd[4] & 0x01; + + /* eject code from file_storage.c:do_start_stop() */ + + if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ + (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (!start) { + /* Are we allowed to unload the media? */ + if (curlun->prevent_medium_removal) { + LDBG(curlun, "unload attempt prevented\n"); + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; + return -EINVAL; + } + if (loej) { /* Simulate an unload/eject */ + up_read(&common->filesem); + down_write(&common->filesem); + fsg_lun_close(curlun); + up_write(&common->filesem); + down_read(&common->filesem); + } + } else { + + /* Our emulation doesn't support mounting; the medium is + * available for use as soon as it is loaded. */ + if (!fsg_lun_is_open(curlun)) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + } return 0; } -static int do_prevent_allow(struct fsg_dev *fsg) + +static int do_prevent_allow(struct fsg_common *common) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; int prevent; - prevent = fsg->cmnd[4] & 0x01; - if ((fsg->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + if (!common->curlun) { + return -EINVAL; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } if (curlun->prevent_medium_removal && !prevent) - fsync_sub(curlun); + fsg_lun_fsync_sub(curlun); curlun->prevent_medium_removal = prevent; return 0; } -static int do_read_format_capacities(struct fsg_dev *fsg, +static int do_read_format_capacities(struct fsg_common *common, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; u8 *buf = (u8 *) bh->buf; buf[0] = buf[1] = buf[2] = 0; buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ buf += 4; - put_be32(&buf[0], curlun->num_sectors); /* Number of blocks */ - put_be32(&buf[4], 512); /* Block length */ - buf[4] = 0x02; /* Current capacity */ + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + buf[4] = 0x02; /* Current capacity */ return 12; } -static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; /* We don't support MODE SELECT */ - curlun->sense_data = SS_INVALID_COMMAND; + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; } -static int do_reserve(struct fsg_dev *fsg, struct fsg_buffhd *bh) -{ - int call_us_ret = -1; - char *envp[] = { - "HOME=/", - "PATH=/sbin:/system/sbin:/system/bin:/system/xbin", - NULL, - }; - char *exec_path[2] = {"/system/bin/stop", "/system/bin/start" }; - char *argv_stop[] = { exec_path[0], "adbd", NULL, }; - char *argv_start[] = { exec_path[1], "adbd", NULL, }; - - if (fsg->cmnd[1] == ('h'&0x1f) && fsg->cmnd[2] == 't' - && fsg->cmnd[3] == 'c') { - /* No special options */ - switch (fsg->cmnd[5]) { - case 0x01: /* enable adbd */ - call_us_ret = call_usermodehelper(exec_path[1], - argv_start, envp, UMH_WAIT_PROC); - break; - case 0x02: /*disable adbd */ - call_us_ret = call_usermodehelper(exec_path[0], - argv_stop, envp, UMH_WAIT_PROC); - break; - default: - printk(KERN_DEBUG "Unknown hTC specific command...(0x%2.2X)\n", - fsg->cmnd[5]); - break; - } - } - printk(KERN_NOTICE "%s adb daemon from mass_storage %s(%d)\n", - (fsg->cmnd[5] == 0x01)?"Enable":(fsg->cmnd[5] == 0x02)?"Disable":"Unknown", - (call_us_ret == 0)?"DONE":"FAIL", call_us_ret); - return 0; -} /*-------------------------------------------------------------------------*/ -#if 0 -static int write_zero(struct fsg_dev *fsg) + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) { - struct fsg_buffhd *bh; + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = usb_ep_set_wedge(fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_wedge(fsg->bulk_in); + } + return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; int rc; - DBG(fsg, "write_zero\n"); - /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; - while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); - if (rc) - return rc; + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsg->common->usb_amount_left = nkeep + fsg->common->residue; + while (fsg->common->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->common->next_buffhd_to_fill = bh->next; + fsg->common->usb_amount_left -= nsend; + nkeep = 0; } - - bh->inreq->length = 0; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - - fsg->next_buffhd_to_fill = bh->next; return 0; } -#endif -static int throw_away_data(struct fsg_dev *fsg) +static int throw_away_data(struct fsg_common *common) { struct fsg_buffhd *bh; u32 amount; int rc; - DBG(fsg, "throw_away_data\n"); - while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || - fsg->usb_amount_left > 0) { + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { /* Throw away the data in a filled buffer */ if (bh->state == BUF_STATE_FULL) { smp_rmb(); bh->state = BUF_STATE_EMPTY; - fsg->next_buffhd_to_drain = bh->next; + common->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ if (bh->outreq->actual != bh->outreq->length || bh->outreq->status != 0) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); return -EINTR; } continue; } /* Try to submit another request if we need one */ - bh = fsg->next_buffhd_to_fill; - if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { - amount = min(fsg->usb_amount_left, (u32) fsg->buf_size); + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; - fsg->usb_amount_left -= amount; + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; continue; } /* Otherwise wait for something to happen */ - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -1698,52 +1634,75 @@ static int throw_away_data(struct fsg_dev *fsg) } -static int finish_reply(struct fsg_dev *fsg) +static int finish_reply(struct fsg_common *common) { - struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; int rc = 0; - switch (fsg->data_dir) { + switch (common->data_dir) { case DATA_DIR_NONE: break; /* Nothing to send */ + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ case DATA_DIR_UNKNOWN: - rc = -EINVAL; + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } break; /* All but the last buffer of data must have already been sent */ case DATA_DIR_TO_HOST: - if (fsg->data_size == 0) - ; /* Nothing to send */ + if (common->data_size == 0) { + /* Nothing to send */ /* If there's no residue, simply send the last buffer */ - else if (fsg->residue == 0) { - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; + } else if (common->residue == 0) { + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + } else if (common->can_stall) { + bh->inreq->zero = 1; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->fsg) + rc = halt_bulk_in_endpoint(common->fsg); + } else if (fsg_is_set(common)) { + rc = pad_with_zeros(common->fsg); } else { - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); - fsg->next_buffhd_to_fill = bh->next; -#if 0 - /* this is unnecessary, and was causing problems with MacOS */ - if (bh->inreq->length > 0) - write_zero(fsg); -#endif + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; } break; /* We have processed all we want from the data the host has sent. * There may still be outstanding bulk-out requests. */ case DATA_DIR_FROM_HOST: - if (fsg->residue == 0) - ; /* Nothing to receive */ + if (common->residue == 0) { + /* Nothing to receive */ /* Did the host stop sending unexpectedly early? */ - else if (fsg->short_packet_received) { - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); rc = -EINTR; - } /* We haven't processed all the incoming data. Even though * we may be allowed to stall, doing so would cause a race. @@ -1752,35 +1711,38 @@ static int finish_reply(struct fsg_dev *fsg) * STALL. Not realizing the endpoint was halted, it wouldn't * clear the halt -- leading to problems later on. */ #if 0 - fsg_set_halt(fsg, fsg->bulk_out); - raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); - rc = -EINTR; + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; #endif /* We can't stall. Read in the excess data and throw it * all away. */ - else - rc = throw_away_data(fsg); + } else { + rc = throw_away_data(common); + } break; } return rc; } -static int send_status(struct fsg_dev *fsg) +static int send_status(struct fsg_common *common) { - struct lun *curlun = fsg->curlun; + struct fsg_lun *curlun = common->curlun; struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; int rc; u8 status = USB_STATUS_PASS; u32 sd, sdinfo = 0; - struct bulk_cs_wrap *csw; - DBG(fsg, "send_status\n"); /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } @@ -1788,36 +1750,39 @@ static int send_status(struct fsg_dev *fsg) if (curlun) { sd = curlun->sense_data; sdinfo = curlun->sense_data_info; - } else if (fsg->bad_lun_okay) + } else if (common->bad_lun_okay) sd = SS_NO_SENSE; else sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; - if (fsg->phase_error) { - DBG(fsg, "sending phase-error status\n"); + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); status = USB_STATUS_PHASE_ERROR; sd = SS_INVALID_COMMAND; } else if (sd != SS_NO_SENSE) { - DBG(fsg, "sending command-failure status\n"); + DBG(common, "sending command-failure status\n"); status = USB_STATUS_FAIL; - VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" " info x%x\n", SK(sd), ASC(sd), ASCQ(sd), sdinfo); } - csw = bh->buf; - /* Store and send the Bulk-only CSW */ - csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG); - csw->Tag = fsg->tag; - csw->Residue = cpu_to_le32(fsg->residue); + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); csw->Status = status; bh->inreq->length = USB_BULK_CS_WRAP_LEN; - start_transfer(fsg, fsg->bulk_in, bh->inreq, - &bh->inreq_busy, &bh->state); + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; - fsg->next_buffhd_to_fill = bh->next; + common->next_buffhd_to_fill = bh->next; return 0; } @@ -1826,91 +1791,95 @@ static int send_status(struct fsg_dev *fsg) /* Check whether the command is properly formed and whether its data size * and direction agree with the values we already have. */ -static int check_command(struct fsg_dev *fsg, int cmnd_size, +static int check_command(struct fsg_common *common, int cmnd_size, enum data_direction data_dir, unsigned int mask, int needs_medium, const char *name) { int i; - int lun = fsg->cmnd[1] >> 5; + int lun = common->cmnd[1] >> 5; static const char dirletter[4] = {'u', 'o', 'i', 'n'}; char hdlen[20]; - struct lun *curlun; + struct fsg_lun *curlun; hdlen[0] = 0; - if (fsg->data_dir != DATA_DIR_UNKNOWN) - sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], - fsg->data_size); - VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", - name, cmnd_size, dirletter[(int) data_dir], - fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen); + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); /* We can't reply at all until we know the correct data direction * and size. */ - if (fsg->data_size_from_cmnd == 0) + if (common->data_size_from_cmnd == 0) data_dir = DATA_DIR_NONE; - if (fsg->data_dir == DATA_DIR_UNKNOWN) { /* CB or CBI */ - fsg->data_dir = data_dir; - fsg->data_size = fsg->data_size_from_cmnd; - - } else { /* Bulk-only */ - if (fsg->data_size < fsg->data_size_from_cmnd) { - - /* Host data size < Device data size is a phase error. - * Carry out the command, but only transfer as much - * as we are allowed. */ - DBG(fsg, "phase error 1\n"); - fsg->data_size_from_cmnd = fsg->data_size; - fsg->phase_error = 1; - } + if (common->data_size < common->data_size_from_cmnd) { + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; } - fsg->residue = fsg->usb_amount_left = fsg->data_size; + common->residue = common->data_size; + common->usb_amount_left = common->data_size; /* Conflicting data directions is a phase error */ - if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) { - fsg->phase_error = 1; - DBG(fsg, "phase error 2\n"); + if (common->data_dir != data_dir + && common->data_size_from_cmnd > 0) { + common->phase_error = 1; return -EINVAL; } /* Verify the length of the command itself */ - if (cmnd_size != fsg->cmnd_size) { + if (cmnd_size != common->cmnd_size) { - /* Special case workaround: MS-Windows issues REQUEST SENSE/ - * INQUIRY with cbw->Length == 12 (it should be 6). */ - if ((fsg->cmnd[0] == SC_REQUEST_SENSE && fsg->cmnd_size == 12) - || (fsg->cmnd[0] == SC_INQUIRY && fsg->cmnd_size == 12)) - cmnd_size = fsg->cmnd_size; - else if (fsg->cmnd[0] == SC_RESERVE) - cmnd_size = fsg->cmnd_size; - else { - fsg->phase_error = 1; + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; return -EINVAL; } } /* Check that the LUN values are consistent */ - if (fsg->lun != lun) - DBG(fsg, "using LUN %d from CBW, " - "not LUN %d from CDB\n", - fsg->lun, lun); + if (common->lun != lun) + DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + common->lun, lun); /* Check the LUN */ - if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { - fsg->curlun = curlun = &fsg->luns[fsg->lun]; - if (fsg->cmnd[0] != SC_REQUEST_SENSE) { + if (common->lun >= 0 && common->lun < common->nluns) { + curlun = &common->luns[common->lun]; + common->curlun = curlun; + if (common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; curlun->sense_data_info = 0; curlun->info_valid = 0; } } else { - fsg->curlun = curlun = NULL; - fsg->bad_lun_okay = 0; + common->curlun = NULL; + curlun = NULL; + common->bad_lun_okay = 0; /* INQUIRY and REQUEST SENSE commands are explicitly allowed * to use unsupported LUNs; all others may not. */ - if (fsg->cmnd[0] != SC_INQUIRY && - fsg->cmnd[0] != SC_REQUEST_SENSE) { - DBG(fsg, "unsupported LUN %d\n", fsg->lun); + if (common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { + DBG(common, "unsupported LUN %d\n", common->lun); return -EINVAL; } } @@ -1918,29 +1887,27 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, /* If a unit attention condition exists, only INQUIRY and * REQUEST SENSE commands are allowed; anything else must fail. */ if (curlun && curlun->unit_attention_data != SS_NO_SENSE && - fsg->cmnd[0] != SC_INQUIRY && - fsg->cmnd[0] != SC_REQUEST_SENSE) { + common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { curlun->sense_data = curlun->unit_attention_data; curlun->unit_attention_data = SS_NO_SENSE; return -EINVAL; } /* Check that only command bytes listed in the mask are non-zero */ - fsg->cmnd[1] &= 0x1f; /* Mask away the LUN */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ for (i = 1; i < cmnd_size; ++i) { - if (fsg->cmnd[i] && !(mask & (1 << i))) { + if (common->cmnd[i] && !(mask & (1 << i))) { if (curlun) curlun->sense_data = SS_INVALID_FIELD_IN_CDB; - DBG(fsg, "SS_INVALID_FIELD_IN_CDB\n"); return -EINVAL; } } /* If the medium isn't mounted and the command needs to access * it, return an error. */ - if (curlun && !backing_file_is_open(curlun) && needs_medium) { + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { curlun->sense_data = SS_MEDIUM_NOT_PRESENT; - DBG(fsg, "SS_MEDIUM_NOT_PRESENT\n"); return -EINVAL; } @@ -1948,7 +1915,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, } -static int do_scsi_command(struct fsg_dev *fsg) +static int do_scsi_command(struct fsg_common *common) { struct fsg_buffhd *bh; int rc; @@ -1956,159 +1923,181 @@ static int do_scsi_command(struct fsg_dev *fsg) int i; static char unknown[16]; - dump_cdb(fsg); + dump_cdb(common); /* Wait for the next buffer to become available for data or status */ - bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } - fsg->phase_error = 0; - fsg->short_packet_received = 0; + common->phase_error = 0; + common->short_packet_received = 0; - down_read(&fsg->filesem); /* We're using the backing file */ - switch (fsg->cmnd[0]) { + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { case SC_INQUIRY: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "INQUIRY")) == 0) - reply = do_inquiry(fsg, bh); + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); break; case SC_MODE_SELECT_6: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (1<<1) | (1<<4), 0, - "MODE SELECT(6)")) == 0) - reply = do_mode_select(fsg, bh); + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); break; case SC_MODE_SELECT_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (3<<7), 0, - "MODE SELECT(10)")) == 0) - reply = do_mode_select(fsg, bh); + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); break; case SC_MODE_SENSE_6: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (1<<4), 0, - "MODE SENSE(6)")) == 0) - reply = do_mode_sense(fsg, bh); + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); break; case SC_MODE_SENSE_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (1<<2) | (3<<7), 0, - "MODE SENSE(10)")) == 0) - reply = do_mode_sense(fsg, bh); + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); break; case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<4), 0, - "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) - reply = do_prevent_allow(fsg); + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); break; case SC_READ_6: - i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (7<<1) | (1<<4), 1, - "READ(6)")) == 0) - reply = do_read(fsg); + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = do_read(common); break; case SC_READ_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "READ(10)")) == 0) - reply = do_read(fsg); + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = do_read(common); break; case SC_READ_12: - fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9; - if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "READ(12)")) == 0) - reply = do_read(fsg); + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = do_read(common); break; case SC_READ_CAPACITY: - fsg->data_size_from_cmnd = 8; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (0xf<<2) | (1<<8), 1, - "READ CAPACITY")) == 0) - reply = do_read_capacity(fsg, bh); + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); break; case SC_READ_HEADER: - if (!fsg->curlun->cdrom) - goto unknown_cmd; - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7) | (0x1f<<1), 1, - "READ HEADER")) == 0) - reply = do_read_header(fsg, bh); - + if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = do_read_header(common, bh); break; case SC_READ_TOC: - if (!fsg->curlun->cdrom) - goto unknown_cmd; - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, - "READ TOC")) == 0) - reply = do_read_toc(fsg, bh); - + if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = do_read_toc(common, bh); break; case SC_READ_FORMAT_CAPACITIES: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (3<<7), 1, - "READ FORMAT CAPACITIES")) == 0) - reply = do_read_format_capacities(fsg, bh); + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = do_read_format_capacities(common, bh); break; case SC_REQUEST_SENSE: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, - (1<<4), 0, - "REQUEST SENSE")) == 0) - reply = do_request_sense(fsg, bh); + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); break; case SC_START_STOP_UNIT: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 6, DATA_DIR_NONE, - (1<<1) | (1<<4), 0, - "START-STOP UNIT")) == 0) - reply = do_start_stop(fsg); + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); break; case SC_SYNCHRONIZE_CACHE: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (0xf<<2) | (3<<7), 1, - "SYNCHRONIZE CACHE")) == 0) - reply = do_synchronize_cache(fsg); + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); break; case SC_TEST_UNIT_READY: - fsg->data_size_from_cmnd = 0; - reply = check_command(fsg, 6, DATA_DIR_NONE, + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, 0, 1, "TEST UNIT READY"); break; @@ -2116,80 +2105,79 @@ static int do_scsi_command(struct fsg_dev *fsg) /* Although optional, this command is used by MS-Windows. We * support a minimal version: BytChk must be 0. */ case SC_VERIFY: - fsg->data_size_from_cmnd = 0; - if ((reply = check_command(fsg, 10, DATA_DIR_NONE, - (1<<1) | (0xf<<2) | (3<<7), 1, - "VERIFY")) == 0) - reply = do_verify(fsg); + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = do_verify(common); break; case SC_WRITE_6: - i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; - if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, - (7<<1) | (1<<4), 1, - "WRITE(6)")) == 0) - reply = do_write(fsg); + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = do_write(common); break; case SC_WRITE_10: - fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; - if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (3<<7), 1, - "WRITE(10)")) == 0) - reply = do_write(fsg); + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = do_write(common); break; case SC_WRITE_12: - fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9; - if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, - (1<<1) | (0xf<<2) | (0xf<<6), 1, - "WRITE(12)")) == 0) - reply = do_write(fsg); + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = do_write(common); break; - case SC_RESERVE: - fsg->data_size_from_cmnd = fsg->cmnd[4]; - if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, - (1<<1) | (0xf<<2) , 0, - "RESERVE(6)")) == 0) - reply = do_reserve(fsg, bh); - break; /* Some mandatory commands that we recognize but don't implement. * They don't mean much in this setting. It's left as an exercise * for anyone interested to implement RESERVE and RELEASE in terms * of Posix locks. */ case SC_FORMAT_UNIT: case SC_RELEASE: + case SC_RESERVE: case SC_SEND_DIAGNOSTIC: /* Fall through */ default: -unknown_cmd: - fsg->data_size_from_cmnd = 0; - sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); - if ((reply = check_command(fsg, fsg->cmnd_size, - DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { - fsg->curlun->sense_data = SS_INVALID_COMMAND; +unknown_cmnd: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; reply = -EINVAL; } break; } - up_read(&fsg->filesem); + up_read(&common->filesem); - VDBG(fsg, "reply: %d, fsg->data_size_from_cmnd: %d\n", - reply, fsg->data_size_from_cmnd); if (reply == -EINTR || signal_pending(current)) return -EINTR; /* Set up the single reply buffer for finish_reply() */ if (reply == -EINVAL) reply = 0; /* Error reply length */ - if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32) reply, fsg->data_size_from_cmnd); + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, common->data_size_from_cmnd); bh->inreq->length = reply; bh->state = BUF_STATE_FULL; - fsg->residue -= reply; + common->residue -= reply; } /* Otherwise it's already set */ return 0; @@ -2201,64 +2189,87 @@ unknown_cmd: static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct usb_request *req = bh->outreq; - struct bulk_cb_wrap *cbw = req->buf; + struct fsg_bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; - /* Was this a real packet? */ - if (req->status) + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) return -EINVAL; /* Is the CBW valid? */ if (req->actual != USB_BULK_CB_WRAP_LEN || - cbw->Signature != __constant_cpu_to_le32( + cbw->Signature != cpu_to_le32( USB_BULK_CB_SIG)) { DBG(fsg, "invalid CBW: len %u sig 0x%x\n", req->actual, le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); return -EINVAL; } /* Is the CBW meaningful? */ - if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " "cmdlen %u\n", cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } return -EINVAL; } /* Save the command for later */ - fsg->cmnd_size = cbw->Length; - memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); if (cbw->Flags & USB_BULK_IN_FLAG) - fsg->data_dir = DATA_DIR_TO_HOST; + common->data_dir = DATA_DIR_TO_HOST; else - fsg->data_dir = DATA_DIR_FROM_HOST; - fsg->data_size = le32_to_cpu(cbw->DataTransferLength); - if (fsg->data_size == 0) - fsg->data_dir = DATA_DIR_NONE; - fsg->lun = cbw->Lun; - fsg->tag = cbw->Tag; + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + common->tag = cbw->Tag; return 0; } -static int get_next_command(struct fsg_dev *fsg) +static int get_next_command(struct fsg_common *common) { struct fsg_buffhd *bh; int rc = 0; /* Wait for the next buffer to become available */ - bh = fsg->next_buffhd_to_fill; + bh = common->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } /* Queue a request to read a Bulk-only CBW */ - set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - start_transfer(fsg, fsg->bulk_out, bh->outreq, - &bh->outreq_busy, &bh->state); + set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; /* We will drain the buffer in software, which means we * can reuse it for the next filling. No need to advance @@ -2266,12 +2277,12 @@ static int get_next_command(struct fsg_dev *fsg) /* Wait for the CBW to arrive */ while (bh->state != BUF_STATE_FULL) { - rc = sleep_thread(fsg); + rc = sleep_thread(common); if (rc) return rc; } smp_rmb(); - rc = received_cbw(fsg, bh); + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; bh->state = BUF_STATE_EMPTY; return rc; @@ -2280,96 +2291,103 @@ static int get_next_command(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, const struct usb_endpoint_descriptor *d) { int rc; - DBG(fsg, "usb_ep_enable %s\n", ep->name); - ep->driver_data = fsg; + ep->driver_data = common; rc = usb_ep_enable(ep, d); if (rc) - ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); + ERROR(common, "can't enable %s, result %d\n", ep->name, rc); return rc; } -static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, struct usb_request **preq) { *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); if (*preq) return 0; - ERROR(fsg, "can't allocate request for %s\n", ep->name); + ERROR(common, "can't allocate request for %s\n", ep->name); return -ENOMEM; } -/* - * Reset interface setting and re-init endpoint state (toggle etc). - * Call with altsetting < 0 to disable the interface. The only other - * available altsetting is 0, which enables the interface. - */ -static int do_set_interface(struct fsg_dev *fsg, int altsetting) +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) { - struct usb_composite_dev *cdev = fsg->cdev; - int rc = 0; - int i; - const struct usb_endpoint_descriptor *d; + const struct usb_endpoint_descriptor *d; + struct fsg_dev *fsg; + int i, rc = 0; + + if (common->running) + DBG(common, "reset interface\n"); - if (fsg->running) - DBG(fsg, "reset interface\n"); reset: /* Deallocate the requests */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - if (bh->inreq) { - usb_ep_free_request(fsg->bulk_in, bh->inreq); - bh->inreq = NULL; + if (common->fsg) { + fsg = common->fsg; + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } } - if (bh->outreq) { - usb_ep_free_request(fsg->bulk_out, bh->outreq); - bh->outreq = NULL; + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + common->fsg = NULL; + wake_up(&common->fsg_wait); } - /* Disable the endpoints */ - if (fsg->bulk_in_enabled) { - DBG(fsg, "usb_ep_disable %s\n", fsg->bulk_in->name); - usb_ep_disable(fsg->bulk_in); - fsg->bulk_in_enabled = 0; - } - if (fsg->bulk_out_enabled) { - DBG(fsg, "usb_ep_disable %s\n", fsg->bulk_out->name); - usb_ep_disable(fsg->bulk_out); - fsg->bulk_out_enabled = 0; - } - - fsg->running = 0; - if (altsetting < 0 || rc != 0) + common->running = 0; + if (!new_fsg || rc) return rc; - DBG(fsg, "set interface %d\n", altsetting); + common->fsg = new_fsg; + fsg = common->fsg; /* Enable the endpoints */ - d = ep_desc(cdev->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + rc = enable_endpoint(common, fsg->bulk_in, d); + if (rc) goto reset; fsg->bulk_in_enabled = 1; - d = ep_desc(cdev->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc); - if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + rc = enable_endpoint(common, fsg->bulk_out, d); + if (rc) goto reset; fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; - rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq); - if (rc != 0) + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) goto reset; - rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq); - if (rc != 0) + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) goto reset; bh->inreq->buf = bh->outreq->buf = bh->buf; bh->inreq->context = bh->outreq->context = bh; @@ -2377,168 +2395,168 @@ reset: bh->outreq->complete = bulk_out_complete; } - fsg->running = 1; - for (i = 0; i < fsg->nluns; ++i) - fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; - + common->running = 1; + for (i = 0; i < common->nluns; ++i) + common->luns[i].unit_attention_data = SS_RESET_OCCURRED; return rc; } -static void adjust_wake_lock(struct fsg_dev *fsg) + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { - int ums_active = 0; - int i; - unsigned long flags; - - spin_lock_irqsave(&fsg->lock, flags); - - if (fsg->config) { - for (i = 0; i < fsg->nluns; ++i) { - if (backing_file_is_open(&fsg->luns[i])) - ums_active = 1; - } - } - - if (ums_active) - wake_lock(&fsg->wake_lock); - else - wake_unlock(&fsg->wake_lock); - - spin_unlock_irqrestore(&fsg->lock, flags); + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = fsg; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return 0; } -/* - * Change our operational configuration. This code must agree with the code - * that returns config descriptors, and with interface altsetting code. - * - * It's also responsible for power management interactions. Some - * configurations might not work with our current power sources. - * For now we just assume the gadget is always self-powered. - */ -static int do_set_config(struct fsg_dev *fsg, u8 new_config) +static void fsg_disable(struct usb_function *f) { - int rc = 0; - - if (new_config == fsg->config) - return rc; - - /* Disable the single interface */ - if (fsg->config != 0) { - DBG(fsg, "reset config\n"); - fsg->config = 0; - } - - /* Enable the interface */ - if (new_config != 0) - fsg->config = new_config; - - if (new_config || fsg->function.hidden) { - fsg->ums_state = new_config; - printk(KERN_INFO "ums: set state %d\n", new_config); - switch_set_state(&fsg->sdev, new_config); - } - adjust_wake_lock(fsg); - return rc; + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); } /*-------------------------------------------------------------------------*/ -static void handle_exception(struct fsg_dev *fsg) +static struct fsg_dev *the_fsg; + +static void handle_exception(struct fsg_common *common) { siginfo_t info; - int sig; int i; struct fsg_buffhd *bh; enum fsg_state old_state; - u8 new_config; - struct lun *curlun; - int rc; - unsigned long flags; + struct fsg_lun *curlun; + unsigned int exception_req_tag; - DBG(fsg, "handle_exception state: %d\n", (int)fsg->state); /* Clear the existing signals. Anything but SIGUSR1 is converted * into a high-priority EXIT exception. */ for (;;) { - sig = dequeue_signal_lock(current, ¤t->blocked, &info); + int sig = + dequeue_signal_lock(current, ¤t->blocked, &info); if (!sig) break; if (sig != SIGUSR1) { - if (fsg->state < FSG_STATE_EXIT) - DBG(fsg, "Main thread exiting on signal\n"); - raise_exception(fsg, FSG_STATE_EXIT); + if (common->state < FSG_STATE_EXIT) + DBG(common, "Main thread exiting on signal\n"); + raise_exception(common, FSG_STATE_EXIT); } } - /* Clear out the controller's fifos */ - if (fsg->bulk_in_enabled) - usb_ep_fifo_flush(fsg->bulk_in); - if (fsg->bulk_out_enabled) - usb_ep_fifo_flush(fsg->bulk_out); + /* Cancel all the pending transfers */ + if (likely(common->fsg)) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(common)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } /* Reset the I/O buffer states and pointers, the SCSI * state, and the exception. Then invoke the handler. */ - spin_lock_irqsave(&fsg->lock, flags); + spin_lock_irq(&common->lock); - for (i = 0; i < NUM_BUFFERS; ++i) { - bh = &fsg->buffhds[i]; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } - fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = - &fsg->buffhds[0]; - - new_config = fsg->new_config; - old_state = fsg->state; + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + old_state = common->state; if (old_state == FSG_STATE_ABORT_BULK_OUT) - fsg->state = FSG_STATE_STATUS_PHASE; + common->state = FSG_STATE_STATUS_PHASE; else { - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; + for (i = 0; i < common->nluns; ++i) { + curlun = &common->luns[i]; curlun->prevent_medium_removal = 0; - curlun->sense_data = curlun->unit_attention_data = - SS_NO_SENSE; + curlun->sense_data = SS_NO_SENSE; + curlun->unit_attention_data = SS_NO_SENSE; curlun->sense_data_info = 0; curlun->info_valid = 0; } - fsg->state = FSG_STATE_IDLE; + common->state = FSG_STATE_IDLE; } - spin_unlock_irqrestore(&fsg->lock, flags); + spin_unlock_irq(&common->lock); /* Carry out any extra actions required for the exception */ switch (old_state) { - default: - break; - case FSG_STATE_ABORT_BULK_OUT: - DBG(fsg, "FSG_STATE_ABORT_BULK_OUT\n"); - spin_lock_irqsave(&fsg->lock, flags); - if (fsg->state == FSG_STATE_STATUS_PHASE) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irqrestore(&fsg->lock, flags); + send_status(common); + spin_lock_irq(&common->lock); + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); break; case FSG_STATE_RESET: - /* really not much to do here */ + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + /* Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. */ + /* for (i = 0; i < common->nluns; ++i) */ + /* common->luns[i].unit_attention_data = */ + /* SS_RESET_OCCURRED; */ break; case FSG_STATE_CONFIG_CHANGE: - rc = do_set_config(fsg, new_config); - if (new_config == 0) { - /* We're using the backing file */ - down_read(&fsg->filesem); - fsync_all(fsg); - up_read(&fsg->filesem); - } + do_set_interface(common, common->new_fsg); + switch_set_state(&the_fsg->sdev, !!common->new_fsg); break; case FSG_STATE_EXIT: case FSG_STATE_TERMINATED: - do_set_interface(fsg, -1); - do_set_config(fsg, 0); /* Free resources */ - spin_lock_irqsave(&fsg->lock, flags); - fsg->state = FSG_STATE_TERMINATED; /* Stop the thread */ - spin_unlock_irqrestore(&fsg->lock, flags); + do_set_interface(common, NULL); /* Free resources */ + spin_lock_irq(&common->lock); + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: break; } } @@ -2546,10 +2564,9 @@ static void handle_exception(struct fsg_dev *fsg) /*-------------------------------------------------------------------------*/ -static int fsg_main_thread(void *fsg_) +static int fsg_main_thread(void *common_) { - struct fsg_dev *fsg = fsg_; - unsigned long flags; + struct fsg_common *common = common_; /* Allow the thread to be killed by a signal, but set the signal mask * to block everything but INT, TERM, KILL, and USR1. */ @@ -2567,610 +2584,641 @@ static int fsg_main_thread(void *fsg_) set_fs(get_ds()); /* The main loop */ - while (fsg->state != FSG_STATE_TERMINATED) { - if (exception_in_progress(fsg) || signal_pending(current)) { - handle_exception(fsg); + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common) || signal_pending(current)) { + handle_exception(common); continue; } - if (!fsg->running) { - sleep_thread(fsg); + if (!common->running) { + sleep_thread(common); continue; } - if (get_next_command(fsg)) + if (get_next_command(common)) continue; - spin_lock_irqsave(&fsg->lock, flags); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_DATA_PHASE; - spin_unlock_irqrestore(&fsg->lock, flags); + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + spin_unlock_irq(&common->lock); - if (do_scsi_command(fsg) || finish_reply(fsg)) + if (do_scsi_command(common) || finish_reply(common)) continue; - spin_lock_irqsave(&fsg->lock, flags); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_STATUS_PHASE; - spin_unlock_irqrestore(&fsg->lock, flags); + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + spin_unlock_irq(&common->lock); - if (send_status(fsg)) + if (send_status(common)) continue; - spin_lock_irqsave(&fsg->lock, flags); - if (!exception_in_progress(fsg)) - fsg->state = FSG_STATE_IDLE; - spin_unlock_irqrestore(&fsg->lock, flags); + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); + } + + spin_lock_irq(&common->lock); + common->thread_task = NULL; + spin_unlock_irq(&common->lock); + + if (!common->thread_exits || common->thread_exits(common) < 0) { + struct fsg_lun *curlun = common->luns; + unsigned i = common->nluns; + + down_write(&common->filesem); + for (; i--; ++curlun) { + if (!fsg_lun_is_open(curlun)) + continue; + + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; } - - spin_lock_irqsave(&fsg->lock, flags); - fsg->thread_task = NULL; - spin_unlock_irqrestore(&fsg->lock, flags); - - /* In case we are exiting because of a signal, unregister the - * gadget driver and close the backing file. */ - if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) - close_all_backing_files(fsg); + up_write(&common->filesem); + } /* Let the unbind and cleanup routines know the thread has exited */ - complete_and_exit(&fsg->thread_notifier, 0); + complete_and_exit(&common->thread_notifier, 0); } -/*-------------------------------------------------------------------------*/ +/*************************** DEVICE ATTRIBUTES ***************************/ -/* If the next two routines are called while the gadget is registered, - * the caller must own fsg->filesem for writing. */ +/* Write permission is checked per LUN in store_*() functions. */ +static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); +static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); -static int open_backing_file(struct fsg_dev *fsg, struct lun *curlun, - const char *filename) + +/****************************** FSG COMMON ******************************/ + +static void fsg_common_release(struct kref *ref); + +static void fsg_lun_release(struct device *dev) { - int ro; - struct file *filp = NULL; - int rc = -EINVAL; - struct inode *inode = NULL; - loff_t size; - loff_t num_sectors; - loff_t min_sectors; + /* Nothing needs to be done */ +} - /* R/W if we can, R/O if we must */ - ro = curlun->ro; - if (!ro) { - filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); - if (-EROFS == PTR_ERR(filp)) - ro = 1; - } - if (ro) - filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(filp)) { - LINFO(curlun, "unable to open backing file: %s\n", filename); - return PTR_ERR(filp); - } +static inline void fsg_common_get(struct fsg_common *common) +{ + kref_get(&common->ref); +} - if (!(filp->f_mode & FMODE_WRITE)) - ro = 1; - - if (filp->f_path.dentry) - inode = filp->f_path.dentry->d_inode; - if (inode && S_ISBLK(inode->i_mode)) { - if (bdev_read_only(inode->i_bdev)) - ro = 1; - } else if (!inode || !S_ISREG(inode->i_mode)) { - LINFO(curlun, "invalid file type: %s\n", filename); - goto out; - } - - /* If we can't read the file, it's no good. - * If we can't write the file, use it read-only. */ - if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { - LINFO(curlun, "file not readable: %s\n", filename); - goto out; - } - if (!(filp->f_op->write || filp->f_op->aio_write)) - ro = 1; - - size = i_size_read(inode->i_mapping->host); - if (size < 0) { - LINFO(curlun, "unable to find file size: %s\n", filename); - rc = (int) size; - goto out; - } - num_sectors = size >> 9; /* File size in 512-byte sectors */ - min_sectors = 1; - if (curlun->cdrom) { - num_sectors &= ~3; /* Reduce to a multiple of 2048 */ -#if 0 - min_sectors = 300*4; /* Smallest track is 300 frames */ -#endif - if (num_sectors >= 256*60*75*4) { - num_sectors = (256*60*75 - 1) * 4; - LINFO(curlun, "file too big: %s\n", filename); - LINFO(curlun, "using only first %d blocks\n", - (int) num_sectors); - } - } - if (num_sectors < min_sectors) { - LINFO(curlun, "file too small: %s\n", filename); - rc = -ETOOSMALL; - goto out; - } - - get_file(filp); - curlun->ro = ro; - curlun->filp = filp; - curlun->file_length = size; - curlun->num_sectors = num_sectors; - LDBG(curlun, "open backing file: %s size: %lld num_sectors: %lld\n", - filename, size, num_sectors); - rc = 0; - adjust_wake_lock(fsg); - -out: - filp_close(filp, current->files); - return rc; +static inline void fsg_common_put(struct fsg_common *common) +{ + kref_put(&common->ref, fsg_common_release); } -static void close_backing_file(struct fsg_dev *fsg, struct lun *curlun) +static struct fsg_common *fsg_common_init(struct fsg_common *common, + struct usb_composite_dev *cdev, + struct fsg_config *cfg) { - if (curlun->filp) { - int rc; - - /* - * XXX: San: Ugly hack here added to ensure that - * our pages get synced to disk. - * Also drop caches here just to be extra-safe - */ - rc = vfs_fsync(curlun->filp, curlun->filp->f_path.dentry, 1); - if (rc < 0) - printk(KERN_ERR "ums: Error syncing data (%d)\n", rc); - /* drop_pagecache and drop_slab are no longer available */ - /* drop_pagecache(); */ - /* drop_slab(); */ - - LDBG(curlun, "close backing file\n"); - fput(curlun->filp); - curlun->filp = NULL; - adjust_wake_lock(fsg); - } -} - -static void close_all_backing_files(struct fsg_dev *fsg) -{ - int i; - - for (i = 0; i < fsg->nluns; ++i) - close_backing_file(fsg, &fsg->luns[i]); -} - -static ssize_t show_file(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - char *p; - ssize_t rc; - - down_read(&fsg->filesem); - if (backing_file_is_open(curlun)) { /* Get the complete pathname */ - p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); - if (IS_ERR(p)) - rc = PTR_ERR(p); - else { - rc = strlen(p); - memmove(buf, p, rc); - buf[rc] = '\n'; /* Add a newline */ - buf[++rc] = 0; - } - } else { /* No file, return 0 bytes */ - *buf = 0; - rc = 0; - } - up_read(&fsg->filesem); - return rc; -} - -static ssize_t store_file(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = dev_get_drvdata(dev); - int rc = 0; - - DBG(fsg, "store_file: \"%s\"\n", buf); -#if 0 - /* disabled because we need to allow closing the backing file if the media was removed */ - if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) { - LDBG(curlun, "eject attempt prevented\n"); - return -EBUSY; /* "Door is locked" */ - } -#endif - - /* Remove a trailing newline */ - if (count > 0 && buf[count-1] == '\n') - ((char *) buf)[count-1] = 0; - - /* Eject current medium */ - down_write(&fsg->filesem); - if (backing_file_is_open(curlun)) { - close_backing_file(fsg, curlun); - curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; - } - - /* Load new medium */ - if (count > 0 && buf[0]) { - rc = open_backing_file(fsg, curlun, buf); - if (rc == 0) - curlun->unit_attention_data = - SS_NOT_READY_TO_READY_TRANSITION; - } - up_write(&fsg->filesem); - return (rc < 0 ? rc : count); -} - - -static DEVICE_ATTR(file, 0444, show_file, store_file); - -/*-------------------------------------------------------------------------*/ - -static void fsg_release(struct kref *ref) -{ - struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref); - - kfree(fsg->luns); - kfree(fsg); -} - -static void lun_release(struct device *dev) -{ - struct fsg_dev *fsg = dev_get_drvdata(dev); - - kref_put(&fsg->ref, fsg_release); -} - - -/*-------------------------------------------------------------------------*/ - -static int __init fsg_alloc(void) -{ - struct fsg_dev *fsg; - - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); - if (!fsg) - return -ENOMEM; - spin_lock_init(&fsg->lock); - init_rwsem(&fsg->filesem); - kref_init(&fsg->ref); - init_completion(&fsg->thread_notifier); - - the_fsg = fsg; - return 0; -} - -static ssize_t print_switch_name(struct switch_dev *sdev, char *buf) -{ - return sprintf(buf, "%s\n", DRIVER_NAME); -} - -static ssize_t print_switch_state(struct switch_dev *sdev, char *buf) -{ - struct fsg_dev *fsg = container_of(sdev, struct fsg_dev, sdev); - return sprintf(buf, "%s\n", (fsg->ums_state ? "online" : "offline")); -} - -static void -fsg_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct fsg_dev *fsg = func_to_dev(f); - int i; - struct lun *curlun; - - DBG(fsg, "fsg_function_unbind\n"); - clear_bit(REGISTERED, &fsg->atomic_bitflags); - - /* Unregister the sysfs attribute files and the LUNs */ - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - if (curlun->registered) { - device_remove_file(&curlun->dev, &dev_attr_file); - device_unregister(&curlun->dev); - curlun->registered = 0; - } - } - - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - - /* Free the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) { - kfree(fsg->buffhds[i].buf); - fsg->buffhds[i].buf = NULL; - } - switch_dev_unregister(&the_fsg->sdev); -} - -static int -fsg_function_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct fsg_dev *fsg = func_to_dev(f); - int rc; - int i; - int id; - struct lun *curlun; - struct usb_ep *ep; - char *pathbuf, *p; - - fsg->cdev = cdev; - DBG(fsg, "fsg_function_bind\n"); - - dev_attr_file.attr.mode = 0644; + struct usb_gadget *gadget = cdev->gadget; + struct fsg_buffhd *bh; + struct fsg_lun *curlun; + struct fsg_lun_config *lcfg; + int nluns, i, rc; + char *pathbuf; /* Find out how many LUNs there should be */ - i = fsg->nluns; - if (i == 0) - i = 1; - if (i > MAX_LUNS) { - ERROR(fsg, "invalid number of LUNs: %d\n", i); - rc = -EINVAL; - goto out; + nluns = cfg->nluns; + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); + } + + /* Allocate? */ + if (!common) { + common = kzalloc(sizeof *common, GFP_KERNEL); + if (!common) + return ERR_PTR(-ENOMEM); + common->free_storage_on_release = 1; + } else { + memset(common, 0, sizeof common); + common->free_storage_on_release = 0; + } + + common->private_data = cfg->private_data; + + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; + + /* Maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + rc = usb_string_id(cdev); + if (unlikely(rc < 0)) + goto error_release; + fsg_strings[FSG_STRING_INTERFACE].id = rc; + fsg_intf_desc.iInterface = rc; } /* Create the LUNs, open their backing files, and register the * LUN devices in sysfs. */ - fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL); - if (!fsg->luns) { + curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL); + if (unlikely(!curlun)) { rc = -ENOMEM; - goto out; + goto error_release; } - fsg->nluns = i; + common->luns = curlun; - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - curlun->ro = 0; - if (fsg->cdrom_lun & (1 << i)) { - curlun->cdrom = 1; - curlun->ro = 1; - } - curlun->dev.release = lun_release; - /* use "usb_mass_storage" platform device as parent if available */ - if (fsg->pdev) - curlun->dev.parent = &fsg->pdev->dev; - else - curlun->dev.parent = &cdev->gadget->dev; - dev_set_drvdata(&curlun->dev, fsg); - dev_set_name(&curlun->dev,"lun%d", i); + init_rwsem(&common->filesem); + + for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { + curlun->cdrom = !!lcfg->cdrom; + curlun->ro = lcfg->cdrom || lcfg->ro; + curlun->removable = lcfg->removable; + curlun->dev.release = fsg_lun_release; + +#ifdef CONFIG_USB_ANDROID_MASS_STORAGE + /* use "usb_mass_storage" platform device as parent */ + curlun->dev.parent = &cfg->pdev->dev; +#else + curlun->dev.parent = &gadget->dev; +#endif + /* curlun->dev.driver = &fsg_driver.driver; XXX */ + dev_set_drvdata(&curlun->dev, &common->filesem); + dev_set_name(&curlun->dev, + cfg->lun_name_format + ? cfg->lun_name_format + : "lun%d", + i); rc = device_register(&curlun->dev); - if (rc != 0) { - INFO(fsg, "failed to register LUN%d: %d\n", i, rc); - goto out; + if (rc) { + INFO(common, "failed to register LUN%d: %d\n", i, rc); + common->nluns = i; + goto error_release; } + + rc = device_create_file(&curlun->dev, &dev_attr_ro); + if (rc) + goto error_luns; rc = device_create_file(&curlun->dev, &dev_attr_file); - if (rc != 0) { - ERROR(fsg, "device_create_file failed: %d\n", rc); - device_unregister(&curlun->dev); - goto out; + if (rc) + goto error_luns; + + if (lcfg->filename) { + rc = fsg_lun_open(curlun, lcfg->filename); + if (rc) + goto error_luns; + } else if (!curlun->removable) { + ERROR(common, "no file given for LUN%d\n", i); + rc = -EINVAL; + goto error_luns; } - curlun->registered = 1; - kref_get(&fsg->ref); } + common->nluns = nluns; - /* allocate interface ID(s) */ - id = usb_interface_id(c, f); - if (id < 0) - return id; - intf_desc.bInterfaceNumber = id; - ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_in_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; /* claim the endpoint */ - fsg->bulk_in = ep; - - ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_out_desc); - if (!ep) - goto autoconf_fail; - ep->driver_data = fsg; /* claim the endpoint */ - fsg->bulk_out = ep; - - rc = -ENOMEM; - - if (gadget_is_dualspeed(cdev->gadget)) { - /* Assume endpoint addresses are the same for both speeds */ - hs_bulk_in_desc.bEndpointAddress = - fs_bulk_in_desc.bEndpointAddress; - hs_bulk_out_desc.bEndpointAddress = - fs_bulk_out_desc.bEndpointAddress; - - f->hs_descriptors = hs_function; - } - - /* Allocate the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - /* Allocate for the bulk-in endpoint. We assume that - * the buffer will also work with the bulk-out (and - * interrupt-in) endpoint. */ - bh->buf = kmalloc(fsg->buf_size, GFP_KERNEL); - if (!bh->buf) - goto out; + /* Data buffers cyclic list */ + bh = common->buffhds; + i = FSG_NUM_BUFFERS; + goto buffhds_first_it; + do { bh->next = bh + 1; - } - fsg->buffhds[NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + ++bh; +buffhds_first_it: + bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); + if (unlikely(!bh->buf)) { + rc = -ENOMEM; + goto error_release; + } + } while (--i); + bh->next = common->buffhds; - fsg->thread_task = kthread_create(fsg_main_thread, fsg, - shortname); - if (IS_ERR(fsg->thread_task)) { - rc = PTR_ERR(fsg->thread_task); - ERROR(fsg, "kthread_create failed: %d\n", rc); - goto out; - } - INFO(fsg, "Number of LUNs=%d\n", fsg->nluns); + /* Prepare inquiryString */ + if (cfg->release != 0xffff) { + i = cfg->release; + } else { + i = usb_gadget_controller_number(gadget); + if (i >= 0) { + i = 0x0300 + i; + } else { + WARNING(common, "controller '%s' not recognized\n", + gadget->name); + i = 0x0399; + } + } +#define OR(x, y) ((x) ? (x) : (y)) + snprintf(common->inquiry_string, sizeof common->inquiry_string, + "%-8s%-16s%04x", + OR(cfg->vendor_name, "Linux "), + /* Assume product name dependent on the first LUN */ + OR(cfg->product_name, common->luns->cdrom + ? "File-Stor Gadget" + : "File-CD Gadget "), + i); + + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + common->can_stall = cfg->can_stall && + !(gadget_is_at91(common->gadget)); + + + spin_lock_init(&common->lock); + kref_init(&common->ref); + + + /* Tell the thread to start working */ + common->thread_exits = cfg->thread_exits; + common->thread_task = + kthread_create(fsg_main_thread, common, + OR(cfg->thread_name, "file-storage")); + if (IS_ERR(common->thread_task)) { + rc = PTR_ERR(common->thread_task); + goto error_release; + } + init_completion(&common->thread_notifier); + init_waitqueue_head(&common->fsg_wait); +#undef OR + + + /* Information */ + INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + INFO(common, "Number of LUNs=%d\n", common->nluns); pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - for (i = 0; i < fsg->nluns; ++i) { - curlun = &fsg->luns[i]; - if (backing_file_is_open(curlun)) { - p = NULL; + for (i = 0, nluns = common->nluns, curlun = common->luns; + i < nluns; + ++curlun, ++i) { + char *p = "(no medium)"; + if (fsg_lun_is_open(curlun)) { + p = "(error)"; if (pathbuf) { p = d_path(&curlun->filp->f_path, pathbuf, PATH_MAX); if (IS_ERR(p)) - p = NULL; + p = "(error)"; } - LINFO(curlun, "ro=%d, file: %s\n", - curlun->ro, (p ? p : "(error)")); } + LINFO(curlun, "LUN: %s%s%sfile: %s\n", + curlun->removable ? "removable " : "", + curlun->ro ? "read only " : "", + curlun->cdrom ? "CD-ROM " : "", + p); } kfree(pathbuf); - set_bit(REGISTERED, &fsg->atomic_bitflags); + DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); + + wake_up_process(common->thread_task); + + return common; + + +error_luns: + common->nluns = i + 1; +error_release: + common->state = FSG_STATE_TERMINATED; /* The thread is dead */ + /* Call fsg_common_release() directly, ref might be not + * initialised */ + fsg_common_release(&common->ref); + return ERR_PTR(rc); +} + + +static void fsg_common_release(struct kref *ref) +{ + struct fsg_common *common = container_of(ref, struct fsg_common, ref); + + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + wait_for_completion(&common->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&common->thread_notifier); + } + + if (likely(common->luns)) { + struct fsg_lun *lun = common->luns; + unsigned i = common->nluns; + + /* In error recovery common->nluns may be zero. */ + for (; i; --i, ++lun) { + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); + fsg_lun_close(lun); + device_unregister(&lun->dev); + } + + kfree(common->luns); + } + + { + struct fsg_buffhd *bh = common->buffhds; + unsigned i = FSG_NUM_BUFFERS; + do { + kfree(bh->buf); + } while (++bh, --i); + } + + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct fsg_common *common = fsg->common; + + DBG(fsg, "unbind\n"); + if (fsg->common->fsg == fsg) { + fsg->common->new_fsg = NULL; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + /* FIXME: make interruptible or killable somehow? */ + wait_event(common->fsg_wait, common->fsg != fsg); + } + + fsg_common_put(common); + usb_free_descriptors(fsg->function.descriptors); + usb_free_descriptors(fsg->function.hs_descriptors); + switch_dev_unregister(&fsg->sdev); + kfree(fsg); +} + + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + int i; + struct usb_ep *ep; + + fsg->gadget = gadget; + + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + return i; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_out = ep; + + /* Copy descriptors */ + f->descriptors = usb_copy_descriptors(fsg_fs_function); + if (unlikely(!f->descriptors)) + return -ENOMEM; + + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + f->hs_descriptors = usb_copy_descriptors(fsg_hs_function); + if (unlikely(!f->hs_descriptors)) { + usb_free_descriptors(f->descriptors); + return -ENOMEM; + } + } - /* Tell the thread to start working */ - wake_up_process(fsg->thread_task); return 0; autoconf_fail: ERROR(fsg, "unable to autoconfigure all endpoints\n"); - rc = -ENOTSUPP; + return -ENOTSUPP; +} -out: - DBG(fsg, "fsg_function_bind failed: %d\n", rc); - fsg->state = FSG_STATE_TERMINATED; /* The thread is dead */ - fsg_function_unbind(c, f); - close_all_backing_files(fsg); + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; + +static ssize_t print_switch_name(struct switch_dev *sdev, char *buf) +{ + return sprintf(buf, "%s\n", FUNCTION_NAME); +} + +static ssize_t print_switch_state(struct switch_dev *sdev, char *buf) +{ + struct fsg_dev *fsg = container_of(sdev, struct fsg_dev, sdev); + return sprintf(buf, "%s\n", (fsg->common->new_fsg ? "online" : "offline")); +} + +static int fsg_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) +{ + struct fsg_dev *fsg; + int rc; + + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + if (unlikely(!fsg)) + return -ENOMEM; + + the_fsg = fsg; + fsg->sdev.name = FUNCTION_NAME; + fsg->sdev.print_name = print_switch_name; + fsg->sdev.print_state = print_switch_state; + rc = switch_dev_register(&fsg->sdev); + if (rc < 0) + return rc; + +#ifdef CONFIG_USB_ANDROID_MASS_STORAGE + fsg->function.name = FUNCTION_NAME; +#else + fsg->function.name = FSG_DRIVER_DESC; +#endif + fsg->function.strings = fsg_strings_array; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + + fsg->common = common; + /* Our caller holds a reference to common structure so we + * don't have to be worry about it being freed until we return + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ + + rc = usb_add_function(c, &fsg->function); + if (unlikely(rc)) + kfree(fsg); + else + fsg_common_get(fsg->common); return rc; } -static int fsg_function_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) + + +/************************* Module parameters *************************/ + + +struct fsg_module_parameters { + char *file[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; + int removable[FSG_MAX_LUNS]; + int cdrom[FSG_MAX_LUNS]; + + unsigned int file_count, ro_count, removable_count, cdrom_count; + unsigned int luns; /* nluns */ + int stall; /* can_stall */ +}; + + +#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ + module_param_array_named(prefix ## name, params.name, type, \ + &prefix ## params.name ## _count, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ + module_param_named(prefix ## name, params.name, type, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ + "names of backing files or devices"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ + "true to force read-only"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ + "true to simulate removable media"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ + "true to simulate CD-ROM instead of disk"); \ + _FSG_MODULE_PARAM(prefix, params, luns, uint, \ + "number of LUNs"); \ + _FSG_MODULE_PARAM(prefix, params, stall, bool, \ + "false to prevent bulk stalls") + + +static void +fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params) { - struct fsg_dev *fsg = func_to_dev(f); - DBG(fsg, "fsg_function_set_alt intf: %d alt: %d\n", intf, alt); - fsg->new_config = 1; - do_set_interface(fsg, 0); - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); - return 0; + struct fsg_lun_config *lun; + unsigned i; + + /* Configure LUNs */ + cfg->nluns = + min(params->luns ?: (params->file_count ?: 1u), + (unsigned)FSG_MAX_LUNS); + for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) { + lun->ro = !!params->ro[i]; + lun->cdrom = !!params->cdrom[i]; + lun->removable = /* Removable by default */ + params->removable_count <= i || params->removable[i]; + lun->filename = + params->file_count > i && params->file[i][0] + ? params->file[i] + : 0; + } + + /* Let MSF use defaults */ + cfg->lun_name_format = 0; + cfg->thread_name = 0; + cfg->vendor_name = 0; + cfg->product_name = 0; + cfg->release = 0xffff; + + cfg->thread_exits = 0; + cfg->private_data = 0; + + /* Finalise */ + cfg->can_stall = params->stall; } -static void fsg_function_disable(struct usb_function *f) +static inline struct fsg_common * +fsg_common_from_params(struct fsg_common *common, + struct usb_composite_dev *cdev, + const struct fsg_module_parameters *params) + __attribute__((unused)); +static inline struct fsg_common * +fsg_common_from_params(struct fsg_common *common, + struct usb_composite_dev *cdev, + const struct fsg_module_parameters *params) { - struct fsg_dev *fsg = func_to_dev(f); - DBG(fsg, "fsg_function_disable\n"); - if (fsg->new_config) - do_set_interface(fsg, -1); - fsg->new_config = 0; - raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); + struct fsg_config cfg; + fsg_config_from_params(&cfg, params); + return fsg_common_init(common, cdev, &cfg); } -static int __init fsg_probe(struct platform_device *pdev) +#ifdef CONFIG_USB_ANDROID_MASS_STORAGE + +static struct fsg_config fsg_cfg; + +static int fsg_probe(struct platform_device *pdev) { struct usb_mass_storage_platform_data *pdata = pdev->dev.platform_data; - struct fsg_dev *fsg = the_fsg; + int i, nluns; - fsg->pdev = pdev; - printk(KERN_INFO "fsg_probe pdata: %p\n", pdata); + printk(KERN_INFO "fsg_probe pdev: %p, pdata: %p\n", pdev, pdata); + if (!pdata) + return -1; - if (pdata) { - if (pdata->vendor) - fsg->vendor = pdata->vendor; + nluns = pdata->nluns; + if (nluns > FSG_MAX_LUNS) + nluns = FSG_MAX_LUNS; + fsg_cfg.nluns = nluns; + for (i = 0; i < nluns; i++) + fsg_cfg.luns[i].removable = 1; - if (pdata->product) - fsg->product = pdata->product; - - if (pdata->release) - fsg->release = pdata->release; - fsg->nluns = pdata->nluns; - fsg->cdrom_lun = pdata->cdrom_lun; - } + fsg_cfg.vendor_name = pdata->vendor; + fsg_cfg.product_name = pdata->product; + fsg_cfg.release = pdata->release; + fsg_cfg.can_stall = 0; + fsg_cfg.pdev = pdev; return 0; } static struct platform_driver fsg_platform_driver = { - .driver = { .name = "usb_mass_storage", }, + .driver = { .name = FUNCTION_NAME, }, .probe = fsg_probe, }; int mass_storage_bind_config(struct usb_configuration *c) { - int rc; - struct fsg_dev *fsg; - - printk(KERN_INFO "mass_storage_bind_config\n"); - rc = fsg_alloc(); - if (rc) - return rc; - fsg = the_fsg; - - spin_lock_init(&fsg->lock); - init_rwsem(&fsg->filesem); - kref_init(&fsg->ref); - init_completion(&fsg->thread_notifier); - - the_fsg->buf_size = BULK_BUFFER_SIZE; - - the_fsg->sdev.name = DRIVER_NAME; - the_fsg->sdev.print_name = print_switch_name; - the_fsg->sdev.print_state = print_switch_state; - rc = switch_dev_register(&the_fsg->sdev); - if (rc < 0) - goto err_switch_dev_register; - - rc = platform_driver_register(&fsg_platform_driver); - if (rc != 0) - goto err_platform_driver_register; - - wake_lock_init(&the_fsg->wake_lock, WAKE_LOCK_SUSPEND, - "usb_mass_storage"); - - fsg->cdev = c->cdev; - fsg->function.name = shortname; - fsg->function.descriptors = fs_function; - fsg->function.bind = fsg_function_bind; - fsg->function.unbind = fsg_function_unbind; - fsg->function.setup = fsg_function_setup; - fsg->function.set_alt = fsg_function_set_alt; - fsg->function.disable = fsg_function_disable; - - usb_register_notifier(&connect_status_notifier); - rc = usb_add_function(c, &fsg->function); - if (rc != 0) - goto err_usb_add_function; - - - return 0; - -err_usb_add_function: - wake_lock_destroy(&the_fsg->wake_lock); - platform_driver_unregister(&fsg_platform_driver); -err_platform_driver_register: - switch_dev_unregister(&the_fsg->sdev); -err_switch_dev_register: - kref_put(&the_fsg->ref, fsg_release); - - return rc; + struct fsg_common *common = fsg_common_init(NULL, c->cdev, &fsg_cfg); + if (IS_ERR(common)) + return -1; + return fsg_add(c->cdev, c, common); } static struct android_usb_function mass_storage_function = { - .name = "usb_mass_storage", + .name = FUNCTION_NAME, .bind_config = mass_storage_bind_config, }; static int __init init(void) { + int rc; printk(KERN_INFO "f_mass_storage init\n"); + rc = platform_driver_register(&fsg_platform_driver); + if (rc != 0) + return rc; android_register_function(&mass_storage_function); return 0; -} -module_init(init); +}module_init(init); + +#endif /* CONFIG_USB_ANDROID_MASS_STORAGE */ diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c new file mode 100644 index 00000000..c0e67a2d --- /dev/null +++ b/drivers/usb/gadget/f_mtp.c @@ -0,0 +1,1265 @@ +/* + * Gadget Function Driver for MTP + * + * Copyright (C) 2010 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BULK_BUFFER_SIZE 16384 +#define INTR_BUFFER_SIZE 28 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* values for mtp_dev.state */ +#define STATE_OFFLINE 0 /* initial state, disconnected */ +#define STATE_READY 1 /* ready for userspace calls */ +#define STATE_BUSY 2 /* processing userspace calls */ +#define STATE_CANCELED 3 /* transaction canceled by host */ +#define STATE_ERROR 4 /* error from completion routine */ + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 + +/* ID for Microsoft MTP OS String */ +#define MTP_OS_STRING_ID 0xEE + +/* MTP class reqeusts */ +#define MTP_REQ_CANCEL 0x64 +#define MTP_REQ_GET_EXT_EVENT_DATA 0x65 +#define MTP_REQ_RESET 0x66 +#define MTP_REQ_GET_DEVICE_STATUS 0x67 + +/* constants for device status */ +#define MTP_RESPONSE_OK 0x2001 +#define MTP_RESPONSE_DEVICE_BUSY 0x2019 + +static const char shortname[] = "mtp_usb"; + +struct mtp_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + /* appear as MTP or PTP when enumerating */ + int interface_mode; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + struct usb_ep *ep_intr; + + int state; + + /* synchronize access to our device file */ + atomic_t open_excl; + /* to enforce only one ioctl at a time */ + atomic_t ioctl_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + wait_queue_head_t intr_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + struct usb_request *intr_req; + int rx_done; + /* true if interrupt endpoint is busy */ + int intr_busy; + + /* for processing MTP_SEND_FILE and MTP_RECEIVE_FILE + * ioctls on a work queue + */ + struct workqueue_struct *wq; + struct work_struct send_file_work; + struct work_struct receive_file_work; + struct file *xfer_file; + loff_t xfer_file_offset; + int64_t xfer_file_length; + int xfer_result; +}; + +static struct usb_interface_descriptor mtp_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_interface_descriptor ptp_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_STILL_IMAGE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, +}; + +static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_intr_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), + .bInterval = 6, +}; + +static struct usb_descriptor_header *fs_mtp_descs[] = { + (struct usb_descriptor_header *) &mtp_interface_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_mtp_descs[] = { + (struct usb_descriptor_header *) &mtp_interface_desc, + (struct usb_descriptor_header *) &mtp_highspeed_in_desc, + (struct usb_descriptor_header *) &mtp_highspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_ptp_descs[] = { + (struct usb_descriptor_header *) &ptp_interface_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_ptp_descs[] = { + (struct usb_descriptor_header *) &ptp_interface_desc, + (struct usb_descriptor_header *) &mtp_highspeed_in_desc, + (struct usb_descriptor_header *) &mtp_highspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_string mtp_string_defs[] = { + /* Naming interface "MTP" so libmtp will recognize us */ + [INTERFACE_STRING_INDEX].s = "MTP", + { }, /* end of list */ +}; + +static struct usb_gadget_strings mtp_string_table = { + .language = 0x0409, /* en-US */ + .strings = mtp_string_defs, +}; + +static struct usb_gadget_strings *mtp_strings[] = { + &mtp_string_table, + NULL, +}; + +/* Microsoft MTP OS String */ +static u8 mtp_os_string[] = { + 18, /* sizeof(mtp_os_string) */ + USB_DT_STRING, + /* Signature field: "MSFT100" */ + 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, + /* vendor code */ + 1, + /* padding */ + 0 +}; + +/* Microsoft Extended Configuration Descriptor Header Section */ +struct mtp_ext_config_desc_header { + __le32 dwLength; + __u16 bcdVersion; + __le16 wIndex; + __u8 bCount; + __u8 reserved[7]; +}; + +/* Microsoft Extended Configuration Descriptor Function Section */ +struct mtp_ext_config_desc_function { + __u8 bFirstInterfaceNumber; + __u8 bInterfaceCount; + __u8 compatibleID[8]; + __u8 subCompatibleID[8]; + __u8 reserved[6]; +}; + +/* MTP Extended Configuration Descriptor */ +struct { + struct mtp_ext_config_desc_header header; + struct mtp_ext_config_desc_function function; +} mtp_ext_config_desc = { + .header = { + .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), + .bcdVersion = __constant_cpu_to_le16(0x0100), + .wIndex = __constant_cpu_to_le16(4), + .bCount = __constant_cpu_to_le16(1), + }, + .function = { + .bFirstInterfaceNumber = 0, + .bInterfaceCount = 1, + .compatibleID = { 'M', 'T', 'P' }, + }, +}; + +struct mtp_device_status { + __le16 wLength; + __le16 wCode; +}; + +/* temporary variable used between mtp_open() and mtp_gadget_bind() */ +static struct mtp_dev *_mtp_dev; + +static inline struct mtp_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct mtp_dev, function); +} + +static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int _lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void _unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +static void req_put(struct mtp_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct mtp_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + if (req->status != 0) + dev->state = STATE_ERROR; + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + dev->rx_done = 1; + if (req->status != 0) + dev->state = STATE_ERROR; + + wake_up(&dev->read_wq); +} + +static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", req->status, req->actual); + dev->intr_busy = 0; + if (req->status != 0) + dev->state = STATE_ERROR; + + wake_up(&dev->intr_wq); +} + +static int __init create_bulk_endpoints(struct mtp_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc, + struct usb_endpoint_descriptor *intr_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, intr_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_intr = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_in; + req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_out; + dev->rx_req[i] = req; + } + req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_intr; + dev->intr_req = req; + + return 0; + +fail: + printk(KERN_ERR "mtp_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t mtp_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct mtp_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int r = count, xfer; + int ret = 0; + + DBG(cdev, "mtp_read(%d)\n", count); + + if (count > BULK_BUFFER_SIZE) + return -EINVAL; + + /* we will block until we're online */ + DBG(cdev, "mtp_read: waiting for online state\n"); + ret = wait_event_interruptible(dev->read_wq, + dev->state != STATE_OFFLINE); + if (ret < 0) { + r = ret; + goto done; + } + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + return -ECANCELED; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + DBG(cdev, "rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + goto done; + } + if (dev->state == STATE_BUSY) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + DBG(cdev, "rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + r = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + + DBG(cdev, "mtp_read returning %d\n", r); + return r; +} + +static ssize_t mtp_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct mtp_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + int r = count, xfer; + int sendZLP = 0; + int ret; + + DBG(cdev, "mtp_write(%d)\n", count); + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + return -ECANCELED; + } + if (dev->state == STATE_OFFLINE) { + spin_unlock_irq(&dev->lock); + return -ENODEV; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + + /* we need to send a zero length packet to signal the end of transfer + * if the transfer size is aligned to a packet boundary. + */ + if ((count & (dev->ep_in->maxpacket - 1)) == 0) { + sendZLP = 1; + } + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + + if (dev->state != STATE_BUSY) { + DBG(cdev, "mtp_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) + || dev->state != STATE_BUSY)); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (xfer && copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "mtp_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + r = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + + DBG(cdev, "mtp_write returning %d\n", r); + return r; +} + +/* read from a local file and write to USB */ +static void send_file_work(struct work_struct *data) { + struct mtp_dev *dev = container_of(data, struct mtp_dev, send_file_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + struct file *filp; + loff_t offset; + int64_t count; + int xfer, ret; + int r = 0; + int sendZLP = 0; + + /* read our parameters */ + smp_rmb(); + filp = dev->xfer_file; + offset = dev->xfer_file_offset; + count = dev->xfer_file_length; + + DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); + + /* we need to send a zero length packet to signal the end of transfer + * if the transfer size is aligned to a packet boundary. + */ + if ((dev->xfer_file_length & (dev->ep_in->maxpacket - 1)) == 0) { + sendZLP = 1; + } + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + (req = req_get(dev, &dev->tx_idle)) + || dev->state != STATE_BUSY); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + ret = vfs_read(filp, req->buf, xfer, &offset); + if (ret < 0) { + r = ret; + break; + } + xfer = ret; + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "send_file_work: xfer error %d\n", ret); + dev->state = STATE_ERROR; + r = -EIO; + break; + } + + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + DBG(cdev, "send_file_work returning %d\n", r); + /* write the result */ + dev->xfer_result = r; + smp_wmb(); +} + +/* read from USB and write to a local file */ +static void receive_file_work(struct work_struct *data) +{ + struct mtp_dev *dev = container_of(data, struct mtp_dev, receive_file_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *read_req = NULL, *write_req = NULL; + struct file *filp; + loff_t offset; + int64_t count; + int ret, cur_buf = 0; + int r = 0; + + /* read our parameters */ + smp_rmb(); + filp = dev->xfer_file; + offset = dev->xfer_file_offset; + count = dev->xfer_file_length; + + DBG(cdev, "receive_file_work(%lld)\n", count); + + while (count > 0 || write_req) { + if (count > 0) { + /* queue a request */ + read_req = dev->rx_req[cur_buf]; + cur_buf = (cur_buf + 1) % RX_REQ_MAX; + + read_req->length = (count > BULK_BUFFER_SIZE + ? BULK_BUFFER_SIZE : count); + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + dev->state = STATE_ERROR; + break; + } + } + + if (write_req) { + DBG(cdev, "rx %p %d\n", write_req, write_req->actual); + ret = vfs_write(filp, write_req->buf, write_req->actual, + &offset); + DBG(cdev, "vfs_write %d\n", ret); + if (ret != write_req->actual) { + r = -EIO; + dev->state = STATE_ERROR; + break; + } + write_req = NULL; + } + + if (read_req) { + /* wait for our last read to complete */ + ret = wait_event_interruptible(dev->read_wq, + dev->rx_done || dev->state != STATE_BUSY); + if (ret < 0 || dev->state != STATE_BUSY) { + r = ret; + break; + } + /* if xfer_file_length is 0xFFFFFFFF, then we read until + * we get a zero length packet + */ + if (count != 0xFFFFFFFF) + count -= read_req->actual; + if (read_req->actual < read_req->length) { + /* short packet is used to signal EOF for sizes > 4 gig */ + DBG(cdev, "got short packet\n"); + count = 0; + } + + write_req = read_req; + read_req = NULL; + } + } + + DBG(cdev, "receive_file_work returning %d\n", r); + /* write the result */ + dev->xfer_result = r; + smp_wmb(); +} + +static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) +{ + struct usb_request *req; + int ret; + int length = event->length; + + DBG(dev->cdev, "mtp_send_event(%d)\n", event->length); + + if (length < 0 || length > INTR_BUFFER_SIZE) + return -EINVAL; + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->intr_wq, !dev->intr_busy || dev->state == STATE_OFFLINE); + if (ret < 0) + return ret; + if (dev->state == STATE_OFFLINE) + return -ENODEV; + req = dev->intr_req; + if (copy_from_user(req->buf, (void __user *)event->data, length)) + return -EFAULT; + req->length = length; + dev->intr_busy = 1; + ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL); + if (ret) + dev->intr_busy = 0; + + return ret; +} + +static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct mtp_dev *dev = fp->private_data; + struct file *filp = NULL; + int ret = -EINVAL; + + if (_lock(&dev->ioctl_excl)) + return -EBUSY; + + switch (code) { + case MTP_SEND_FILE: + case MTP_RECEIVE_FILE: + { + struct mtp_file_range mfr; + struct work_struct *work; + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + ret = -ECANCELED; + goto out; + } + if (dev->state == STATE_OFFLINE) { + spin_unlock_irq(&dev->lock); + ret = -ENODEV; + goto out; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + + if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { + ret = -EFAULT; + goto fail; + } + /* hold a reference to the file while we are working with it */ + filp = fget(mfr.fd); + if (!filp) { + ret = -EBADF; + goto fail; + } + + /* write the parameters */ + dev->xfer_file = filp; + dev->xfer_file_offset = mfr.offset; + dev->xfer_file_length = mfr.length; + smp_wmb(); + + if (code == MTP_SEND_FILE) + work = &dev->send_file_work; + else + work = &dev->receive_file_work; + + /* We do the file transfer on a work queue so it will run + * in kernel context, which is necessary for vfs_read and + * vfs_write to use our buffers in the kernel address space. + */ + queue_work(dev->wq, work); + /* wait for operation to complete */ + flush_workqueue(dev->wq); + fput(filp); + + /* read the result */ + smp_rmb(); + ret = dev->xfer_result; + break; + } + case MTP_SET_INTERFACE_MODE: + if (value == MTP_INTERFACE_MODE_MTP || + value == MTP_INTERFACE_MODE_PTP) { + dev->interface_mode = value; + if (value == MTP_INTERFACE_MODE_PTP) { + dev->function.descriptors = fs_ptp_descs; + dev->function.hs_descriptors = hs_ptp_descs; + } else { + dev->function.descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + } + ret = 0; + } + break; + case MTP_SEND_EVENT: + { + struct mtp_event event; + /* return here so we don't change dev->state below, + * which would interfere with bulk transfer state. + */ + if (copy_from_user(&event, (void __user *)value, sizeof(event))) + ret = -EFAULT; + else + ret = mtp_send_event(dev, &event); + goto out; + } + } + +fail: + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + ret = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); +out: + _unlock(&dev->ioctl_excl); + DBG(dev->cdev, "ioctl returning %d\n", ret); + return ret; +} + +static int mtp_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "mtp_open\n"); + if (_lock(&_mtp_dev->open_excl)) + return -EBUSY; + + /* clear any error condition */ + if (_mtp_dev->state != STATE_OFFLINE) + _mtp_dev->state = STATE_READY; + + fp->private_data = _mtp_dev; + return 0; +} + +static int mtp_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "mtp_release\n"); + + _unlock(&_mtp_dev->open_excl); + return 0; +} + +/* file operations for /dev/mtp_usb */ +static const struct file_operations mtp_fops = { + .owner = THIS_MODULE, + .read = mtp_read, + .write = mtp_write, + .unlocked_ioctl = mtp_ioctl, + .open = mtp_open, + .release = mtp_release, +}; + +static struct miscdevice mtp_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = shortname, + .fops = &mtp_fops, +}; + +static int +mtp_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct mtp_dev *dev = func_to_dev(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "mtp_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + mtp_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, + &mtp_fullspeed_out_desc, &mtp_intr_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + mtp_highspeed_in_desc.bEndpointAddress = + mtp_fullspeed_in_desc.bEndpointAddress; + mtp_highspeed_out_desc.bEndpointAddress = + mtp_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct mtp_dev *dev = func_to_dev(f); + struct usb_request *req; + int i; + + spin_lock_irq(&dev->lock); + while ((req = req_get(dev, &dev->tx_idle))) + mtp_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + mtp_request_free(dev->rx_req[i], dev->ep_out); + mtp_request_free(dev->intr_req, dev->ep_intr); + dev->state = STATE_OFFLINE; + spin_unlock_irq(&dev->lock); + wake_up(&dev->intr_wq); + + misc_deregister(&mtp_device); + kfree(_mtp_dev); + _mtp_dev = NULL; +} + +static int mtp_function_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct mtp_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; + + /* do nothing if we are disabled */ + if (dev->function.disabled) + return value; + + VDBG(cdev, "mtp_function_setup " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* Handle MTP OS string */ + if (dev->interface_mode == MTP_INTERFACE_MODE_MTP + && ctrl->bRequestType == + (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) + && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR + && (w_value >> 8) == USB_DT_STRING + && (w_value & 0xFF) == MTP_OS_STRING_ID) { + value = (w_length < sizeof(mtp_os_string) + ? w_length : sizeof(mtp_os_string)); + memcpy(cdev->req->buf, mtp_os_string, value); + /* return here since composite.c will send for us */ + return value; + } + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + /* Handle MTP OS descriptor */ + DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", + ctrl->bRequest, w_index, w_value, w_length); + + if (dev->interface_mode == MTP_INTERFACE_MODE_MTP + && ctrl->bRequest == 1 + && (ctrl->bRequestType & USB_DIR_IN) + && (w_index == 4 || w_index == 5)) { + value = (w_length < sizeof(mtp_ext_config_desc) ? + w_length : sizeof(mtp_ext_config_desc)); + memcpy(cdev->req->buf, &mtp_ext_config_desc, value); + } + } + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + DBG(cdev, "class request: %d index: %d value: %d length: %d\n", + ctrl->bRequest, w_index, w_value, w_length); + + if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0 + && w_value == 0) { + DBG(cdev, "MTP_REQ_CANCEL\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state == STATE_BUSY) { + dev->state = STATE_CANCELED; + wake_up(&dev->read_wq); + wake_up(&dev->write_wq); + } + spin_unlock_irqrestore(&dev->lock, flags); + + /* We need to queue a request to read the remaining + * bytes, but we don't actually need to look at + * the contents. + */ + value = w_length; + } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS + && w_index == 0 && w_value == 0) { + struct mtp_device_status *status = cdev->req->buf; + status->wLength = + __constant_cpu_to_le16(sizeof(*status)); + + DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n"); + spin_lock_irqsave(&dev->lock, flags); + /* device status is "busy" until we report + * the cancelation to userspace + */ + if (dev->state == STATE_BUSY + || dev->state == STATE_CANCELED) + status->wCode = + __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); + else + status->wCode = + __cpu_to_le16(MTP_RESPONSE_OK); + spin_unlock_irqrestore(&dev->lock, flags); + value = sizeof(*status); + } + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + int rc; + cdev->req->zero = value < w_length; + cdev->req->length = value; + rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (rc < 0) + ERROR(cdev, "%s setup response queue error\n", __func__); + } + + if (value == -EOPNOTSUPP) + VDBG(cdev, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; +} + +static int mtp_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct mtp_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt); + ret = usb_ep_enable(dev->ep_in, + ep_choose(cdev->gadget, + &mtp_highspeed_in_desc, + &mtp_fullspeed_in_desc)); + if (ret) + return ret; + ret = usb_ep_enable(dev->ep_out, + ep_choose(cdev->gadget, + &mtp_highspeed_out_desc, + &mtp_fullspeed_out_desc)); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + ret = usb_ep_enable(dev->ep_intr, &mtp_intr_desc); + if (ret) { + usb_ep_disable(dev->ep_out); + usb_ep_disable(dev->ep_in); + return ret; + } + dev->state = STATE_READY; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void mtp_function_disable(struct usb_function *f) +{ + struct mtp_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "mtp_function_disable\n"); + dev->state = STATE_OFFLINE; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + usb_ep_disable(dev->ep_intr); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + wake_up(&dev->intr_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int mtp_bind_config(struct usb_configuration *c) +{ + struct mtp_dev *dev; + int ret = 0; + + printk(KERN_INFO "mtp_bind_config\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* allocate a string ID for our interface */ + if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; + mtp_interface_desc.iInterface = ret; + } + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + init_waitqueue_head(&dev->intr_wq); + atomic_set(&dev->open_excl, 0); + atomic_set(&dev->ioctl_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + + dev->wq = create_singlethread_workqueue("f_mtp"); + if (!dev->wq) + goto err1; + INIT_WORK(&dev->send_file_work, send_file_work); + INIT_WORK(&dev->receive_file_work, receive_file_work); + + dev->cdev = c->cdev; + dev->function.name = "mtp"; + dev->function.strings = mtp_strings, + dev->function.descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + dev->function.bind = mtp_function_bind; + dev->function.unbind = mtp_function_unbind; + dev->function.setup = mtp_function_setup; + dev->function.set_alt = mtp_function_set_alt; + dev->function.disable = mtp_function_disable; + + /* MTP mode by default */ + dev->interface_mode = MTP_INTERFACE_MODE_MTP; + + /* _mtp_dev must be set before calling usb_gadget_register_driver */ + _mtp_dev = dev; + + ret = misc_register(&mtp_device); + if (ret) + goto err1; + + ret = usb_add_function(c, &dev->function); + if (ret) + goto err2; + + return 0; + +err2: + misc_deregister(&mtp_device); +err1: + if (dev->wq) + destroy_workqueue(dev->wq); + kfree(dev); + printk(KERN_ERR "mtp gadget driver failed to initialize\n"); + return ret; +} + +static struct android_usb_function mtp_function = { + .name = "mtp", + .bind_config = mtp_bind_config, +}; + +static int __init init(void) +{ + printk(KERN_INFO "f_mtp init\n"); + android_register_function(&mtp_function); + return 0; +} +module_init(init); diff --git a/drivers/usb/gadget/f_mtp.h b/drivers/usb/gadget/f_mtp.h new file mode 100644 index 00000000..e4fd8806 --- /dev/null +++ b/drivers/usb/gadget/f_mtp.h @@ -0,0 +1,53 @@ +/* + * Gadget Function Driver for MTP + * + * Copyright (C) 2010 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_USB_F_MTP_H +#define __LINUX_USB_F_MTP_H + +/* Constants for MTP_SET_INTERFACE_MODE */ +#define MTP_INTERFACE_MODE_MTP 0 +#define MTP_INTERFACE_MODE_PTP 1 + + +struct mtp_file_range { + /* file descriptor for file to transfer */ + int fd; + /* offset in file for start of transfer */ + loff_t offset; + /* number of bytes to transfer */ + int64_t length; +}; + +struct mtp_event { + /* size of the event */ + size_t length; + /* event data to send */ + void *data; +}; + +/* Sends the specified file range to the host */ +#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range) +/* Receives data from the host and writes it to a file. + * The file is created if it does not exist. + */ +#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range) +/* Sets the driver mode to either MTP or PTP */ +#define MTP_SET_INTERFACE_MODE _IOW('M', 2, int) +/* Sends an event to the host via the interrupt endpoint */ +#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event) + +#endif /* __LINUX_USB_F_MTP_H */ diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 689093b2..2d9d97ec 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -4,6 +4,8 @@ * Copyright (C) 2003-2005,2008 David Brownell * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +24,7 @@ /* #define VERBOSE_DEBUG */ +#include #include #include #include @@ -120,7 +123,7 @@ static unsigned int bitrate(struct usb_gadget *g) /* interface descriptor: */ -static struct usb_interface_descriptor rndis_control_intf __initdata = { +static struct usb_interface_descriptor rndis_control_intf = { .bLength = sizeof rndis_control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -130,7 +133,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = { #ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS /* "Wireless" RNDIS; auto-detected by Windows */ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER, - .bInterfaceSubClass = 1, + .bInterfaceSubClass = 1, .bInterfaceProtocol = 3, #else .bInterfaceClass = USB_CLASS_COMM, @@ -140,7 +143,7 @@ static struct usb_interface_descriptor rndis_control_intf __initdata = { /* .iInterface = DYNAMIC */ }; -static struct usb_cdc_header_desc header_desc __initdata = { +static struct usb_cdc_header_desc header_desc = { .bLength = sizeof header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -148,7 +151,7 @@ static struct usb_cdc_header_desc header_desc __initdata = { .bcdCDC = cpu_to_le16(0x0110), }; -static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { +static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { .bLength = sizeof call_mgmt_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, @@ -157,15 +160,15 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { .bDataInterface = 0x01, }; -static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { - .bLength = sizeof acm_descriptor, +static struct usb_cdc_acm_descriptor rndis_acm_descriptor = { + .bLength = sizeof rndis_acm_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ACM_TYPE, .bmCapabilities = 0x00, }; -static struct usb_cdc_union_desc rndis_union_desc __initdata = { +static struct usb_cdc_union_desc rndis_union_desc = { .bLength = sizeof(rndis_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_UNION_TYPE, @@ -175,7 +178,7 @@ static struct usb_cdc_union_desc rndis_union_desc __initdata = { /* the data interface has two bulk endpoints */ -static struct usb_interface_descriptor rndis_data_intf __initdata = { +static struct usb_interface_descriptor rndis_data_intf = { .bLength = sizeof rndis_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -187,9 +190,23 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = { /* .iInterface = DYNAMIC */ }; + +static struct usb_interface_assoc_descriptor +rndis_iad_descriptor = { + .bLength = sizeof rndis_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, /* XXX, hardcoded */ + .bInterfaceCount = 2, // control + data + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + /* full speed support: */ -static struct usb_endpoint_descriptor fs_notify_desc __initdata = { +static struct usb_endpoint_descriptor fs_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -199,7 +216,7 @@ static struct usb_endpoint_descriptor fs_notify_desc __initdata = { .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, }; -static struct usb_endpoint_descriptor fs_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -207,7 +224,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor fs_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -215,12 +232,13 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = { .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *eth_fs_function[] __initdata = { +static struct usb_descriptor_header *eth_fs_function[] = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &fs_notify_desc, /* data interface has no altsetting */ @@ -232,7 +250,7 @@ static struct usb_descriptor_header *eth_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor hs_notify_desc __initdata = { +static struct usb_endpoint_descriptor hs_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -241,7 +259,7 @@ static struct usb_endpoint_descriptor hs_notify_desc __initdata = { .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, }; -static struct usb_endpoint_descriptor hs_in_desc __initdata = { +static struct usb_endpoint_descriptor hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -250,7 +268,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor hs_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -259,12 +277,13 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = { .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *eth_hs_function[] __initdata = { +static struct usb_descriptor_header *eth_hs_function[] = { + (struct usb_descriptor_header *) &rndis_iad_descriptor, /* control interface matches ACM, not Ethernet */ (struct usb_descriptor_header *) &rndis_control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &call_mgmt_descriptor, - (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &rndis_acm_descriptor, (struct usb_descriptor_header *) &rndis_union_desc, (struct usb_descriptor_header *) &hs_notify_desc, /* data interface has no altsetting */ @@ -279,6 +298,7 @@ static struct usb_descriptor_header *eth_hs_function[] __initdata = { static struct usb_string rndis_string_defs[] = { [0].s = "RNDIS Communications Control", [1].s = "RNDIS Ethernet Data", + [2].s = "RNDIS", { } /* end of list */ }; @@ -586,7 +606,7 @@ static void rndis_close(struct gether *geth) /* ethernet function driver setup/binding */ -static int __init +static int rndis_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -599,6 +619,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (status < 0) goto fail; rndis->ctrl_id = status; + rndis_iad_descriptor.bFirstInterface = status; rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; @@ -762,10 +783,6 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) /* Some controllers can't support RNDIS ... */ static inline bool can_support_rndis(struct usb_configuration *c) { - /* only two endpoints on sa1100 */ - if (gadget_is_sa1100(c->cdev->gadget)) - return false; - /* everything else is *presumably* fine */ return true; } @@ -782,7 +799,8 @@ static inline bool can_support_rndis(struct usb_configuration *c) * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) +int +rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) { struct f_rndis *rndis; int status; @@ -811,6 +829,13 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) return status; rndis_string_defs[1].id = status; rndis_data_intf.iInterface = status; + + /* IAD iFunction label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_string_defs[2].id = status; + rndis_iad_descriptor.iFunction = status; } /* allocate and initialize one new instance */ @@ -840,7 +865,7 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) #ifdef CONFIG_USB_ANDROID_RNDIS /* start disabled */ - rndis->port.func.hidden = 1; + rndis->port.func.disabled = 1; #endif status = usb_add_function(c, &rndis->port.func); @@ -855,7 +880,7 @@ fail: #ifdef CONFIG_USB_ANDROID_RNDIS #include "rndis.c" -static int __init rndis_probe(struct platform_device *pdev) +static int rndis_probe(struct platform_device *pdev) { rndis_pdata = pdev->dev.platform_data; return 0; diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 68ee7acb..490b00b0 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -10,14 +10,14 @@ * either version 2 of that License or (at your option) any later version. */ +#include #include #include -#include #include "u_serial.h" #include "gadget_chips.h" -#define CONFIG_MODEM_SUPPORT + /* * This function packages a simple "generic serial" port with no real * control mechanisms, just raw data transfer over two bulk endpoints. @@ -30,187 +30,78 @@ struct gser_descs { struct usb_endpoint_descriptor *in; struct usb_endpoint_descriptor *out; -#ifdef CONFIG_MODEM_SUPPORT - struct usb_endpoint_descriptor *notify; -#endif }; struct f_gser { struct gserial port; u8 data_id; u8 port_num; - u8 disabled; - u8 configured; struct gser_descs fs; struct gser_descs hs; - u8 online; -#ifdef CONFIG_MODEM_SUPPORT - u8 pending; - spinlock_t lock; - struct usb_ep *notify; - struct usb_endpoint_descriptor *notify_desc; - struct usb_request *notify_req; - - struct usb_cdc_line_coding port_line_coding; - - /* SetControlLineState request */ - u16 port_handshake_bits; -#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */ -#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */ - - /* SerialState notification */ - u16 serial_state; -#define ACM_CTRL_OVERRUN (1 << 6) -#define ACM_CTRL_PARITY (1 << 5) -#define ACM_CTRL_FRAMING (1 << 4) -#define ACM_CTRL_RI (1 << 3) -#define ACM_CTRL_BRK (1 << 2) -#define ACM_CTRL_DSR (1 << 1) -#define ACM_CTRL_DCD (1 << 0) -#endif }; -static struct usb_function *modem_function; -static struct usb_function *serial_function; static inline struct f_gser *func_to_gser(struct usb_function *f) { return container_of(f, struct f_gser, port.func); } -#ifdef CONFIG_MODEM_SUPPORT -static inline struct f_gser *port_to_gser(struct gserial *p) -{ - return container_of(p, struct f_gser, port); -} -#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */ -#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */ -#endif /*-------------------------------------------------------------------------*/ /* interface descriptor: */ -static struct usb_interface_descriptor gser_interface_desc = { +static struct usb_interface_descriptor gser_interface_desc __initdata = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ -#ifdef CONFIG_MODEM_SUPPORT - .bNumEndpoints = 3, -#else .bNumEndpoints = 2, -#endif .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, /* .iInterface = DYNAMIC */ }; -#ifdef CONFIG_MODEM_SUPPORT -static struct usb_cdc_header_desc gser_header_desc = { - .bLength = sizeof(gser_header_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_HEADER_TYPE, - .bcdCDC = __constant_cpu_to_le16(0x0110), -}; -static struct usb_cdc_call_mgmt_descriptor -gser_call_mgmt_descriptor = { - .bLength = sizeof(gser_call_mgmt_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, - .bmCapabilities = 0, - /* .bDataInterface = DYNAMIC */ -}; - -static struct usb_cdc_acm_descriptor gser_descriptor = { - .bLength = sizeof(gser_descriptor), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_ACM_TYPE, - .bmCapabilities = USB_CDC_CAP_LINE, -}; - -static struct usb_cdc_union_desc gser_union_desc = { - .bLength = sizeof(gser_union_desc), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_UNION_TYPE, - /* .bMasterInterface0 = DYNAMIC */ - /* .bSlaveInterface0 = DYNAMIC */ -}; -#endif /* full speed support: */ -#ifdef CONFIG_MODEM_SUPPORT -static struct usb_endpoint_descriptor gser_fs_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, -}; -#endif -static struct usb_endpoint_descriptor gser_fs_in_desc = { +static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_endpoint_descriptor gser_fs_out_desc = { +static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static struct usb_descriptor_header *gser_fs_function[] = { +static struct usb_descriptor_header *gser_fs_function[] __initdata = { (struct usb_descriptor_header *) &gser_interface_desc, -#ifdef CONFIG_MODEM_SUPPORT - (struct usb_descriptor_header *) &gser_header_desc, - (struct usb_descriptor_header *) &gser_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gser_descriptor, - (struct usb_descriptor_header *) &gser_union_desc, - (struct usb_descriptor_header *) &gser_fs_notify_desc, -#endif (struct usb_descriptor_header *) &gser_fs_in_desc, (struct usb_descriptor_header *) &gser_fs_out_desc, NULL, }; /* high speed support: */ -#ifdef CONFIG_MODEM_SUPPORT -static struct usb_endpoint_descriptor gser_hs_notify_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET), - .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, -}; -#endif -static struct usb_endpoint_descriptor gser_hs_in_desc = { +static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), + .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_endpoint_descriptor gser_hs_out_desc = { +static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), + .wMaxPacketSize = cpu_to_le16(512), }; -static struct usb_descriptor_header *gser_hs_function[] = { +static struct usb_descriptor_header *gser_hs_function[] __initdata = { (struct usb_descriptor_header *) &gser_interface_desc, -#ifdef CONFIG_MODEM_SUPPORT - (struct usb_descriptor_header *) &gser_header_desc, - (struct usb_descriptor_header *) &gser_call_mgmt_descriptor, - (struct usb_descriptor_header *) &gser_descriptor, - (struct usb_descriptor_header *) &gser_union_desc, - (struct usb_descriptor_header *) &gser_hs_notify_desc, -#endif (struct usb_descriptor_header *) &gser_hs_in_desc, (struct usb_descriptor_header *) &gser_hs_out_desc, NULL, @@ -218,365 +109,64 @@ static struct usb_descriptor_header *gser_hs_function[] = { /* string descriptors: */ -static struct usb_string modem_string_defs[] = { - [0].s = "HTC Modem", +static struct usb_string gser_string_defs[] = { + [0].s = "Generic Serial", { } /* end of list */ }; -static struct usb_gadget_strings modem_string_table = { +static struct usb_gadget_strings gser_string_table = { .language = 0x0409, /* en-us */ - .strings = modem_string_defs, + .strings = gser_string_defs, }; -static struct usb_gadget_strings *modem_strings[] = { - &modem_string_table, +static struct usb_gadget_strings *gser_strings[] = { + &gser_string_table, NULL, }; -static struct usb_string serial_string_defs[] = { - [0].s = "HTC Serial", - { } /* end of list */ -}; - -static struct usb_gadget_strings serial_string_table = { - .language = 0x0409, /* en-us */ - .strings = serial_string_defs, -}; - -static struct usb_gadget_strings *serial_strings[] = { - &serial_string_table, - NULL, -}; -#ifdef CONFIG_MODEM_SUPPORT -static void gser_complete_set_line_coding(struct usb_ep *ep, - struct usb_request *req) -{ - struct f_gser *gser = ep->driver_data; - struct usb_composite_dev *cdev = gser->port.func.config->cdev; - - if (req->status != 0) { - DBG(cdev, "gser ttyGS%d completion, err %d\n", - gser->port_num, req->status); - return; - } - - /* normal completion */ - if (req->actual != sizeof(gser->port_line_coding)) { - DBG(cdev, "gser ttyGS%d short resp, len %d\n", - gser->port_num, req->actual); - usb_ep_set_halt(ep); - } else { - struct usb_cdc_line_coding *value = req->buf; - gser->port_line_coding = *value; - } -} /*-------------------------------------------------------------------------*/ -static int -gser_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) -{ - struct f_gser *gser = func_to_gser(f); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { - - /* SET_LINE_CODING ... just read and save what the host sends */ - case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) - | USB_CDC_REQ_SET_LINE_CODING: - if (w_length != sizeof(struct usb_cdc_line_coding) - || w_index != gser->data_id) - goto invalid; - value = w_length; - cdev->gadget->ep0->driver_data = gser; - req->complete = gser_complete_set_line_coding; - break; - - /* GET_LINE_CODING ... return what host sent, or initial value */ - case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) - | USB_CDC_REQ_GET_LINE_CODING: - if (w_index != gser->data_id) - goto invalid; - - value = min_t(unsigned, w_length, - sizeof(struct usb_cdc_line_coding)); - memcpy(req->buf, &gser->port_line_coding, value); - break; - - /* SET_CONTROL_LINE_STATE ... save what the host sent */ - case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) - | USB_CDC_REQ_SET_CONTROL_LINE_STATE: - if (w_index != gser->data_id) - goto invalid; - - value = 0; - gser->port_handshake_bits = w_value; - break; - - default: -invalid: - ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer or status phase? */ - if (value >= 0) { - DBG(cdev, "gser ttyGS%d req%02x.%02x v%04x i%04x l%d\n", - gser->port_num, ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - req->zero = 0; - req->length = value; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) - ERROR(cdev, "gser response on ttyGS%d, err %d\n", - gser->port_num, value); - } - - /* device either stalls (value < 0) or reports success */ - return value; -} -#endif static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { - struct f_gser *gser = func_to_gser(f); + struct f_gser *gser = func_to_gser(f); struct usb_composite_dev *cdev = f->config->cdev; - struct usb_interface_descriptor *desc; /* we know alt == 0, so this is an activation or a reset */ - if (cdev->gadget->speed == USB_SPEED_HIGH) - desc = (struct usb_interface_descriptor *) *(f->hs_descriptors); - else - desc = (struct usb_interface_descriptor *) *(f->descriptors); - gser->data_id = desc->bInterfaceNumber; -#ifdef CONFIG_MODEM_SUPPORT -#if 0 - if (gser->notify->driver_data) { - DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); - usb_ep_disable(gser->notify); - } -#endif - gser->notify_desc = ep_choose(cdev->gadget, - gser->hs.notify, - gser->fs.notify); - usb_ep_enable(gser->notify, gser->notify_desc); - gser->notify->driver_data = gser; -#endif -#if 0 if (gser->port.in->driver_data) { DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); } else { DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); + gser->port.in_desc = ep_choose(cdev->gadget, + gser->hs.in, gser->fs.in); + gser->port.out_desc = ep_choose(cdev->gadget, + gser->hs.out, gser->fs.out); } -#endif - gser->port.in_desc = ep_choose(cdev->gadget, - gser->hs.in, gser->fs.in); - gser->port.out_desc = ep_choose(cdev->gadget, - gser->hs.out, gser->fs.out); gserial_connect(&gser->port, gser->port_num); - gser->online = 1; return 0; } static void gser_disable(struct usb_function *f) { - struct f_gser *gser = func_to_gser(f); + struct f_gser *gser = func_to_gser(f); struct usb_composite_dev *cdev = f->config->cdev; DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); gserial_disconnect(&gser->port); -#if 0 - /* disable endpoints, aborting down any active I/O */ - usb_ep_fifo_flush(gser->port.out); - usb_ep_disable(gser->port.out); - gser->port.out->driver_data = NULL; - - usb_ep_fifo_flush(gser->port.in); - usb_ep_disable(gser->port.in); - gser->port.in->driver_data = NULL; -#endif -#ifdef CONFIG_MODEM_SUPPORT - usb_ep_fifo_flush(gser->notify); - usb_ep_disable(gser->notify); - gser->notify->driver_data = NULL; -#endif - gser->online = 0; -} -#ifdef CONFIG_MODEM_SUPPORT -static int gser_notify(struct f_gser *gser, u8 type, u16 value, - void *data, unsigned length) -{ - struct usb_ep *ep = gser->notify; - struct usb_request *req; - struct usb_cdc_notification *notify; - const unsigned len = sizeof(*notify) + length; - void *buf; - int status; - struct usb_composite_dev *cdev = gser->port.func.config->cdev; - - req = gser->notify_req; - gser->notify_req = NULL; - gser->pending = false; - - req->length = len; - notify = req->buf; - buf = notify + 1; - - notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS - | USB_RECIP_INTERFACE; - notify->bNotificationType = type; - notify->wValue = cpu_to_le16(value); - notify->wIndex = cpu_to_le16(gser->data_id); - notify->wLength = cpu_to_le16(length); - memcpy(buf, data, length); - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status < 0) { - ERROR(cdev, "gser ttyGS%d can't notify serial state, %d\n", - gser->port_num, status); - gser->notify_req = req; - } - - return status; } -static int gser_notify_serial_state(struct f_gser *gser) -{ - int status; - unsigned long flags; - struct usb_composite_dev *cdev = gser->port.func.config->cdev; - if (gser->disabled) - return 0; - - spin_lock_irqsave(&gser->lock, flags); - if (gser->notify_req) { - DBG(cdev, "gser ttyGS%d serial state %04x\n", - gser->port_num, gser->serial_state); - status = gser_notify(gser, USB_CDC_NOTIFY_SERIAL_STATE, - 0, &gser->serial_state, - sizeof(gser->serial_state)); - } else { - gser->pending = true; - status = 0; - } - spin_unlock_irqrestore(&gser->lock, flags); - return status; -} - -static void gser_notify_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct f_gser *gser = req->context; - u8 doit = false; - unsigned long flags; - - /* on this call path we do NOT hold the port spinlock, - * which is why ACM needs its own spinlock - */ - spin_lock_irqsave(&gser->lock, flags); - if (req->status != -ESHUTDOWN) - doit = gser->pending; - gser->notify_req = req; - spin_unlock_irqrestore(&gser->lock, flags); - - if (doit && gser->online) - gser_notify_serial_state(gser); -} -static void gser_connect(struct gserial *port) -{ - struct f_gser *gser = port_to_gser(port); - - gser->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD; - gser_notify_serial_state(gser); -} - -unsigned int gser_get_dtr(struct gserial *port) -{ - struct f_gser *gser = port_to_gser(port); - - if (gser->port_handshake_bits & ACM_CTRL_DTR) - return 1; - else - return 0; -} - -unsigned int gser_get_rts(struct gserial *port) -{ - struct f_gser *gser = port_to_gser(port); - - if (gser->port_handshake_bits & ACM_CTRL_RTS) - return 1; - else - return 0; -} - -unsigned int gser_send_carrier_detect(struct gserial *port, unsigned int yes) -{ - struct f_gser *gser = port_to_gser(port); - u16 state; - - state = gser->serial_state; - state &= ~ACM_CTRL_DCD; - if (yes) - state |= ACM_CTRL_DCD; - - gser->serial_state = state; - return gser_notify_serial_state(gser); - -} - -unsigned int gser_send_ring_indicator(struct gserial *port, unsigned int yes) -{ - struct f_gser *gser = port_to_gser(port); - u16 state; - - state = gser->serial_state; - state &= ~ACM_CTRL_RI; - if (yes) - state |= ACM_CTRL_RI; - - gser->serial_state = state; - return gser_notify_serial_state(gser); - -} -static void gser_disconnect(struct gserial *port) -{ - struct f_gser *gser = port_to_gser(port); - - gser->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD); - gser_notify_serial_state(gser); -} - -static int gser_send_break(struct gserial *port, int duration) -{ - struct f_gser *gser = port_to_gser(port); - u16 state; - - state = gser->serial_state; - state &= ~ACM_CTRL_BRK; - if (duration) - state |= ACM_CTRL_BRK; - - gser->serial_state = state; - return gser_notify_serial_state(gser); -} -#endif /*-------------------------------------------------------------------------*/ /* serial function driver setup/binding */ -static int +static int __init gser_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; - struct f_gser *gser = func_to_gser(f); - int status; - struct usb_ep *ep; - struct usb_gadget_strings *s; + struct f_gser *gser = func_to_gser(f); + int status; + struct usb_ep *ep; /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); @@ -584,10 +174,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) goto fail; gser->data_id = status; gser_interface_desc.bInterfaceNumber = status; - if (f->strings) { - s = *(f->strings); - gser_interface_desc.iInterface = s->strings[0].id; - } status = -ENODEV; @@ -596,30 +182,13 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; gser->port.in = ep; - ep->driver_data = gser; /* claim */ + ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); if (!ep) goto fail; gser->port.out = ep; - ep->driver_data = gser; /* claim */ - -#ifdef CONFIG_MODEM_SUPPORT - ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_notify_desc); - if (!ep) - goto fail; - gser->notify = ep; - ep->driver_data = gser; /* claim */ - /* allocate notification */ - gser->notify_req = gs_alloc_req(ep, - sizeof(struct usb_cdc_notification) + 2, - GFP_KERNEL); - if (!gser->notify_req) - goto fail; - - gser->notify_req->complete = gser_notify_complete; - gser->notify_req->context = gser; -#endif + ep->driver_data = cdev; /* claim */ /* copy descriptors, and track endpoint copies */ f->descriptors = usb_copy_descriptors(gser_fs_function); @@ -628,10 +197,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) f->descriptors, &gser_fs_in_desc); gser->fs.out = usb_find_endpoint(gser_fs_function, f->descriptors, &gser_fs_out_desc); -#ifdef CONFIG_MODEM_SUPPORT - gser->fs.notify = usb_find_endpoint(gser_fs_function, - f->descriptors, &gser_fs_notify_desc); -#endif /* support all relevant hardware speeds... we expect that when @@ -643,10 +208,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) gser_fs_in_desc.bEndpointAddress; gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; -#ifdef CONFIG_MODEM_SUPPORT - gser_hs_notify_desc.bEndpointAddress = - gser_fs_notify_desc.bEndpointAddress; -#endif /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(gser_hs_function); @@ -655,10 +216,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors, &gser_hs_in_desc); gser->hs.out = usb_find_endpoint(gser_hs_function, f->hs_descriptors, &gser_hs_out_desc); -#ifdef CONFIG_MODEM_SUPPORT - gser->hs.notify = usb_find_endpoint(gser_hs_function, - f->hs_descriptors, &gser_hs_notify_desc); -#endif } DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", @@ -668,14 +225,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: -#ifdef CONFIG_MODEM_SUPPORT - if (gser->notify_req) - gs_free_req(gser->notify, gser->notify_req); - - /* we might as well release our claims on endpoints */ - if (gser->notify) - gser->notify->driver_data = NULL; -#endif /* we might as well release our claims on endpoints */ if (gser->port.out) gser->port.out->driver_data = NULL; @@ -690,15 +239,9 @@ fail: static void gser_unbind(struct usb_configuration *c, struct usb_function *f) { -#ifdef CONFIG_MODEM_SUPPORT - struct f_gser *gser = func_to_gser(f); -#endif if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); usb_free_descriptors(f->descriptors); -#ifdef CONFIG_MODEM_SUPPORT - gs_free_req(gser->notify, gser->notify_req); -#endif kfree(func_to_gser(f)); } @@ -714,9 +257,9 @@ gser_unbind(struct usb_configuration *c, struct usb_function *f) * handle all the ones it binds. Caller is also responsible * for calling @gserial_cleanup() before module unload. */ -int gser_bind_config(struct usb_configuration *c, u8 port_num) +int __init gser_bind_config(struct usb_configuration *c, u8 port_num) { - struct f_gser *gser; + struct f_gser *gser; int status; /* REVISIT might want instance-specific strings to help @@ -724,129 +267,29 @@ int gser_bind_config(struct usb_configuration *c, u8 port_num) */ /* maybe allocate device-global string ID */ - if (modem_string_defs[0].id == 0 && port_num == 0) { + if (gser_string_defs[0].id == 0) { status = usb_string_id(c->cdev); - if (status < 0) { - printk(KERN_ERR "%s: return %d\n", __func__, status); + if (status < 0) return status; - } - modem_string_defs[0].id = status; - } - if (serial_string_defs[0].id == 0 && port_num == 2) { - status = usb_string_id(c->cdev); - if (status < 0) { - printk(KERN_ERR "%s: return %d\n", __func__, status); - return status; - } - serial_string_defs[0].id = status; + gser_string_defs[0].id = status; } + /* allocate and initialize one new instance */ gser = kzalloc(sizeof *gser, GFP_KERNEL); if (!gser) return -ENOMEM; -#ifdef CONFIG_MODEM_SUPPORT - spin_lock_init(&gser->lock); -#endif gser->port_num = port_num; - if (port_num == 0) { - gser->port.func.name = "modem"; - gser->port.func.strings = modem_strings; - modem_function = &gser->port.func; - } else if (port_num == 2) { - gser->port.func.name = "serial"; - gser->port.func.strings = serial_strings; - serial_function = &gser->port.func; - } + gser->port.func.name = "gser"; + gser->port.func.strings = gser_strings; gser->port.func.bind = gser_bind; gser->port.func.unbind = gser_unbind; gser->port.func.set_alt = gser_set_alt; gser->port.func.disable = gser_disable; -#ifdef CONFIG_MODEM_SUPPORT - gser->port.func.setup = gser_setup; - gser->port.connect = gser_connect; - gser->port.get_dtr = gser_get_dtr; - gser->port.get_rts = gser_get_rts; - gser->port.send_carrier_detect = gser_send_carrier_detect; - gser->port.send_ring_indicator = gser_send_ring_indicator; - gser->port.disconnect = gser_disconnect; - gser->port.send_break = gser_send_break; -#endif - gser->port.func.hidden = 1; - gser->disabled = 1; status = usb_add_function(c, &gser->port.func); if (status) kfree(gser); return status; } - -static int modem_set_enabled(const char *val, struct kernel_param *kp) -{ - struct f_gser *gser; - int enabled = simple_strtol(val, NULL, 0); - printk(KERN_INFO "%s: %d\n", __func__, enabled); - gser = func_to_gser(modem_function); - if (!gser) - return 0; - gser->disabled = !enabled; - android_enable_function(modem_function, enabled); - return 0; -} - -static int modem_get_enabled(char *buffer, struct kernel_param *kp) -{ - buffer[0] = '0' + !modem_function->hidden; - printk(KERN_INFO "%s: %d\n", __func__, buffer[0] - '0'); - return 1; -} -module_param_call(modem_enabled, modem_set_enabled, modem_get_enabled, NULL, 0664); - -static int serial_set_enabled(const char *val, struct kernel_param *kp) -{ - struct f_gser *gser; - int enabled = simple_strtol(val, NULL, 0); - printk(KERN_INFO "%s: %d\n", __func__, enabled); - gser = func_to_gser(serial_function); - if (!gser) - return 0; - gser->disabled = !enabled; - android_enable_function(serial_function, enabled); - return 0; -} - -static int serial_get_enabled(char *buffer, struct kernel_param *kp) -{ - buffer[0] = '0' + !serial_function->hidden; - printk(KERN_INFO "%s: %d\n", __func__, buffer[0] - '0'); - return 1; -} -module_param_call(serial_enabled, serial_set_enabled, serial_get_enabled, NULL, 0664); - -static int serial_bind_config(struct usb_configuration *c) -{ - int ret; - - printk(KERN_INFO "serial_bind_config\n"); - ret = gser_bind_config(c, 0); - if (ret) - return ret; - ret = gser_bind_config(c, 2); - if (ret == 0) - gserial_setup(c->cdev->gadget, 3); - return ret; -} - -static struct android_usb_function android_serial_function = { - .name = "serial", - .bind_config = serial_bind_config, -}; - -static int __init init(void) -{ - printk(KERN_INFO "serial init\n"); - android_register_function(&android_serial_function); - return 0; -} -module_init(init); diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 3c9c1349..3bf25a01 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -49,6 +49,7 @@ #ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC #include #endif +#include static const char driver_name[] = "msm72k_udc"; @@ -80,11 +81,14 @@ static struct usb_info *the_usb_info; static int vbus; static int use_mfg_serialno; static char mfg_df_serialno[16]; +static int disable_charger; -#ifdef CONFIG_USB_ACCESSORY_DETECT +#if defined (CONFIG_DOCK_ACCESSORY_DETECT) || defined(CONFIG_USB_ACCESSORY_DETECT) #ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC extern int htc_get_usb_accessory_adc_level(uint32_t *buffer); #endif + + static struct switch_dev dock_switch = { .name = "dock", }; @@ -94,6 +98,12 @@ static struct switch_dev dock_switch = { #define DOCK_STATE_CAR (1 << 1) #endif +#include +#include + +static struct wake_lock vbus_idle_wake_lock; +static struct perf_lock usb_perf_lock; + struct msm_request { struct usb_request req; @@ -143,8 +153,10 @@ static void check_charger(struct work_struct *w); #ifdef CONFIG_USB_ACCESSORY_DETECT static void accessory_detect_work(struct work_struct *w); #endif -extern int android_switch_function(unsigned func); -extern int android_show_function(char *buf); +#ifdef CONFIG_DOCK_ACCESSORY_DETECT +static void dock_detect_work(struct work_struct *w); +static void dock_detect_init(struct usb_info *ui); +#endif extern void android_set_serialno(char *serialno); #define USB_STATE_IDLE 0 @@ -201,6 +213,10 @@ struct usb_info { void (*phy_reset)(void); void (*hw_reset)(bool en); void (*usb_uart_switch)(int); + void (*serial_debug_gpios)(int); + void (*usb_hub_enable)(bool); + int (*china_ac_detect)(void); + void (*disable_usb_charger)(void); /* for notification when USB is connected or disconnected */ void (*usb_connected)(int); @@ -209,6 +225,7 @@ struct usb_info { struct work_struct work; struct delayed_work chg_work; struct work_struct detect_work; + struct work_struct dock_work; struct work_struct notifier_work; unsigned phy_status; unsigned phy_fail_count; @@ -233,10 +250,13 @@ struct usb_info { u8 in_lpm; /* for accessory detection */ + bool dock_detect; u8 accessory_detect; u8 mfg_usb_carkit_enable; int idpin_irq; + int dockpin_irq; int usb_id_pin_gpio; + int dock_pin_gpio; void (*config_usb_id_gpios)(bool output_enable); /* 0: none, 1: carkit, 2: usb headset */ u8 accessory_type; @@ -327,7 +347,7 @@ static int ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) USB_ULPI_VIEWPORT); /* wait for completion */ - while((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ; + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ; if (timeout == 0) { printk(KERN_ERR "ulpi_write: timeout\n"); @@ -514,6 +534,8 @@ static void usb_ept_start(struct msm_endpoint *ept) { struct usb_info *ui = ept->ui; struct msm_request *req = ept->req; + int i, cnt; + unsigned n = 1 << ept->bit; BUG_ON(req->live); @@ -521,14 +543,36 @@ static void usb_ept_start(struct msm_endpoint *ept) ept->head->next = req->item_dma; ept->head->info = 0; - /* start the endpoint */ - writel(1 << ept->bit, USB_ENDPTPRIME); + /* during high throughput testing it is observed that + * ept stat bit is not set even thoguh all the data + * structures are updated properly and ept prime bit + * is set. To workaround the issue, try to check if + * ept stat bit otherwise try to re-prime the ept + */ + for (i = 0; i < 5; i++) { + writel(n, USB_ENDPTPRIME); + for (cnt = 0; cnt < 3000; cnt++) { + if (!(readl(USB_ENDPTPRIME) & n) && + (readl(USB_ENDPTSTAT) & n)) + goto DONE; + udelay(1); + } + } + if (!(readl(USB_ENDPTSTAT) & n)) { + pr_err("Unable to prime the ept%d%s\n", + ept->num, + ept->flags & EPT_FLAG_IN ? "in" : "out"); + return; + } + +DONE: /* mark this chain of requests as live */ while (req) { req->live = 1; req = req->next; } + } int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req) @@ -752,7 +796,6 @@ static void handle_setup(struct usb_info *ui) { u16 temp = 0; - temp = 1 << USB_DEVICE_SELF_POWERED; temp |= (ui->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); memcpy(req->buf, &temp, 2); @@ -807,6 +850,10 @@ static void handle_setup(struct usb_info *ui) case J_TEST: case K_TEST: case SE0_NAK_TEST: + if (!ui->test_mode) { + disable_charger = 1; + queue_delayed_work(ui->usb_wq, &ui->chg_work, 0); + } case TST_PKT_TEST: ui->test_mode = ctl.wIndex; goto ack; @@ -1108,30 +1155,6 @@ static ssize_t show_usb_cable_connect(struct device *dev, static DEVICE_ATTR(usb_cable_connect, 0444, show_usb_cable_connect, NULL); -static ssize_t show_usb_function_switch(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return android_show_function(buf); -} - -static ssize_t store_usb_function_switch(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned u; - ssize_t ret; - - u = simple_strtoul(buf, NULL, 10); - ret = android_switch_function(u); - - if (ret == 0) - return count; - else - return 0; -} - -static DEVICE_ATTR(usb_function_switch, 0666, - show_usb_function_switch, store_usb_function_switch); - static ssize_t show_usb_serial_number(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1242,6 +1265,27 @@ static ssize_t show_USB_ID_status(struct device *dev, static DEVICE_ATTR(USB_ID_status, 0444, show_USB_ID_status, NULL); +static ssize_t show_usb_car_kit_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_info *ui = the_usb_info; + int value = 1; + unsigned length; + + if (!ui) + return 0; + if (ui->accessory_detect == 0) { + value = 0; + } + printk(KERN_INFO "usb: USB_car_kit_enable %d\n", ui->accessory_detect); + length = sprintf(buf, "%d", value); + return length; +} + +static DEVICE_ATTR(usb_car_kit_enable, 0444, + show_usb_car_kit_enable, NULL);/*for kar kit AP check if car kit enable*/ + static ssize_t show_usb_phy_setting(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1321,13 +1365,17 @@ static ssize_t store_mfg_carkit_enable(struct device *dev, static DEVICE_ATTR(usb_mfg_carkit_enable, 0644, show_mfg_carkit_enable, store_mfg_carkit_enable); +#endif +#if defined (CONFIG_DOCK_ACCESSORY_DETECT) || defined(CONFIG_USB_ACCESSORY_DETECT) static ssize_t dock_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_info *ui = the_usb_info; if (ui->accessory_type == 1) return sprintf(buf, "online\n"); + else if (ui->accessory_type == 3) /*desk dock*/ + return sprintf(buf, "online\n"); else return sprintf(buf, "offline\n"); } @@ -1364,6 +1412,13 @@ static void usb_prepare(struct usb_info *ui) #ifdef CONFIG_USB_ACCESSORY_DETECT INIT_WORK(&ui->detect_work, accessory_detect_work); #endif +#ifdef CONFIG_DOCK_ACCESSORY_DETECT + if (ui->dock_detect) { + INIT_WORK(&ui->dock_work, dock_detect_work); + dock_detect_init(ui); + } +#endif + INIT_WORK(&ui->notifier_work, send_usb_connect_notify); INIT_DELAYED_WORK(&ui->chg_work, check_charger); @@ -1372,11 +1427,6 @@ static void usb_prepare(struct usb_info *ui) if (ret != 0) printk(KERN_WARNING "dev_attr_usb_cable_connect failed\n"); - ret = device_create_file(&ui->pdev->dev, - &dev_attr_usb_function_switch); - if (ret != 0) - printk(KERN_WARNING "dev_attr_usb_function_switch failed\n"); - ret = device_create_file(&ui->pdev->dev, &dev_attr_usb_serial_number); if (ret != 0) @@ -1403,6 +1453,11 @@ static void usb_prepare(struct usb_info *ui) if (ret != 0) printk(KERN_WARNING "dev_attr_usb_mfg_carkit_enable failed\n"); #endif + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_car_kit_enable);/*for kar kit AP check if car kit enable*/ + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_car_kit_enable failed\n"); + } static int usb_wakeup_phy(struct usb_info *ui) @@ -1545,6 +1600,18 @@ static void usb_start(struct usb_info *ui) spin_lock_irqsave(&ui->lock, flags); ui->flags |= USB_FLAG_START; +/*if msm_hsusb_set_vbus_state set 1, but usb did not init, the ui =NULL, */ +/*it would cause reboot with usb, it did not swith to USB and ADB fail*/ +/*So when USB start, check again*/ + if (vbus) { + ui->flags |= USB_FLAG_VBUS_ONLINE; + } else { + ui->flags |= USB_FLAG_VBUS_OFFLINE; + } + /* online->switch to USB, offline->switch to uart */ + if (ui->usb_uart_switch) + ui->usb_uart_switch(!vbus); + queue_work(ui->usb_wq, &ui->work); spin_unlock_irqrestore(&ui->lock, flags); } @@ -1609,6 +1676,13 @@ static void usb_lpm_enter(struct usb_info *ui) clk_set_rate(ui->ebi1clk, 0); ui->in_lpm = 1; spin_unlock_irqrestore(&ui->lock, iflags); + + if (board_mfg_mode() == 1) {/*for MFG adb unstable in FROYO ROM*/ + printk(KERN_INFO "usb: idle_wake_unlock and perf unlock\n"); + wake_unlock(&vbus_idle_wake_lock); + if (is_perf_lock_active(&usb_perf_lock)) + perf_unlock(&usb_perf_lock); + } } static void usb_lpm_exit(struct usb_info *ui) @@ -1626,8 +1700,86 @@ static void usb_lpm_exit(struct usb_info *ui) clk_enable(ui->otgclk); usb_wakeup_phy(ui); ui->in_lpm = 0; + + if (board_mfg_mode() == 1) {/*for MFG adb unstable in FROYO ROM*/ + printk(KERN_INFO "usb: idle_wake_lock and perf lock\n"); + wake_lock(&vbus_idle_wake_lock); + if (!is_perf_lock_active(&usb_perf_lock)) + perf_lock(&usb_perf_lock); + } } +#ifdef CONFIG_DOCK_ACCESSORY_DETECT +static irqreturn_t dock_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + disable_irq_nosync(ui->dockpin_irq); + queue_work(ui->usb_wq, &ui->dock_work); + return IRQ_HANDLED; +} +static void dock_detect_work(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, dock_work); + int value; + + value = gpio_get_value(ui->dock_pin_gpio); + + if (value == 0 && vbus) { + set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_HIGH); + switch_set_state(&dock_switch, DOCK_STATE_DESK); + ui->accessory_type = 3; + printk(KERN_INFO "usb:dock: set state %d\n", DOCK_STATE_DESK); + } else { + set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_LOW); + switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); + ui->accessory_type = 0; + printk(KERN_INFO "usb:dock: set state %d\n", DOCK_STATE_UNDOCKED); + } + enable_irq(ui->dockpin_irq); + + +} +static void dock_detect_init(struct usb_info *ui) +{ + int ret; + + if (ui->dock_pin_gpio == 0) + return; + if (ui->dockpin_irq == 0) + ui->dockpin_irq = gpio_to_irq(ui->dock_pin_gpio); + + ret = request_irq(ui->dockpin_irq, dock_interrupt, + IRQF_TRIGGER_LOW, + "dock_irq", ui); + if (ret < 0) { + printk(KERN_ERR "%s: request_irq failed\n", __func__); + return; + } + printk(KERN_INFO "%s: dock irq %d\n", __func__, + ui->dockpin_irq); + ret = set_irq_wake(ui->dockpin_irq, 1); + if (ret < 0) { + printk(KERN_ERR "%s: set_irq_wake failed\n", __func__); + goto err; + } + + if (switch_dev_register(&dock_switch) < 0) { + printk(KERN_ERR "usb: fail to register dock switch!\n"); + goto err; + } + + ret = device_create_file(dock_switch.dev, &dev_attr_status); + if (ret != 0) + printk(KERN_WARNING "dev_attr_status failed\n"); + + return; + +err: + free_irq(ui->dockpin_irq, 0); +} +#endif + + #ifdef CONFIG_USB_ACCESSORY_DETECT static void carkit_detect(struct usb_info *ui) { @@ -1685,7 +1837,7 @@ static void accessory_detect_by_adc(struct usb_info *ui) if (adc_value >= 0x2112 && adc_value <= 0x3D53) { printk(KERN_INFO "usb: headset inserted\n"); ui->accessory_type = 2; - headset_ext_detect(USB_HEADSET); + headset_ext_detect(USB_AUDIO_OUT); } else if (adc_value >= 0x88A && adc_value <= 0x1E38) { printk(KERN_INFO "usb: carkit inserted\n"); ui->accessory_type = 1; @@ -1699,7 +1851,7 @@ static void accessory_detect_by_adc(struct usb_info *ui) } else { if (ui->accessory_type == 2) { printk(KERN_INFO "usb: headset removed\n"); - headset_ext_detect(NO_DEVICE); + headset_ext_detect(USB_NO_HEADSET); } else if (ui->accessory_type == 1) { printk(KERN_INFO "usb: carkit removed\n"); switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); @@ -1751,7 +1903,8 @@ static void accessory_detect_init(struct usb_info *ui) if (ui->usb_id_pin_gpio == 0) return; - ui->idpin_irq = gpio_to_irq(ui->usb_id_pin_gpio); + if (ui->idpin_irq == 0) + ui->idpin_irq = gpio_to_irq(ui->usb_id_pin_gpio); ret = request_irq(ui->idpin_irq, usbid_interrupt, IRQF_TRIGGER_LOW, @@ -1771,9 +1924,11 @@ static void accessory_detect_init(struct usb_info *ui) printk(KERN_ERR "usb: fail to register dock switch!\n"); goto err; } + ret = device_create_file(dock_switch.dev, &dev_attr_status); if (ret != 0) printk(KERN_WARNING "dev_attr_status failed\n"); + return; err: free_irq(ui->idpin_irq, 0); @@ -1782,6 +1937,44 @@ err: #endif #define DELAY_FOR_CHECK_CHG msecs_to_jiffies(300) + +static void charger_detect_by_uart(struct usb_info *ui) +{ + int is_china_ac; + + /*UART*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(1); + + is_china_ac = ui->china_ac_detect(); + + if (is_china_ac) { + ui->connect_type = CONNECT_TYPE_AC; + queue_work(ui->usb_wq, &ui->notifier_work); + usb_lpm_enter(ui); + printk(KERN_INFO "usb: AC charger\n"); + } else { + ui->connect_type = CONNECT_TYPE_UNKNOWN; + queue_delayed_work(ui->usb_wq, &ui->chg_work, + DELAY_FOR_CHECK_CHG); + printk(KERN_INFO "usb: not AC charger\n"); + + /*set uart to gpo*/ + if (ui->serial_debug_gpios) + ui->serial_debug_gpios(0); + /*turn on USB HUB*/ + if (ui->usb_hub_enable) + ui->usb_hub_enable(1); + + /*USB*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(0); + + usb_lpm_exit(ui); + usb_reset(ui); + } +} + static void charger_detect(struct usb_info *ui) { if (!vbus) @@ -1806,6 +1999,13 @@ static void charger_detect(struct usb_info *ui) static void check_charger(struct work_struct *w) { struct usb_info *ui = container_of(w, struct usb_info, chg_work.work); + if (disable_charger) { + printk(KERN_INFO "usb: disable charger\n"); + if (ui->disable_usb_charger) + ui->disable_usb_charger(); + disable_charger = 0; + return; + } /* unknown charger */ if (vbus && ui->connect_type == CONNECT_TYPE_UNKNOWN) queue_work(ui->usb_wq, &ui->notifier_work); @@ -1817,6 +2017,7 @@ static void usb_do_work(struct work_struct *w) unsigned long iflags; unsigned flags, _vbus; + for (;;) { spin_lock_irqsave(&ui->lock, iflags); flags = ui->flags; @@ -1831,9 +2032,15 @@ static void usb_do_work(struct work_struct *w) case USB_STATE_IDLE: if (flags & USB_FLAG_START) { pr_info("hsusb: IDLE -> ONLINE\n"); - usb_lpm_exit(ui); - usb_reset(ui); - charger_detect(ui); + + if (ui->china_ac_detect) + charger_detect_by_uart(ui); + else { + usb_lpm_exit(ui); + usb_reset(ui); + + charger_detect(ui); + } ui->state = USB_STATE_ONLINE; #ifdef CONFIG_USB_ACCESSORY_DETECT @@ -1905,9 +2112,14 @@ static void usb_do_work(struct work_struct *w) */ if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) { pr_info("hsusb: OFFLINE -> ONLINE\n"); - usb_lpm_exit(ui); - usb_reset(ui); - charger_detect(ui); + + if (ui->china_ac_detect) + charger_detect_by_uart(ui); + else { + usb_lpm_exit(ui); + usb_reset(ui); + charger_detect(ui); + } ui->state = USB_STATE_ONLINE; usb_do_work_check_vbus(ui); @@ -1937,9 +2149,33 @@ void msm_hsusb_set_vbus_state(int online) } else { ui->flags |= USB_FLAG_VBUS_OFFLINE; } - /* online->switch to USB, offline->switch to uart */ - if (ui->usb_uart_switch) - ui->usb_uart_switch(!online); + + if (online) { + /*USB*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(0); + } else { + /*turn off USB HUB*/ + if (ui->usb_hub_enable) + ui->usb_hub_enable(0); + + /*UART*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(1); + /*configure uart pin to alternate function*/ + if (ui->serial_debug_gpios) + ui->serial_debug_gpios(1); +#ifdef CONFIG_DOCK_ACCESSORY_DETECT + if (ui->accessory_type == 3) { + set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_LOW); + switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); + ui->accessory_type = 0; + printk(KERN_INFO "usb:dock: vbus offline\n"); + enable_irq(ui->dockpin_irq); + } +#endif + } + queue_work(ui->usb_wq, &ui->work); } } @@ -2360,6 +2596,10 @@ static int msm72k_probe(struct platform_device *pdev) ui->phy_init_seq = pdata->phy_init_seq; ui->usb_connected = pdata->usb_connected; ui->usb_uart_switch = pdata->usb_uart_switch; + ui->serial_debug_gpios = pdata->serial_debug_gpios; + ui->usb_hub_enable = pdata->usb_hub_enable; + ui->china_ac_detect = pdata->china_ac_detect; + ui->disable_usb_charger = pdata->disable_usb_charger; ui->accessory_detect = pdata->accessory_detect; printk(KERN_INFO "usb: accessory detect %d\n", @@ -2367,6 +2607,15 @@ static int msm72k_probe(struct platform_device *pdev) ui->usb_id_pin_gpio = pdata->usb_id_pin_gpio; printk(KERN_INFO "usb: id_pin_gpio %d\n", pdata->usb_id_pin_gpio); + + ui->dock_detect = pdata->dock_detect; + printk(KERN_INFO "usb: dock detect %d\n", + ui->dock_detect); + ui->dock_pin_gpio = pdata->dock_pin_gpio; + printk(KERN_INFO "usb: dock pin gpio %d\n", + ui->dock_pin_gpio); + + ui->idpin_irq = pdata->id_pin_irq; if (pdata->config_usb_id_gpios) ui->config_usb_id_gpios = pdata->config_usb_id_gpios; } @@ -2452,9 +2701,11 @@ static int msm72k_probe(struct platform_device *pdev) /* initialize mfg serial number */ - if (board_mfg_mode() == 1) + if (board_mfg_mode() == 1) { use_mfg_serialno = 1; - else + wake_lock_init(&vbus_idle_wake_lock, WAKE_LOCK_IDLE, "usb_idle_lock"); + perf_lock_init(&usb_perf_lock, PERF_LOCK_HIGHEST, "usb"); + } else use_mfg_serialno = 0; strncpy(mfg_df_serialno, "000000000000", strlen("000000000000")); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 48267bc0..1043da1f 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -291,9 +292,13 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_VENDOR_DESCRIPTION: pr_debug("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__); - length = strlen (rndis_per_dev_params [configNr].vendorDescr); - memcpy (outbuf, - rndis_per_dev_params [configNr].vendorDescr, length); + if ( rndis_per_dev_params [configNr].vendorDescr ) { + length = strlen (rndis_per_dev_params [configNr].vendorDescr); + memcpy (outbuf, + rndis_per_dev_params [configNr].vendorDescr, length); + } else { + outbuf[0] = 0; + } retval = 0; break; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c new file mode 100644 index 00000000..869248b4 --- /dev/null +++ b/drivers/usb/gadget/storage_common.c @@ -0,0 +1,784 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_manufacturer -- name of the manufacturer + * - fsg_string_product -- name of the product + * - fsg_string_serial -- product's serial + * - fsg_string_config -- name of the configuration + * - fsg_string_interface -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* + * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and + * fsg_hs_intr_in_desc objects as well as + * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES + * macros are not defined. + * + * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER, + * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not + * defined (as well as corresponding entries in string tables are + * missing) and FSG_STRING_INTERFACE has value of zero. + * + * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. + */ + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + + +#include + + +/* Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + + +/*-------------------------------------------------------------------------*/ + + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) + +/* Keep those macros in sync with thos in + * include/linux/ubs/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. No + * checking if those symbols is defined is performed because warning + * is desired when those macros were defined by someone else to mean + * something else. */ +#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args) + + + +#ifdef DUMP_MSGS + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + + + + + +/*-------------------------------------------------------------------------*/ + +/* SCSI device types */ +#define TYPE_DISK 0x00 +#define TYPE_CDROM 0x05 + +/* USB protocol value = the transport method */ +#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define USB_PR_BULK 0x50 /* Bulk-only */ + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */ +#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ +#define USB_SC_QIC 0x03 /* QIC-157 (tape) */ +#define USB_SC_UFI 0x04 /* UFI (floppy) */ +#define USB_SC_8070 0x05 /* SFF-8070i (removable) */ +#define USB_SC_SCSI 0x06 /* Transparent SCSI */ + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct fsg_bulk_cb_wrap { + __le32 Signature; /* Contains 'USBC' */ + u32 Tag; /* Unique per command id */ + __le32 DataTransferLength; /* Size of the data */ + u8 Flags; /* Direction in bit 7 */ + u8 Lun; /* LUN (normally 0) */ + u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ + u8 CDB[16]; /* Command Data Block */ +}; + +#define USB_BULK_CB_WRAP_LEN 31 +#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ +#define USB_BULK_IN_FLAG 0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + __le32 Signature; /* Should = 'USBS' */ + u32 Tag; /* Same as original command */ + __le32 Residue; /* Amount not transferred */ + u8 Status; /* See below */ +}; + +#define USB_BULK_CS_WRAP_LEN 13 +#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ +#define USB_STATUS_PASS 0 +#define USB_STATUS_FAIL 1 +#define USB_STATUS_PHASE_ERROR 2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST 0xff +#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe + + +/* CBI Interrupt data structure */ +struct interrupt_data { + u8 bType; + u8 bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN 2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST 0x00 + + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SELECT_6 0x15 +#define SC_MODE_SELECT_10 0x55 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_READ_CAPACITY 0x25 +#define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#define SC_START_STOP_UNIT 0x1b +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_VERIFY 0x2f +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + + +/*-------------------------------------------------------------------------*/ + + +struct fsg_lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + +#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL) + +static struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsg_lun, dev); +} + + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#define FSG_NUM_BUFFERS 2 + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsg_buffhd { +#ifdef FSG_BUFFHD_STATIC_BUFFER + char buf[FSG_BUFLEN]; +#else + void *buf; +#endif + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + + +/*-------------------------------------------------------------------------*/ + + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + + +/*-------------------------------------------------------------------------*/ + + +enum { +#ifndef FSG_NO_DEVICE_STRINGS + FSG_STRING_MANUFACTURER = 1, + FSG_STRING_PRODUCT, + FSG_STRING_SERIAL, + FSG_STRING_CONFIG, +#endif + FSG_STRING_INTERFACE +}; + + +#ifndef FSG_NO_OTG +static struct usb_otg_descriptor +fsg_otg_desc = { + .bLength = sizeof fsg_otg_desc, + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; +#endif + +/* There is only one interface. */ + +static struct usb_interface_descriptor +fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, +}; + +/* Three full-speed endpoint descriptors: bulk-in, bulk-out, + * and interrupt-in. */ + +static struct usb_endpoint_descriptor +fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_fs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 32, /* frames -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_fs_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_fs_intr_in_desc, +#endif + NULL, +}; + + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the config descriptor. + */ +static struct usb_endpoint_descriptor +fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_hs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_hs_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_hs_intr_in_desc, +#endif + NULL, +}; + +/* Maxpacket and other transfer characteristics vary by speed. */ +static struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsg_strings[] = { +#ifndef FSG_NO_DEVICE_STRINGS + {FSG_STRING_MANUFACTURER, fsg_string_manufacturer}, + {FSG_STRING_PRODUCT, fsg_string_product}, + {FSG_STRING_SERIAL, fsg_string_serial}, + {FSG_STRING_CONFIG, fsg_string_config}, +#endif + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + + + /*-------------------------------------------------------------------------*/ + +/* If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. */ + +static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +{ + int ro; + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + loff_t min_sectors; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + if (!ro) { + filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); + if (-EROFS == PTR_ERR(filp)) + ro = 1; + } + if (ro) + filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + LINFO(curlun, "unable to open backing file: %s\n", filename); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + if (filp->f_path.dentry) + inode = filp->f_path.dentry->d_inode; + if (inode && S_ISBLK(inode->i_mode)) { + if (bdev_read_only(inode->i_bdev)) + ro = 1; + } else if (!inode || !S_ISREG(inode->i_mode)) { + LINFO(curlun, "invalid file type: %s\n", filename); + goto out; + } + + /* If we can't read the file, it's no good. + * If we can't write the file, use it read-only. */ + if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { + LINFO(curlun, "file not readable: %s\n", filename); + goto out; + } + if (!(filp->f_op->write || filp->f_op->aio_write)) + ro = 1; + + size = i_size_read(inode->i_mapping->host); + if (size < 0) { + LINFO(curlun, "unable to find file size: %s\n", filename); + rc = (int) size; + goto out; + } + num_sectors = size >> 9; /* File size in 512-byte blocks */ + min_sectors = 1; + if (curlun->cdrom) { + num_sectors &= ~3; /* Reduce to a multiple of 2048 */ + min_sectors = 300*4; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); + rc = -ETOOSMALL; + goto out; + } + + get_file(filp); + curlun->ro = ro; + curlun->filp = filp; + curlun->file_length = size; + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s\n", filename); + rc = 0; + +out: + filp_close(filp, current->files); + return rc; +} + + +static void fsg_lun_close(struct fsg_lun *curlun) +{ + if (curlun->filp) { + LDBG(curlun, "close backing file\n"); + fput(curlun->filp); + curlun->filp = NULL; + } +} + + +/*-------------------------------------------------------------------------*/ + +/* Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). */ +static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + + if (curlun->ro || !filp) + return 0; + return vfs_fsync(filp, filp->f_path.dentry, 1); +} + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} + + +/*-------------------------------------------------------------------------*/ + + +static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) + ? curlun->ro + : curlun->initially_ro); +} + +static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + char *p; + ssize_t rc; + + down_read(filesem); + if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ + p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); + buf[rc] = '\n'; /* Add a newline */ + buf[++rc] = 0; + } + } else { /* No file, return 0 bytes */ + *buf = 0; + rc = 0; + } + up_read(filesem); + return rc; +} + + +static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t rc = count; + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* Allow the write-enable status to change only while the backing file + * is closed. */ + down_read(filesem); + if (fsg_lun_is_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + rc = -EBUSY; + } else { + curlun->ro = !!i; + curlun->initially_ro = !!i; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + } + up_read(filesem); + return rc; +} + +static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + int rc = 0; + + +#ifndef CONFIG_USB_ANDROID_MASS_STORAGE + /* disabled in android because we need to allow closing the backing file + * if the media was removed + */ + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); + return -EBUSY; /* "Door is locked" */ + } +#endif + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; /* Ugh! */ + + /* Eject current medium */ + down_write(filesem); + if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + + /* Load new medium */ + if (count > 0 && buf[0]) { + rc = fsg_lun_open(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + } + up_write(filesem); + return (rc < 0 ? rc : count); +} diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 82b30b5d..99e4aa3b 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -93,13 +93,6 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) if (!gadget_supports_altsettings(gadget)) return false; - /* SA1100 can do ECM, *without* status endpoint ... but we'll - * only use it in non-ECM mode for backwards compatibility - * (and since we currently require a status endpoint) - */ - if (gadget_is_sa1100(gadget)) - return false; - /* Everything else is *presumably* fine ... but this is a bit * chancy, so be **CERTAIN** there are no hardware issues with * your controller. Add it above if it can't handle CDC. @@ -112,7 +105,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); int eem_bind_config(struct usb_configuration *c); -#if defined(CONFIG_USB_ETH_RNDIS) || defined(CONFIG_USB_ANDROID_RNDIS) +#if defined(USB_ETH_RNDIS) || defined(CONFIG_USB_ANDROID_RNDIS) int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); diff --git a/include/linux/slab.h b/include/linux/slab.h index 2da83725..49d1247c 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -70,6 +70,11 @@ #else # define SLAB_NOTRACK 0x00000000UL #endif +#ifdef CONFIG_FAILSLAB +# define SLAB_FAILSLAB 0x02000000UL /* Fault injection mark */ +#else +# define SLAB_FAILSLAB 0x00000000UL +#endif /* The following flags affect the page allocator grouping pages by mobility */ #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ @@ -101,6 +106,7 @@ int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); const char *kmem_cache_name(struct kmem_cache *); +int kern_ptr_validate(const void *ptr, unsigned long size); int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr); /* diff --git a/include/linux/usb/android_composite.h b/include/linux/usb/android_composite.h index dc40ecca..ac09dcb7 100644 --- a/include/linux/usb/android_composite.h +++ b/include/linux/usb/android_composite.h @@ -27,11 +27,6 @@ struct android_usb_function { }; struct android_usb_product { - /* Vendor ID for this set of functions. - * Default vendor_id in platform data will be used if this is zero. - */ - __u16 vendor_id; - /* Default product ID. */ __u16 product_id; @@ -84,9 +79,6 @@ struct usb_mass_storage_platform_data { /* number of LUNS */ int nluns; - - /* bitmap of lun to indicate cdrom disk*/ - int cdrom_lun; }; /* Platform data for USB ethernet driver. */ @@ -96,13 +88,9 @@ struct usb_ether_platform_data { const char *vendorDescr; }; -extern void android_usb_set_connected(int on); - extern void android_register_function(struct android_usb_function *f); extern void android_enable_function(struct usb_function *f, int enable); -extern int android_get_model_id(void); - #endif /* __LINUX_USB_ANDROID_H */ diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 66884030..c8e62b35 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -36,8 +36,10 @@ #include #include +#include +struct usb_composite_dev; struct usb_configuration; /** @@ -100,7 +102,9 @@ struct usb_function { struct usb_descriptor_header **hs_descriptors; struct usb_configuration *config; - int hidden; + + /* disabled is zero if the function is enabled */ + int disabled; /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if @@ -128,6 +132,7 @@ struct usb_function { /* private: */ /* internals */ struct list_head list; + DECLARE_BITMAP(endpoints, 32); struct device *dev; }; @@ -138,6 +143,9 @@ int usb_function_activate(struct usb_function *); int usb_interface_id(struct usb_configuration *, struct usb_function *); +void usb_function_set_enabled(struct usb_function *, int); +void usb_composite_force_reset(struct usb_composite_dev *); + /** * ep_choose - select descriptor endpoint at current device speed * @g: gadget, connected and running at some speed @@ -332,6 +340,7 @@ struct usb_composite_dev { /* private: */ /* internals */ + unsigned int suspended:1; struct usb_device_descriptor desc; struct list_head configs; struct usb_composite_driver *driver; @@ -344,6 +353,15 @@ struct usb_composite_dev { /* protects at least deactivation count */ spinlock_t lock; + + /* switch indicating connected/disconnected state */ + struct switch_dev sw_connected; + /* switch indicating current configuration */ + struct switch_dev sw_config; + /* current connected state for sw_connected */ + bool connected; + + struct work_struct switch_work; }; extern int usb_string_id(struct usb_composite_dev *c); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index bbf45d50..d3ef42d7 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -15,6 +15,8 @@ #ifndef __LINUX_USB_GADGET_H #define __LINUX_USB_GADGET_H +#include + struct usb_ep; /** @@ -492,9 +494,13 @@ static inline void set_gadget_data(struct usb_gadget *gadget, void *data) { dev_set_drvdata(&gadget->dev, data); } static inline void *get_gadget_data(struct usb_gadget *gadget) { return dev_get_drvdata(&gadget->dev); } +static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev) +{ + return container_of(dev, struct usb_gadget, dev); +} /* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */ -#define gadget_for_each_ep(tmp,gadget) \ +#define gadget_for_each_ep(tmp, gadget) \ list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)