diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index e4308183c90..31b4592f489 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -353,6 +353,17 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) } } +static void dwc3_core_num_eps(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + dwc->num_in_eps = DWC3_NUM_IN_EPS(parms); + dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps; + + dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n", + dwc->num_in_eps, dwc->num_out_eps); +} + static void dwc3_cache_hwparams(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; @@ -431,6 +442,8 @@ static int dwc3_core_init(struct dwc3 *dwc) if (dwc->revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; + dwc3_core_num_eps(dwc); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); /* diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 553e9ff290c..c24213fe504 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -621,6 +621,14 @@ struct dwc3_hwparams { /* HWPARAMS1 */ #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) +/* HWPARAMS3 */ +#define DWC3_NUM_IN_EPS_MASK (0x1f << 18) +#define DWC3_NUM_EPS_MASK (0x3f << 12) +#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_EPS_MASK)) >> 12) +#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_IN_EPS_MASK)) >> 18) + /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) @@ -688,6 +696,8 @@ struct dwc3_scratchpad_array { * @u2pel: parameter from Set SEL request. * @u1sel: parameter from Set SEL request. * @u1pel: parameter from Set SEL request. + * @num_out_eps: number of out endpoints + * @num_in_eps: number of in endpoints * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state @@ -708,8 +718,10 @@ struct dwc3 { dma_addr_t ep0_trb_addr; dma_addr_t ep0_bounce_addr; struct dwc3_request ep0_usb_req; + /* device lock */ spinlock_t lock; + struct device *dev; struct dwc3_otg *dotg; @@ -768,6 +780,9 @@ struct dwc3 { u8 speed; + u8 num_out_eps; + u8 num_in_eps; + void *mem; struct dwc3_hwparams hwparams; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c47826ea718..06e5849e5e4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -213,15 +213,11 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) * FIFO space. Also consider the case where TxFIFO RAM space * may change dynamically based on the USB configuration. */ - for (num = 0; num < DWC3_ENDPOINTS_NUM; num++) { - struct dwc3_ep *dep = dwc->eps[num]; - int fifo_number = dep->number >> 1; + for (num = 0; num < dwc->num_in_eps; num++) { + struct dwc3_ep *dep = dwc->eps[(num << 1) | 1]; int mult = 1; int tmp; - if (!(dep->number & 1)) - continue; - if (!(dep->flags & DWC3_EP_ENABLED)) continue; @@ -280,8 +276,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) return -ENOMEM; } - dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(fifo_number), - fifo_size); + dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size); } @@ -2079,14 +2074,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = { /* -------------------------------------------------------------------------- */ -static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) +static int __devinit dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, + u8 num, u32 direction) { struct dwc3_ep *dep; - u8 epnum; + u8 i; - INIT_LIST_HEAD(&dwc->gadget.ep_list); + for (i = 0; i < num; i++) { + u8 epnum = (i << 1) | (!!direction); - for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) { dev_err(dwc->dev, "can't allocate endpoint %d\n", @@ -2100,6 +2096,7 @@ static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, (epnum & 1) ? "in" : "out"); + dep->endpoint.name = dep->name; dep->direction = (epnum & 1); @@ -2130,6 +2127,27 @@ static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) return 0; } +static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) +{ + int ret; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0); + if (ret < 0) { + dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n"); + return ret; + } + + ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1); + if (ret < 0) { + dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n"); + return ret; + } + + return 0; +} + static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -2137,6 +2155,8 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { dep = dwc->eps[epnum]; + if (!dep) + continue; /* * Physical endpoints 0 and 1 are special; they form the * bi-directional USB endpoint 0. @@ -2475,6 +2495,9 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc) struct dwc3_ep *dep; dep = dwc->eps[epnum]; + if (!dep) + continue; + if (!(dep->flags & DWC3_EP_ENABLED)) continue; @@ -2492,6 +2515,8 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) int ret; dep = dwc->eps[epnum]; + if (!dep) + continue; if (!(dep->flags & DWC3_EP_STALL)) continue; @@ -2786,7 +2811,13 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) /* Only perform resume from L2 or Early suspend states */ if (dwc->link_state == DWC3_LINK_STATE_U3) { dbg_event(0xFF, "WAKEUP", 0); + /* + * gadget_driver resume function might require some dwc3-gadget + * operations, such as ep_enable. Hence, dwc->lock must be released. + */ + spin_unlock(&dwc->lock); dwc->gadget_driver->resume(&dwc->gadget); + spin_lock(&dwc->lock); } dwc->link_state = DWC3_LINK_STATE_U0; @@ -2850,7 +2881,14 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, } } else if (next == DWC3_LINK_STATE_U3) { dbg_event(0xFF, "SUSPEND", 0); + /* + * gadget_driver suspend function might require some dwc3-gadget + * operations, such as ep_disable. Hence, dwc->lock must be + * released. + */ + spin_unlock(&dwc->lock); dwc->gadget_driver->suspend(&dwc->gadget); + spin_lock(&dwc->lock); } dwc->link_state = next; diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index bad65f353f7..b2957902062 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -109,6 +109,7 @@ #ifndef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE #include "f_ncm.c" #endif +#include "f_charger.c" #ifdef CONFIG_USB_LOCK_SUPPORT_FOR_MDM #include #endif @@ -1785,6 +1786,19 @@ static struct android_usb_function ccid_function = { .bind_config = ccid_function_bind_config, }; +/* Charger */ +static int charger_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return charger_bind_config(c); +} + +static struct android_usb_function charger_function = { + .name = "charging", + .bind_config = charger_function_bind_config, +}; + + static int mtp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) @@ -2598,6 +2612,7 @@ static struct android_usb_function *supported_functions[] = { #ifdef CONFIG_SND_PCM &audio_source_function, #endif + &charger_function, &uasp_function, NULL }; @@ -2922,8 +2937,6 @@ functions_store(struct device *pdev, struct device_attribute *attr, while (conf_str) { name = strsep(&conf_str, ","); - if (!name) - continue; is_ffs = 0; strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9af18486dcc..d74d1c720a6 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -153,7 +153,7 @@ int config_ep_by_speed(struct usb_gadget *g, } /* else: fall through */ default: - speed_desc = f->descriptors; + speed_desc = f->fs_descriptors; } /* find descriptors */ for_each_ep_desc(speed_desc, d_spd) { @@ -245,7 +245,7 @@ int usb_add_function(struct usb_configuration *config, * as full speed ... it's the function drivers that will need * to avoid bulk and ISO transfers. */ - if (!config->fullspeed && function->descriptors) + if (!config->fullspeed && function->fs_descriptors) config->fullspeed = true; if (!config->highspeed && function->hs_descriptors) config->highspeed = true; @@ -415,7 +415,7 @@ static int config_buf(struct usb_configuration *config, descriptors = f->hs_descriptors; break; default: - descriptors = f->descriptors; + descriptors = f->fs_descriptors; } if (!descriptors) @@ -724,7 +724,7 @@ static int set_config(struct usb_composite_dev *cdev, descriptors = f->hs_descriptors; break; default: - descriptors = f->descriptors; + descriptors = f->fs_descriptors; } for (; *descriptors; ++descriptors) { @@ -1219,7 +1219,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) DBG(cdev, "Config SS device in HS\n"); } } else if (gadget->l1_supported) { - cdev->desc.bcdUSB = cpu_to_le16(0x0210); + cdev->desc.bcdUSB = cpu_to_le16(0x0201); DBG(cdev, "Config HS device with LPM(L1)\n"); } diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index c52d8b62a03..83c4627b843 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -18,6 +18,7 @@ #include #include +#include /** * usb_find_descriptor_fillbuf - fill buffer with the requested descriptor @@ -189,4 +190,41 @@ usb_copy_descriptors(struct usb_descriptor_header **src) return ret; } +EXPORT_SYMBOL_GPL(usb_copy_descriptors); +int usb_assign_descriptors(struct usb_function *f, + struct usb_descriptor_header **fs, + struct usb_descriptor_header **hs, + struct usb_descriptor_header **ss) +{ + struct usb_gadget *g = f->config->cdev->gadget; + + if (fs) { + f->fs_descriptors = usb_copy_descriptors(fs); + if (!f->fs_descriptors) + goto err; + } + if (hs && gadget_is_dualspeed(g)) { + f->hs_descriptors = usb_copy_descriptors(hs); + if (!f->hs_descriptors) + goto err; + } + if (ss && gadget_is_superspeed(g)) { + f->ss_descriptors = usb_copy_descriptors(ss); + if (!f->ss_descriptors) + goto err; + } + return 0; +err: + usb_free_all_descriptors(f); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(usb_assign_descriptors); + +void usb_free_all_descriptors(struct usb_function *f) +{ + usb_free_descriptors(f->fs_descriptors); + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->ss_descriptors); +} +EXPORT_SYMBOL_GPL(usb_free_all_descriptors); diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 7c6d7ed33ac..301822b6968 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -1214,7 +1214,7 @@ static int acc_bind_config(struct usb_configuration *c) dev->cdev = c->cdev; dev->function.name = "accessory"; dev->function.strings = acc_strings, - dev->function.descriptors = fs_acc_descs; + dev->function.fs_descriptors = fs_acc_descs; dev->function.hs_descriptors = hs_acc_descs; if (gadget_is_superspeed(c->cdev->gadget)) dev->function.ss_descriptors = ss_acc_descs; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 623e1d1baed..d38551f22e4 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -875,39 +875,22 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm->notify_req->complete = acm_cdc_notify_complete; acm->notify_req->context = acm; - /* copy descriptors */ - f->descriptors = usb_copy_descriptors(acm_fs_function); - if (!f->descriptors) - goto fail; - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - acm_hs_in_desc.bEndpointAddress = - acm_fs_in_desc.bEndpointAddress; - acm_hs_out_desc.bEndpointAddress = - acm_fs_out_desc.bEndpointAddress; - acm_hs_notify_desc.bEndpointAddress = - acm_fs_notify_desc.bEndpointAddress; + acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; + acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; + acm_hs_notify_desc.bEndpointAddress = + acm_fs_notify_desc.bEndpointAddress; - /* copy descriptors */ - f->hs_descriptors = usb_copy_descriptors(acm_hs_function); - if (!f->hs_descriptors) - goto fail; - } - if (gadget_is_superspeed(c->cdev->gadget)) { - acm_ss_in_desc.bEndpointAddress = - acm_fs_in_desc.bEndpointAddress; - acm_ss_out_desc.bEndpointAddress = - acm_fs_out_desc.bEndpointAddress; + acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; + acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(acm_ss_function); - if (!f->ss_descriptors) - goto fail; - } + status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, + acm_ss_function); + if (status) + goto fail; DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, @@ -925,8 +908,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) fail: if (f->hs_descriptors) usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (acm->notify_req) gs_free_req(acm->notify, acm->notify_req); @@ -952,11 +935,7 @@ acm_unbind(struct usb_configuration *c, struct usb_function *f) #ifdef CONFIG_USB_DUN_SUPPORT modem_unregister(); #endif - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); gs_free_req(acm->notify, acm->notify_req); kfree(acm->port.func.name); kfree(acm); diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index ff2287e8edc..79732b1fd0b 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -666,7 +666,7 @@ static int adb_bind_config(struct usb_configuration *c) dev->cdev = c->cdev; dev->function.name = "adb"; - dev->function.descriptors = fs_adb_descs; + dev->function.fs_descriptors = fs_adb_descs; dev->function.hs_descriptors = hs_adb_descs; if (gadget_is_superspeed(c->cdev->gadget)) dev->function.ss_descriptors = ss_adb_descs; diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 33b645ea445..5c72021eed8 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -826,7 +826,7 @@ static struct audio_dev _audio_dev = { .set_alt = audio_set_alt, .setup = audio_setup, .disable = audio_disable, - .descriptors = fs_audio_desc, + .fs_descriptors = fs_audio_desc, .hs_descriptors = hs_audio_desc, }, .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), diff --git a/drivers/usb/gadget/f_ccid.c b/drivers/usb/gadget/f_ccid.c index 688893555d6..357e5ecc3a0 100644 --- a/drivers/usb/gadget/f_ccid.c +++ b/drivers/usb/gadget/f_ccid.c @@ -489,7 +489,7 @@ static void ccid_function_unbind(struct usb_configuration *c, { if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); } @@ -535,8 +535,8 @@ static int ccid_function_bind(struct usb_configuration *c, ccid_dev->out = ep; ep->driver_data = cdev; - f->descriptors = usb_copy_descriptors(ccid_fs_descs); - if (!f->descriptors) + f->fs_descriptors = usb_copy_descriptors(ccid_fs_descs); + if (!f->fs_descriptors) goto ep_auto_out_fail; if (gadget_is_dualspeed(cdev->gadget)) { @@ -944,7 +944,7 @@ static int ccid_bind_config(struct usb_configuration *c) pr_debug("ccid_bind_config\n"); ccid_dev->cdev = c->cdev; ccid_dev->function.name = FUNCTION_NAME; - ccid_dev->function.descriptors = ccid_fs_descs; + ccid_dev->function.fs_descriptors = ccid_fs_descs; ccid_dev->function.hs_descriptors = ccid_hs_descs; ccid_dev->function.bind = ccid_function_bind; ccid_dev->function.unbind = ccid_function_unbind; diff --git a/drivers/usb/gadget/f_charger.c b/drivers/usb/gadget/f_charger.c new file mode 100644 index 00000000000..2a11d22a16d --- /dev/null +++ b/drivers/usb/gadget/f_charger.c @@ -0,0 +1,368 @@ +/* + * f_charger.c -- USB HID function driver + * + * Copyright (C) 2010 Fabien Chouteau + * Copyright (C) 2014, The Linux Foundation. All rights reserved. + * + * 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. + * + * The Linux Foundation chooses to take subject only to the GPLv2 license + * terms, and distributes only under these terms. + */ + +#include +#include +#include +#include + +struct f_charger { + struct usb_ep *in_ep; + struct usb_function func; +}; + +static inline struct f_charger *func_to_charger(struct usb_function *f) +{ + return container_of(f, struct f_charger, func); +} + +static const uint8_t the_report_descriptor[] = { + 0x06, 0xA0, 0xFF, 0x09, 0xA5, 0xA1, 0x01, 0x09, + 0xA6, 0x09, 0xA7, 0x15, 0x80, 0x25, 0x7F, 0x75, + 0x08, 0x95, 0x02, 0x81, 0x02, 0x09, 0xA9, 0x15, + 0x80, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x91, + 0x02, 0xC0, +}; + + +static struct usb_interface_descriptor charger_interface_desc = { + .bLength = sizeof(charger_interface_desc), + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +static struct hid_descriptor charger_hid_desc = { + .bLength = sizeof(charger_hid_desc), + .bDescriptorType = 0x21, + .bcdHID = 0x0111, + .bCountryCode = 0x00, + .bNumDescriptors = 0x1, + .desc[0].bDescriptorType = 0x22, + .desc[0].wDescriptorLength = sizeof(the_report_descriptor), +}; + +/* Super-Speed Support */ + +static struct usb_endpoint_descriptor charger_ss_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 1 , + .bInterval = 16, +}; + +static struct usb_descriptor_header *charger_ss_descriptors[] = { + (struct usb_descriptor_header *)&charger_interface_desc, + (struct usb_descriptor_header *)&charger_hid_desc, + (struct usb_descriptor_header *)&charger_ss_in_ep_desc, + NULL, +}; + +/* High-Speed Support */ +static struct usb_endpoint_descriptor charger_hs_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 1 , + .bInterval = 16, +}; + +static struct usb_descriptor_header *charger_hs_descriptors[] = { + (struct usb_descriptor_header *)&charger_interface_desc, + (struct usb_descriptor_header *)&charger_hid_desc, + (struct usb_descriptor_header *)&charger_hs_in_ep_desc, + NULL, +}; + +/* Full-Speed Support */ + +static struct usb_endpoint_descriptor charger_fs_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 1, + .bInterval = 16, +}; + +static struct usb_descriptor_header *charger_fs_descriptors[] = { + (struct usb_descriptor_header *)&charger_interface_desc, + (struct usb_descriptor_header *)&charger_hid_desc, + (struct usb_descriptor_header *)&charger_fs_in_ep_desc, + NULL, +}; + +/* Strings */ + +#define CT_FUNC_HID_IDX 0 + +static struct usb_string ct_func_string_defs[] = { + [CT_FUNC_HID_IDX].s = "HID Interface", + {}, /* end of list */ +}; + +static struct usb_gadget_strings ct_func_string_table = { + .language = 0x0409, /* en-US */ + .strings = ct_func_string_defs, +}; + +static struct usb_gadget_strings *ct_func_strings[] = { + &ct_func_string_table, + NULL, +}; + + +static void charger_disable(struct usb_function *f) +{ + struct f_charger *charger = func_to_charger(f); + usb_ep_disable(charger->in_ep); + charger->in_ep->driver_data = NULL; +} + +static int hid_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int status = 0; + __u16 value, length; + bool resp_stall = false; + + value = le16_to_cpu(ctrl->wValue); + length = le16_to_cpu(ctrl->wLength); + + VDBG(cdev, + "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n", + ctrl->bRequestType, ctrl->bRequest, value); + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_REPORT): + VDBG(cdev, "get_report\n"); + + /* send an empty report */ + length = min_t(unsigned, length, + charger_hid_desc.desc[0].wDescriptorLength); + memset(req->buf, 0x0, length); + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_PROTOCOL): + VDBG(cdev, "get_protocol\n"); + resp_stall = true; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_REPORT): + VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); + resp_stall = true; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_PROTOCOL): + VDBG(cdev, "set_protocol\n"); + resp_stall = true; + break; + + case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 + | USB_REQ_GET_DESCRIPTOR): + switch (value >> 8) { + + case HID_DT_HID: + VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); + length = min_t(unsigned short, length, + charger_hid_desc.bLength); + memcpy(req->buf, &charger_hid_desc, length); + break; + + case HID_DT_REPORT: + VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); + length = min_t(unsigned short, length, + charger_hid_desc.desc[0].wDescriptorLength); + memcpy(req->buf, &the_report_descriptor, length); + break; + + default: + VDBG(cdev, "Unknown descriptor request 0x%x\n", + value >> 8); + resp_stall = true; + break; + } + break; + + default: + VDBG(cdev, "Unknown request 0x%x\n", + ctrl->bRequest); + resp_stall = true; + break; + } + + if (resp_stall) { + ERROR(cdev, "usb ep stall\n"); + return -EOPNOTSUPP; + } + + req->zero = 0; + req->length = length; + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (status < 0) + ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); + return status; +} + +static int charger_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_charger *charger = func_to_charger(f); + int status = 0; + + VDBG(cdev, "charger_set_alt intf:%d alt:%d\n", intf, alt); + + if (charger->in_ep != NULL) { + /* restart endpoint */ + if (charger->in_ep->driver_data != NULL) + usb_ep_disable(charger->in_ep); + + status = config_ep_by_speed(f->config->cdev->gadget, f, + charger->in_ep); + if (status) { + charger->in_ep->desc = NULL; + ERROR(cdev, "config_ep_by_speed FAILED!\n"); + goto fail; + } + status = usb_ep_enable(charger->in_ep); + if (status < 0) { + ERROR(cdev, "Enable IN endpoint FAILED!\n"); + goto fail; + } + charger->in_ep->driver_data = charger; + } +fail: + return status; +} + + + +static int charger_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_ep *ep; + struct f_charger *charger = func_to_charger(f); + int status; + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + + charger_interface_desc.bInterfaceNumber = status; + + /* allocate instance-specific endpoints */ + status = -ENODEV; + ep = usb_ep_autoconfig(c->cdev->gadget, &charger_fs_in_ep_desc); + if (!ep) + goto fail; + ep->driver_data = c->cdev; /* claim */ + charger->in_ep = ep; + + /* copy descriptors */ + f->fs_descriptors = usb_copy_descriptors(charger_fs_descriptors); + if (!f->fs_descriptors) + goto fail; + + if (gadget_is_dualspeed(c->cdev->gadget)) { + charger_hs_in_ep_desc.bEndpointAddress = + charger_fs_in_ep_desc.bEndpointAddress; + + f->hs_descriptors = + usb_copy_descriptors(charger_hs_descriptors); + if (!f->hs_descriptors) + goto fail; + } + + if (gadget_is_superspeed(c->cdev->gadget)) { + charger_ss_in_ep_desc.bEndpointAddress = + charger_fs_in_ep_desc.bEndpointAddress; + + f->ss_descriptors = + usb_copy_descriptors(charger_ss_descriptors); + if (!f->ss_descriptors) + goto fail; + } + + return 0; + +fail: + ERROR(f->config->cdev, "charger_bind FAILED\n"); + + if (f->ss_descriptors) + usb_free_descriptors(f->ss_descriptors); + if (f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); + + return status; +} + +static void charger_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_charger *charger = func_to_charger(f); + + /* disable/free request and end point */ + usb_ep_disable(charger->in_ep); + + /* free descriptors copies */ + if (gadget_is_superspeed(c->cdev->gadget)) + usb_free_descriptors(f->ss_descriptors); + if (gadget_is_dualspeed(c->cdev->gadget)) + usb_free_descriptors(f->hs_descriptors); + + usb_free_descriptors(f->fs_descriptors); + + kfree(charger); +} + +static int charger_bind_config(struct usb_configuration *c) +{ + struct f_charger *charger; + int status; + + /* allocate and initialize one new instance */ + charger = kzalloc(sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + charger->func.name = "charging"; + charger->func.strings = ct_func_strings; + charger->func.bind = charger_bind; + charger->func.unbind = charger_unbind; + charger->func.set_alt = charger_set_alt; + charger->func.disable = charger_disable; + charger->func.setup = hid_setup; + + status = usb_add_function(c, &charger->func); + if (status) + kfree(charger); + + return status; +} diff --git a/drivers/usb/gadget/f_conn_gadget.c b/drivers/usb/gadget/f_conn_gadget.c index 81b1b49e8c4..180fe3ac964 100644 --- a/drivers/usb/gadget/f_conn_gadget.c +++ b/drivers/usb/gadget/f_conn_gadget.c @@ -1027,12 +1027,7 @@ static int conn_gadget_bind_config(struct usb_configuration *c) dev->cdev = c->cdev; dev->function.name = "conn_gadget"; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) dev->function.fs_descriptors = fs_conn_gadget_descs; -#else - dev->function.descriptors = fs_conn_gadget_descs; -#endif - dev->function.hs_descriptors = hs_conn_gadget_descs; dev->function.ss_descriptors = ss_conn_gadget_descs; dev->function.bind = conn_gadget_function_bind; diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c index dce5f509a48..1a0713e7511 100644 --- a/drivers/usb/gadget/f_diag.c +++ b/drivers/usb/gadget/f_diag.c @@ -619,7 +619,7 @@ static void diag_function_unbind(struct usb_configuration *c, if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); /* * Channel priv_usb may point to other diag function. @@ -660,8 +660,8 @@ static int diag_function_bind(struct usb_configuration *c, status = -ENOMEM; /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(fs_diag_desc); - if (!f->descriptors) + f->fs_descriptors = usb_copy_descriptors(fs_diag_desc); + if (!f->fs_descriptors) goto fail; if (gadget_is_dualspeed(c->cdev->gadget)) { @@ -694,8 +694,8 @@ fail: usb_free_descriptors(f->ss_descriptors); if (f->hs_descriptors) usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (ctxt->out) ctxt->out->driver_data = NULL; if (ctxt->in) @@ -741,7 +741,7 @@ int diag_function_add(struct usb_configuration *c, const char *name, dev->update_pid_and_serial_num = update_pid; dev->cdev = c->cdev; dev->function.name = _ch->name; - dev->function.descriptors = fs_diag_desc; + dev->function.fs_descriptors = fs_diag_desc; dev->function.hs_descriptors = hs_diag_desc; dev->function.bind = diag_function_bind; dev->function.unbind = diag_function_unbind; diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 672c66aad6b..f221ea264a9 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -742,42 +742,24 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->notify_req->context = ecm; ecm->notify_req->complete = ecm_notify_complete; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(ecm_fs_function); - if (!f->descriptors) - goto fail; - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_ecm_in_desc.bEndpointAddress = - fs_ecm_in_desc.bEndpointAddress; - hs_ecm_out_desc.bEndpointAddress = - fs_ecm_out_desc.bEndpointAddress; - hs_ecm_notify_desc.bEndpointAddress = - fs_ecm_notify_desc.bEndpointAddress; + hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress; + hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress; + hs_ecm_notify_desc.bEndpointAddress = + fs_ecm_notify_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(ecm_hs_function); - if (!f->hs_descriptors) - goto fail; - } + ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress; + ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress; + ss_ecm_notify_desc.bEndpointAddress = + fs_ecm_notify_desc.bEndpointAddress; - if (gadget_is_superspeed(c->cdev->gadget)) { - ss_ecm_in_desc.bEndpointAddress = - fs_ecm_in_desc.bEndpointAddress; - ss_ecm_out_desc.bEndpointAddress = - fs_ecm_out_desc.bEndpointAddress; - ss_ecm_notify_desc.bEndpointAddress = - fs_ecm_notify_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(ecm_ss_function); - if (!f->ss_descriptors) - goto fail; - } + status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function, + ecm_ss_function); + if (status) + goto fail; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -795,11 +777,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (f->descriptors) - usb_free_descriptors(f->descriptors); - if (f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (ecm->notify_req) { kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); @@ -825,11 +802,7 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ecm unbind\n"); - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index a9cf20522ff..cf0ebee8556 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -274,38 +274,20 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) status = -ENOMEM; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(eem_fs_function); - if (!f->descriptors) - goto fail; - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - eem_hs_in_desc.bEndpointAddress = - eem_fs_in_desc.bEndpointAddress; - eem_hs_out_desc.bEndpointAddress = - eem_fs_out_desc.bEndpointAddress; + eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress; + eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(eem_hs_function); - if (!f->hs_descriptors) - goto fail; - } + eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress; + eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress; - if (gadget_is_superspeed(c->cdev->gadget)) { - eem_ss_in_desc.bEndpointAddress = - eem_fs_in_desc.bEndpointAddress; - eem_ss_out_desc.bEndpointAddress = - eem_fs_out_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(eem_ss_function); - if (!f->ss_descriptors) - goto fail; - } + status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function, + eem_ss_function); + if (status) + goto fail; DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n", gadget_is_superspeed(c->cdev->gadget) ? "super" : @@ -314,11 +296,7 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (f->descriptors) - usb_free_descriptors(f->descriptors); - if (f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - + usb_free_all_descriptors(f); if (eem->port.out_ep) eem->port.out_ep->driver_data = NULL; if (eem->port.in_ep) @@ -336,11 +314,7 @@ eem_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "eem unbind\n"); - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); kfree(eem); } diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 51f56ef9eee..c716179b847 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -140,6 +140,8 @@ struct ffs_data { struct usb_request *ep0req; /* P: mutex */ struct completion ep0req_completion; /* P: mutex */ int ep0req_status; /* P: mutex */ + struct completion epin_completion; + struct completion epout_completion; /* reference counter */ atomic_t ref; @@ -196,13 +198,17 @@ struct ffs_data { * Real descriptors are 16 bytes after raw_descs (so you need * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the * first full speed descriptor). raw_descs_length and - * raw_fs_descs_length do not have those 16 bytes added. + * raw_fs_hs_descs_length do not have those 16 bytes added. + * ss_desc are 8 bytes (ss_magic + count) pass the hs_descs */ const void *raw_descs; unsigned raw_descs_length; - unsigned raw_fs_descs_length; + unsigned raw_fs_hs_descs_length; + unsigned raw_ss_descs_offset; + unsigned raw_ss_descs_length; unsigned fs_descs_count; unsigned hs_descs_count; + unsigned ss_descs_count; unsigned short strings_count; unsigned short interfaces_count; @@ -300,8 +306,8 @@ struct ffs_ep { struct usb_ep *ep; /* P: ffs->eps_lock */ struct usb_request *req; /* P: epfile->mutex */ - /* [0]: full speed, [1]: high speed */ - struct usb_endpoint_descriptor *descs[2]; + /* [0]: full speed, [1]: high speed, [2]: super speed */ + struct usb_endpoint_descriptor *descs[3]; u8 num; @@ -312,6 +318,7 @@ struct ffs_epfile { /* Protects ep->ep and ep->req. */ struct mutex mutex; wait_queue_head_t wait; + atomic_t error; struct ffs_data *ffs; struct ffs_ep *ep; /* P: ffs->eps_lock */ @@ -741,8 +748,11 @@ static const struct file_operations ffs_ep0_operations = { static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) { + struct ffs_ep *ep = _ep->driver_data; ENTER(); - if (likely(req->context)) { + + /* req may be freed during unbind */ + if (ep && ep->req && likely(req->context)) { struct ffs_ep *ep = _ep->driver_data; ep->status = req->status ? req->status : req->actual; complete(req->context); @@ -754,9 +764,16 @@ static ssize_t ffs_epfile_io(struct file *file, { struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; + struct ffs_data *ffs = epfile->ffs; char *data = NULL; ssize_t ret; int halt; + int buffer_len = !read ? len : round_up(len, 1024); + + pr_debug("%s: len %d, buffer_len %d, read %d\n", __func__, len, buffer_len, read); + + if (atomic_read(&epfile->error)) + return -ENODEV; goto first_try; do { @@ -778,9 +795,24 @@ first_try: goto error; } - if (wait_event_interruptible(epfile->wait, - (ep = epfile->ep))) { - ret = -EINTR; + /* Don't wait on write if device is offline */ + if (!read) { + ret = -ENODEV; + goto error; + } + + /* + * if ep is disabled, this fails all current IOs + * and wait for next epfile open to happen + */ + if (!atomic_read(&epfile->error)) { + ret = wait_event_interruptible(epfile->wait, + (ep = epfile->ep)); + if (ret < 0) + goto error; + } + if (!ep) { + ret = -ENODEV; goto error; } } @@ -794,7 +826,7 @@ first_try: /* Allocate & copy */ if (!halt && !data) { - data = kzalloc(len, GFP_KERNEL); + data = kzalloc(buffer_len, GFP_KERNEL); if (unlikely(!data)) return -ENOMEM; @@ -831,28 +863,53 @@ first_try: ret = -EBADMSG; } else { /* Fire the request */ - DECLARE_COMPLETION_ONSTACK(done); + struct completion *done; struct usb_request *req = ep->req; - req->context = &done; req->complete = ffs_epfile_io_complete; req->buf = data; - req->length = len; + req->length = buffer_len; + if (read) { + INIT_COMPLETION(ffs->epout_completion); + req->context = done = &ffs->epout_completion; + } else { + INIT_COMPLETION(ffs->epin_completion); + req->context = done = &ffs->epin_completion; + } ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); spin_unlock_irq(&epfile->ffs->eps_lock); if (unlikely(ret < 0)) { - /* nop */ - } else if (unlikely(wait_for_completion_interruptible(&done))) { + ret = -EIO; + } else if (unlikely(wait_for_completion_interruptible(done))) { + spin_lock_irq(&epfile->ffs->eps_lock); + /* + * While we were acquiring lock endpoint got disabled + * (disconnect) or changed (composition switch) ? + */ + if (epfile->ep == ep) + usb_ep_dequeue(ep->ep, req); + spin_unlock_irq(&epfile->ffs->eps_lock); ret = -EINTR; - usb_ep_dequeue(ep->ep, req); } else { - ret = ep->status; - if (read && ret > 0 && - unlikely(copy_to_user(buf, data, ret))) - ret = -EFAULT; + spin_lock_irq(&epfile->ffs->eps_lock); + /* + * While we were acquiring lock endpoint got disabled + * (disconnect) or changed (composition switch) ? + */ + if (epfile->ep == ep) + ret = ep->status; + else + ret = -ENODEV; + spin_unlock_irq(&epfile->ffs->eps_lock); + if (read && ret > 0) { + if (ret > len) + ret = -EOVERFLOW; + else if (unlikely(copy_to_user(buf, data, ret))) + ret = -EFAULT; + } } } @@ -891,6 +948,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) file->private_data = epfile; ffs_data_opened(epfile->ffs); + atomic_set(&epfile->error, 0); return 0; } @@ -902,7 +960,9 @@ ffs_epfile_release(struct inode *inode, struct file *file) ENTER(); + atomic_set(&epfile->error, 1); ffs_data_closed(epfile->ffs); + file->private_data = NULL; return 0; } @@ -1315,6 +1375,8 @@ static struct ffs_data *ffs_data_new(void) spin_lock_init(&ffs->eps_lock); init_waitqueue_head(&ffs->ev.waitq); init_completion(&ffs->ep0req_completion); + init_completion(&ffs->epout_completion); + init_completion(&ffs->epin_completion); /* XXX REVISIT need to update it in some places, or do we? */ ffs->ev.can_stall = 1; @@ -1351,9 +1413,12 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs->stringtabs = NULL; ffs->raw_descs_length = 0; - ffs->raw_fs_descs_length = 0; + ffs->raw_fs_hs_descs_length = 0; + ffs->raw_ss_descs_offset = 0; + ffs->raw_ss_descs_length = 0; ffs->fs_descs_count = 0; ffs->hs_descs_count = 0; + ffs->ss_descs_count = 0; ffs->strings_count = 0; ffs->interfaces_count = 0; @@ -1515,6 +1580,7 @@ static void ffs_func_free(struct ffs_function *func) if (ep->ep && ep->req) usb_ep_free_request(ep->ep, ep->req); ep->req = NULL; + ep->ep = NULL; ++ep; } while (--count); spin_unlock_irqrestore(&func->ffs->eps_lock, flags); @@ -1540,9 +1606,12 @@ static void ffs_func_eps_disable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { + atomic_set(&epfile->error, 1); /* pending requests get nuked */ - if (likely(ep->ep)) + if (likely(ep->ep)) { usb_ep_disable(ep->ep); + ep->ep->driver_data = NULL; + } epfile->ep = NULL; ++ep; @@ -1563,7 +1632,15 @@ static int ffs_func_eps_enable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { struct usb_endpoint_descriptor *ds; - int desc_idx = ffs->gadget->speed == USB_SPEED_HIGH ? 1 : 0; + int desc_idx; + + if (ffs->gadget->speed == USB_SPEED_SUPER) + desc_idx = 2; + else if (ffs->gadget->speed == USB_SPEED_HIGH) + desc_idx = 1; + else + desc_idx = 0; + ds = ep->descs[desc_idx]; if (!ds) { ret = -EINVAL; @@ -1697,6 +1774,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len, } break; + case USB_DT_SS_ENDPOINT_COMP: + pr_vdebug("EP SS companion descriptor\n"); + if (length != sizeof(struct usb_ss_ep_comp_descriptor)) + goto inv_length; + break; + case USB_DT_OTHER_SPEED_CONFIG: case USB_DT_INTERFACE_POWER: case USB_DT_DEBUG: @@ -1807,8 +1890,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { - unsigned fs_count, hs_count; - int fs_len, ret = -EINVAL; + unsigned fs_count, hs_count, ss_count = 0; + int fs_len, hs_len, ss_len, ss_magic, ret = -EINVAL; char *data = _data; ENTER(); @@ -1819,9 +1902,6 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, fs_count = get_unaligned_le32(data + 8); hs_count = get_unaligned_le32(data + 12); - if (!fs_count && !hs_count) - goto einval; - data += 16; len -= 16; @@ -1840,22 +1920,58 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, } if (likely(hs_count)) { - ret = ffs_do_descs(hs_count, data, len, + hs_len = ffs_do_descs(hs_count, data, len, __ffs_data_do_entity, ffs); - if (unlikely(ret < 0)) + if (unlikely(hs_len < 0)) { + ret = hs_len; goto error; + } } else { + hs_len = 0; + } + + if ((len >= hs_len + 8)) { + /* Check SS_MAGIC for presence of ss_descs and get SS_COUNT */ + ss_magic = get_unaligned_le32(data + hs_len); + if (ss_magic != FUNCTIONFS_SS_DESC_MAGIC) + goto einval; + + ss_count = get_unaligned_le32(data + hs_len + 4); + data += hs_len + 8; + len -= hs_len + 8; + } else { + data += hs_len; + len -= hs_len; + } + + if (!fs_count && !hs_count && !ss_count) + goto einval; + + if (ss_count) { + ss_len = ffs_do_descs(ss_count, data, len, + __ffs_data_do_entity, ffs); + if (unlikely(ss_len < 0)) { + ret = ss_len; + goto error; + } + ret = ss_len; + } else { + ss_len = 0; ret = 0; } if (unlikely(len != ret)) goto einval; - ffs->raw_fs_descs_length = fs_len; - ffs->raw_descs_length = fs_len + ret; - ffs->raw_descs = _data; - ffs->fs_descs_count = fs_count; - ffs->hs_descs_count = hs_count; + ffs->raw_fs_hs_descs_length = fs_len + hs_len; + ffs->raw_ss_descs_length = ss_len; + ffs->raw_descs_length = ffs->raw_fs_hs_descs_length + ss_len; + ffs->raw_descs = _data; + ffs->fs_descs_count = fs_count; + ffs->hs_descs_count = hs_count; + ffs->ss_descs_count = ss_count; + if (ffs->ss_descs_count) + ffs->raw_ss_descs_offset = 16 + ffs->raw_fs_hs_descs_length + 8; return 0; @@ -2079,16 +2195,23 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, * If hs_descriptors is not NULL then we are reading hs * descriptors now */ - const int isHS = func->function.hs_descriptors != NULL; - unsigned idx; + const int is_hs = func->function.hs_descriptors != NULL; + const int is_ss = func->function.ss_descriptors != NULL; + unsigned ep_desc_id, idx; if (type != FFS_DESCRIPTOR) return 0; - if (isHS) + if (is_ss) { + func->function.ss_descriptors[(long)valuep] = desc; + ep_desc_id = 2; + } else if (is_hs) { func->function.hs_descriptors[(long)valuep] = desc; - else - func->function.descriptors[(long)valuep] = desc; + ep_desc_id = 1; + } else { + func->function.fs_descriptors[(long)valuep] = desc; + ep_desc_id = 0; + } if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) return 0; @@ -2096,13 +2219,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1; ffs_ep = func->eps + idx; - if (unlikely(ffs_ep->descs[isHS])) { + if (unlikely(ffs_ep->descs[ep_desc_id])) { pr_vdebug("two %sspeed descriptors for EP %d\n", - isHS ? "high" : "full", + is_ss ? "super" : "high/full", ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); return -EINVAL; } - ffs_ep->descs[isHS] = ds; + ffs_ep->descs[ep_desc_id] = ds; ffs_dump_mem(": Original ep desc", ds, ds->bLength); if (ffs_ep->ep) { @@ -2197,8 +2320,10 @@ static int ffs_func_bind(struct usb_configuration *c, const int full = !!func->ffs->fs_descs_count; const int high = gadget_is_dualspeed(func->gadget) && func->ffs->hs_descs_count; + const int super = gadget_is_superspeed(func->gadget) && + func->ffs->ss_descs_count; - int ret; + int fs_len, hs_len, ret; /* Make it a single chunk, less management later on */ struct { @@ -2207,15 +2332,16 @@ static int ffs_func_bind(struct usb_configuration *c, *fs_descs[full ? ffs->fs_descs_count + 1 : 0]; struct usb_descriptor_header *hs_descs[high ? ffs->hs_descs_count + 1 : 0]; + struct usb_descriptor_header + *ss_descs[super ? ffs->ss_descs_count + 1 : 0]; short inums[ffs->interfaces_count]; - char raw_descs[high ? ffs->raw_descs_length - : ffs->raw_fs_descs_length]; + char raw_descs[ffs->raw_descs_length]; } *data; ENTER(); - /* Only high speed but not supported by gadget? */ - if (unlikely(!(full | high))) + /* Only high/super speed but not supported by gadget? */ + if (unlikely(!(full | high | super))) return -ENOTSUPP; /* Allocate */ @@ -2225,7 +2351,15 @@ static int ffs_func_bind(struct usb_configuration *c, /* Zero */ memset(data->eps, 0, sizeof data->eps); - memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs); + /* Copy only raw (hs,fs) descriptors (until ss_magic and ss_count) */ + memcpy(data->raw_descs, ffs->raw_descs + 16, + ffs->raw_fs_hs_descs_length); + /* Copy SS descriptors */ + if (func->ffs->ss_descs_count) + memcpy(data->raw_descs + ffs->raw_fs_hs_descs_length, + ffs->raw_descs + ffs->raw_ss_descs_offset, + ffs->raw_ss_descs_length); + memset(data->inums, 0xff, sizeof data->inums); for (ret = ffs->eps_count; ret; --ret) data->eps[ret].num = -1; @@ -2240,33 +2374,53 @@ static int ffs_func_bind(struct usb_configuration *c, * numbers without worrying that it may be described later on. */ if (likely(full)) { - func->function.descriptors = data->fs_descs; - ret = ffs_do_descs(ffs->fs_descs_count, + func->function.fs_descriptors = data->fs_descs; + fs_len = ffs_do_descs(ffs->fs_descs_count, data->raw_descs, - sizeof data->raw_descs, + sizeof(data->raw_descs), __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + if (unlikely(fs_len < 0)) { + ret = fs_len; goto error; + } } else { - ret = 0; + fs_len = 0; } if (likely(high)) { func->function.hs_descriptors = data->hs_descs; - ret = ffs_do_descs(ffs->hs_descs_count, - data->raw_descs + ret, - (sizeof data->raw_descs) - ret, + hs_len = ffs_do_descs(ffs->hs_descs_count, + data->raw_descs + fs_len, + (sizeof(data->raw_descs)) - fs_len, __ffs_func_bind_do_descs, func); + if (unlikely(hs_len < 0)) { + ret = hs_len; + goto error; + } + } else { + hs_len = 0; } + if (likely(super)) { + func->function.ss_descriptors = data->ss_descs; + ret = ffs_do_descs(ffs->ss_descs_count, + data->raw_descs + fs_len + hs_len, + (sizeof(data->raw_descs)) - fs_len - hs_len, + __ffs_func_bind_do_descs, func); + if (unlikely(ret < 0)) + goto error; + } + + /* * Now handle interface numbers allocation and interface and * endpoint numbers rewriting. We can do that in one go * now. */ ret = ffs_do_descs(ffs->fs_descs_count + - (high ? ffs->hs_descs_count : 0), - data->raw_descs, sizeof data->raw_descs, + (high ? ffs->hs_descs_count : 0) + + (super ? ffs->ss_descs_count : 0), + data->raw_descs, sizeof(data->raw_descs), __ffs_func_bind_do_nums, func); if (unlikely(ret < 0)) goto error; @@ -2314,8 +2468,10 @@ static int ffs_func_set_alt(struct usb_function *f, return intf; } - if (ffs->func) + if (ffs->func) { ffs_func_eps_disable(ffs->func); + ffs->func = NULL; + } if (ffs->state != FFS_ACTIVE) return -ENODEV; diff --git a/drivers/usb/gadget/f_gps.c b/drivers/usb/gadget/f_gps.c index ef08fc5ce7c..313be4427a3 100644 --- a/drivers/usb/gadget/f_gps.c +++ b/drivers/usb/gadget/f_gps.c @@ -238,7 +238,7 @@ static void gps_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->ss_descriptors); if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); gps_free_req(dev->notify, dev->notify_req); @@ -650,9 +650,9 @@ static int gps_bind(struct usb_configuration *c, struct usb_function *f) dev->notify_req->context = dev; ret = -ENOMEM; - f->descriptors = usb_copy_descriptors(gps_fs_function); + f->fs_descriptors = usb_copy_descriptors(gps_fs_function); - if (!f->descriptors) + if (!f->fs_descriptors) goto fail; if (gadget_is_dualspeed(cdev->gadget)) { @@ -688,8 +688,8 @@ fail: usb_free_descriptors(f->ss_descriptors); if (f->hs_descriptors) usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (dev->notify_req) gps_free_req(dev->notify, dev->notify_req); ep_notify_alloc_fail: diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index b2113420b80..a4330fa67ee 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -461,7 +461,6 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) goto fail; hidg_interface_desc.bInterfaceNumber = status; - /* allocate instance-specific endpoints */ status = -ENODEV; ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); @@ -492,18 +491,15 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg->set_report_buff = NULL; - /* copy descriptors */ - f->descriptors = usb_copy_descriptors(hidg_fs_descriptors); - if (!f->descriptors) - goto fail; + hidg_hs_in_ep_desc.bEndpointAddress = + hidg_fs_in_ep_desc.bEndpointAddress; + hidg_hs_out_ep_desc.bEndpointAddress = + hidg_fs_out_ep_desc.bEndpointAddress; - if (gadget_is_dualspeed(c->cdev->gadget)) { - hidg_hs_in_ep_desc.bEndpointAddress = - hidg_fs_in_ep_desc.bEndpointAddress; - f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors); - if (!f->hs_descriptors) - goto fail; - } + status = usb_assign_descriptors(f, hidg_fs_descriptors, + hidg_hs_descriptors, NULL); + if (status) + goto fail; mutex_init(&hidg->lock); spin_lock_init(&hidg->spinlock); @@ -529,9 +525,7 @@ fail: usb_ep_free_request(hidg->in_ep, hidg->req); } - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); - + usb_free_all_descriptors(f); return status; } @@ -548,9 +542,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) kfree(hidg->req->buf); usb_ep_free_request(hidg->in_ep, hidg->req); - /* free descriptors copies */ - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); kfree(hidg->report_desc); kfree(hidg->set_report_buff); diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index 2c0cd824c66..5b773eaa7c4 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -177,6 +177,7 @@ loopback_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_loopback *loop = func_to_loop(f); int id; + int ret; /* allocate interface ID(s) */ id = usb_interface_id(c, f); @@ -201,22 +202,19 @@ autoconf_fail: loop->out_ep->driver_data = cdev; /* claim */ /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_loop_source_desc.bEndpointAddress = - fs_loop_source_desc.bEndpointAddress; - hs_loop_sink_desc.bEndpointAddress = - fs_loop_sink_desc.bEndpointAddress; - f->hs_descriptors = hs_loopback_descs; - } + hs_loop_source_desc.bEndpointAddress = + fs_loop_source_desc.bEndpointAddress; + hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; /* support super speed hardware */ - if (gadget_is_superspeed(c->cdev->gadget)) { - ss_loop_source_desc.bEndpointAddress = - fs_loop_source_desc.bEndpointAddress; - ss_loop_sink_desc.bEndpointAddress = - fs_loop_sink_desc.bEndpointAddress; - f->ss_descriptors = ss_loopback_descs; - } + ss_loop_source_desc.bEndpointAddress = + fs_loop_source_desc.bEndpointAddress; + ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs, + ss_loopback_descs); + if (ret) + return ret; DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", (gadget_is_superspeed(c->cdev->gadget) ? "super" : @@ -228,6 +226,7 @@ autoconf_fail: static void loopback_unbind(struct usb_configuration *c, struct usb_function *f) { + usb_free_all_descriptors(f); kfree(func_to_loop(f)); } @@ -379,7 +378,6 @@ static int __init loopback_bind_config(struct usb_configuration *c) return -ENOMEM; loop->function.name = "loopback"; - loop->function.descriptors = fs_loopback_descs; loop->function.bind = loopback_bind; loop->function.unbind = loopback_unbind; loop->function.set_alt = loopback_set_alt; diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 61f76c66ae8..998748efcc7 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3577,9 +3577,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) } fsg_common_put(common); - usb_free_descriptors(fsg->function.descriptors); - usb_free_descriptors(fsg->function.hs_descriptors); - usb_free_descriptors(fsg->function.ss_descriptors); + usb_free_all_descriptors(&fsg->function); kfree(fsg); } @@ -3589,6 +3587,8 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) struct usb_gadget *gadget = c->cdev->gadget; int i; struct usb_ep *ep; + unsigned max_burst; + int ret; fsg->gadget = gadget; @@ -3612,45 +3612,27 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) 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; + /* 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; - 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; - } - } + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); - if (gadget_is_superspeed(gadget)) { - unsigned max_burst; + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; - /* Calculate bMaxBurst, we know packet size is 1024 */ - max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; - fsg_ss_bulk_in_desc.bEndpointAddress = - fsg_fs_bulk_in_desc.bEndpointAddress; - fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; - - fsg_ss_bulk_out_desc.bEndpointAddress = - fsg_fs_bulk_out_desc.bEndpointAddress; - fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; - - f->ss_descriptors = usb_copy_descriptors(fsg_ss_function); - if (unlikely(!f->ss_descriptors)) { - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); - return -ENOMEM; - } - } + ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function, + fsg_ss_function); + if (ret) + goto autoconf_fail; return 0; @@ -3659,7 +3641,6 @@ autoconf_fail: return -ENOTSUPP; } - /****************************** ADD FUNCTION ******************************/ static struct usb_gadget_strings *fsg_strings_array[] = { diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c index 4ba374e8c7f..140f0d9a7db 100644 --- a/drivers/usb/gadget/f_mbim.c +++ b/drivers/usb/gadget/f_mbim.c @@ -356,6 +356,80 @@ static struct usb_descriptor_header *mbim_hs_function[] = { NULL, }; +/* Super Speed Support */ +static struct usb_endpoint_descriptor ss_mbim_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 4*cpu_to_le16(NCM_STATUS_BYTECOUNT), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_mbim_notify_comp_desc = { + .bLength = sizeof(ss_mbim_notify_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = 4*cpu_to_le16(NCM_STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor ss_mbim_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(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_mbim_in_comp_desc = { + .bLength = sizeof(ss_mbim_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_endpoint_descriptor ss_mbim_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(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_mbim_out_comp_desc = { + .bLength = sizeof(ss_mbim_out_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *mbim_ss_function[] = { + (struct usb_descriptor_header *) &mbim_iad_desc, + /* MBIM control descriptors */ + (struct usb_descriptor_header *) &mbim_control_intf, + (struct usb_descriptor_header *) &mbim_header_desc, + (struct usb_descriptor_header *) &mbim_union_desc, + (struct usb_descriptor_header *) &mbb_desc, + (struct usb_descriptor_header *) &ext_mbb_desc, + (struct usb_descriptor_header *) &ss_mbim_notify_desc, + (struct usb_descriptor_header *) &ss_mbim_notify_comp_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &mbim_data_nop_intf, + (struct usb_descriptor_header *) &mbim_data_intf, + (struct usb_descriptor_header *) &ss_mbim_in_desc, + (struct usb_descriptor_header *) &ss_mbim_in_comp_desc, + (struct usb_descriptor_header *) &ss_mbim_out_desc, + (struct usb_descriptor_header *) &ss_mbim_out_comp_desc, + NULL, +}; + /* string descriptors: */ #define STRING_CTRL_IDX 0 @@ -1490,8 +1564,8 @@ mbim_bind(struct usb_configuration *c, struct usb_function *f) mbb_desc.wMaxSegmentSize = cpu_to_le16(0xfe0); /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(mbim_fs_function); - if (!f->descriptors) + f->fs_descriptors = usb_copy_descriptors(mbim_fs_function); + if (!f->fs_descriptors) goto fail; /* @@ -1513,6 +1587,20 @@ mbim_bind(struct usb_configuration *c, struct usb_function *f) goto fail; } + if (gadget_is_superspeed(c->cdev->gadget)) { + ss_mbim_in_desc.bEndpointAddress = + fs_mbim_in_desc.bEndpointAddress; + ss_mbim_out_desc.bEndpointAddress = + fs_mbim_out_desc.bEndpointAddress; + ss_mbim_notify_desc.bEndpointAddress = + fs_mbim_notify_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->ss_descriptors = usb_copy_descriptors(mbim_ss_function); + if (!f->ss_descriptors) + goto fail; + } + /* * If MBIM is bound in a config other than the first, tell Windows * about it by returning the num as a string in the OS descriptor's @@ -1535,8 +1623,12 @@ mbim_bind(struct usb_configuration *c, struct usb_function *f) fail: pr_err("%s failed to bind, err %d\n", f->name, status); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + if (f->ss_descriptors) + usb_free_descriptors(f->ss_descriptors); + if (f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (mbim->not_port.notify_req) { kfree(mbim->not_port.notify_req->buf); @@ -1560,9 +1652,13 @@ static void mbim_unbind(struct usb_configuration *c, struct usb_function *f) struct f_mbim *mbim = func_to_mbim(f); bam_data_destroy(mbim->port_num); + + if (gadget_is_superspeed(c->cdev->gadget)) + usb_free_descriptors(f->ss_descriptors); + if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); kfree(mbim->not_port.notify_req->buf); usb_ep_free_request(mbim->not_port.notify, mbim->not_port.notify_req); diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index bc415793971..4fc7620c15a 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -420,8 +420,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) kfree(midi->id); midi->id = NULL; - usb_free_descriptors(f->descriptors); - usb_free_descriptors(f->hs_descriptors); + usb_free_all_descriptors(f); kfree(midi); } diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 9efda36a50d..05c8eb05ef7 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -1359,12 +1359,12 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) dev->function.name = "mtp"; dev->function.strings = mtp_strings; if (ptp_config) { - dev->function.descriptors = fs_ptp_descs; + dev->function.fs_descriptors = fs_ptp_descs; dev->function.hs_descriptors = hs_ptp_descs; if (gadget_is_superspeed(c->cdev->gadget)) dev->function.ss_descriptors = ss_ptp_descs; } else { - dev->function.descriptors = fs_mtp_descs; + dev->function.fs_descriptors = fs_mtp_descs; dev->function.hs_descriptors = hs_mtp_descs; if (gadget_is_superspeed(c->cdev->gadget)) dev->function.ss_descriptors = ss_mtp_descs; diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index f54949a320a..dc716984421 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -1379,30 +1379,18 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm->notify_req->context = ncm; ncm->notify_req->complete = ncm_notify_complete; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(ncm_fs_function); - if (!f->descriptors) - goto fail; - /* * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_ncm_in_desc.bEndpointAddress = - fs_ncm_in_desc.bEndpointAddress; - hs_ncm_out_desc.bEndpointAddress = - fs_ncm_out_desc.bEndpointAddress; - hs_ncm_notify_desc.bEndpointAddress = - fs_ncm_notify_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(ncm_hs_function); - if (!f->hs_descriptors) - goto fail; - } + hs_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress; + hs_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress; + hs_ncm_notify_desc.bEndpointAddress = + fs_ncm_notify_desc.bEndpointAddress; + status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, + NULL); /* * NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -1419,9 +1407,7 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (f->descriptors) - usb_free_descriptors(f->descriptors); - + usb_free_all_descriptors(f); if (ncm->notify_req) { kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); @@ -1447,9 +1433,7 @@ ncm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ncm unbind\n"); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 5f400f66aa9..d74491ad82c 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -331,23 +331,19 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) obex->port.out = ep; ep->driver_data = cdev; /* claim */ - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(fs_function); - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - obex_hs_ep_in_desc.bEndpointAddress = - obex_fs_ep_in_desc.bEndpointAddress; - obex_hs_ep_out_desc.bEndpointAddress = - obex_fs_ep_out_desc.bEndpointAddress; + obex_hs_ep_in_desc.bEndpointAddress = + obex_fs_ep_in_desc.bEndpointAddress; + obex_hs_ep_out_desc.bEndpointAddress = + obex_fs_ep_out_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(hs_function); - } + status = usb_assign_descriptors(f, fs_function, hs_function, NULL); + if (status) + goto fail; /* Avoid letting this gadget enumerate until the userspace * OBEX server is active. @@ -368,6 +364,7 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: + usb_free_all_descriptors(f); /* we might as well release our claims on endpoints */ if (obex->port.out) obex->port.out->driver_data = NULL; @@ -382,9 +379,7 @@ fail: static void obex_unbind(struct usb_configuration *c, struct usb_function *f) { - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); kfree(func_to_obex(f)); } diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 16512f921bc..3a57d0cbeef 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -515,14 +515,14 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) fp->in_ep = ep; ep->driver_data = fp; /* Claim */ - pn_hs_sink_desc.bEndpointAddress = - pn_fs_sink_desc.bEndpointAddress; - pn_hs_source_desc.bEndpointAddress = - pn_fs_source_desc.bEndpointAddress; + pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress; + pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress; /* Do not try to bind Phonet twice... */ - fp->function.descriptors = fs_pn_function; - fp->function.hs_descriptors = hs_pn_function; + status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function, + NULL); + if (status) + goto err; /* Incoming USB requests */ status = -ENOMEM; @@ -551,7 +551,7 @@ err_req: for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++) usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); err: - + usb_free_all_descriptors(f); if (fp->out_ep) fp->out_ep->driver_data = NULL; if (fp->in_ep) @@ -573,6 +573,7 @@ pn_unbind(struct usb_configuration *c, struct usb_function *f) if (fp->out_reqv[i]) usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); + usb_free_all_descriptors(f); kfree(fp); } diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c index 3db5b512918..8eb3f2f23fa 100644 --- a/drivers/usb/gadget/f_qc_ecm.c +++ b/drivers/usb/gadget/f_qc_ecm.c @@ -279,6 +279,78 @@ static struct usb_descriptor_header *ecm_qc_hs_function[] = { NULL, }; +static struct usb_endpoint_descriptor ecm_qc_ss_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(ECM_QC_STATUS_BYTECOUNT), + .bInterval = ECM_QC_LOG2_STATUS_INTERVAL_MSEC + 4, +}; + +static struct usb_ss_ep_comp_descriptor ecm_qc_ss_notify_comp_desc = { + .bLength = sizeof(ecm_qc_ss_notify_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(ECM_QC_STATUS_BYTECOUNT), +}; + +static struct usb_endpoint_descriptor ecm_qc_ss_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(1024), +}; + +static struct usb_ss_ep_comp_descriptor ecm_qc_ss_in_comp_desc = { + .bLength = sizeof(ecm_qc_ss_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_endpoint_descriptor ecm_qc_ss_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(1024), +}; + +static struct usb_ss_ep_comp_descriptor ecm_qc_ss_out_comp_desc = { + .bLength = sizeof(ecm_qc_ss_out_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_descriptor_header *ecm_qc_ss_function[] = { + /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_qc_control_intf, + (struct usb_descriptor_header *) &ecm_qc_header_desc, + (struct usb_descriptor_header *) &ecm_qc_union_desc, + (struct usb_descriptor_header *) &ecm_qc_desc, + /* NOTE: status endpoint might need to be removed */ + (struct usb_descriptor_header *) &ecm_qc_ss_notify_desc, + (struct usb_descriptor_header *) &ecm_qc_ss_notify_comp_desc, + /* data interface, altsettings 0 and 1 */ + (struct usb_descriptor_header *) &ecm_qc_data_nop_intf, + (struct usb_descriptor_header *) &ecm_qc_data_intf, + (struct usb_descriptor_header *) &ecm_qc_ss_in_desc, + (struct usb_descriptor_header *) &ecm_qc_ss_in_comp_desc, + (struct usb_descriptor_header *) &ecm_qc_ss_out_desc, + (struct usb_descriptor_header *) &ecm_qc_ss_out_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string ecm_qc_string_defs[] = { @@ -781,8 +853,8 @@ ecm_qc_bind(struct usb_configuration *c, struct usb_function *f) ecm->notify_req->complete = ecm_qc_notify_complete; /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(ecm_qc_fs_function); - if (!f->descriptors) + f->fs_descriptors = usb_copy_descriptors(ecm_qc_fs_function); + if (!f->fs_descriptors) goto fail; /* support all relevant hardware speeds... we expect that when @@ -803,6 +875,19 @@ ecm_qc_bind(struct usb_configuration *c, struct usb_function *f) goto fail; } + if (gadget_is_superspeed(c->cdev->gadget)) { + ecm_qc_ss_in_desc.bEndpointAddress = + ecm_qc_fs_in_desc.bEndpointAddress; + ecm_qc_ss_out_desc.bEndpointAddress = + ecm_qc_fs_out_desc.bEndpointAddress; + ecm_qc_ss_notify_desc.bEndpointAddress = + ecm_qc_fs_notify_desc.bEndpointAddress; + + f->ss_descriptors = usb_copy_descriptors(ecm_qc_ss_function); + if (!f->hs_descriptors) + goto fail; + } + /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). @@ -818,8 +903,13 @@ ecm_qc_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (f->descriptors) - usb_free_descriptors(f->descriptors); + + if (f->ss_descriptors) + usb_free_descriptors(f->ss_descriptors); + if (f->hs_descriptors) + usb_free_descriptors(f->hs_descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (ecm->notify_req) { kfree(ecm->notify_req->buf); @@ -847,9 +937,12 @@ ecm_qc_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ecm unbind\n"); bam_data_destroy(0); + + if (gadget_is_superspeed(c->cdev->gadget)) + usb_free_descriptors(f->ss_descriptors); if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c index 7d86b28afd5..ba87411d4f4 100644 --- a/drivers/usb/gadget/f_qc_rndis.c +++ b/drivers/usb/gadget/f_qc_rndis.c @@ -883,8 +883,8 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) rndis->notify_req->complete = rndis_qc_response_complete; /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(eth_qc_fs_function); - if (!f->descriptors) + f->fs_descriptors = usb_copy_descriptors(eth_qc_fs_function); + if (!f->fs_descriptors) goto fail; /* support all relevant hardware speeds... we expect that when @@ -959,8 +959,8 @@ fail: usb_free_descriptors(f->ss_descriptors); if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (rndis->notify_req) { kfree(rndis->notify_req->buf); @@ -993,7 +993,7 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c index 4ecfeacaf60..161da444052 100644 --- a/drivers/usb/gadget/f_qdss.c +++ b/drivers/usb/gadget/f_qdss.c @@ -644,7 +644,7 @@ static int qdss_bind_config(struct usb_configuration *c, const char *name) spin_unlock_irqrestore(&d_lock, flags); qdss->cdev = c->cdev; qdss->function.name = name; - qdss->function.descriptors = qdss_hs_desc; + qdss->function.fs_descriptors = qdss_hs_desc; qdss->function.hs_descriptors = qdss_hs_desc; qdss->function.strings = qdss_strings; qdss->function.bind = qdss_bind; diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c index 791fbabeafe..fc0bba4e70f 100644 --- a/drivers/usb/gadget/f_rmnet.c +++ b/drivers/usb/gadget/f_rmnet.c @@ -588,7 +588,7 @@ static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_descriptors(f->ss_descriptors); if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->fs_descriptors); frmnet_free_req(dev->notify, dev->notify_req); @@ -1121,9 +1121,9 @@ static int frmnet_bind(struct usb_configuration *c, struct usb_function *f) dev->notify_req->context = dev; ret = -ENOMEM; - f->descriptors = usb_copy_descriptors(rmnet_fs_function); + f->fs_descriptors = usb_copy_descriptors(rmnet_fs_function); - if (!f->descriptors) + if (!f->fs_descriptors) goto fail; if (gadget_is_dualspeed(cdev->gadget)) { @@ -1168,8 +1168,8 @@ fail: usb_free_descriptors(f->ss_descriptors); if (f->hs_descriptors) usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + if (f->fs_descriptors) + usb_free_descriptors(f->fs_descriptors); if (dev->notify_req) frmnet_free_req(dev->notify, dev->notify_req); ep_notify_alloc_fail: diff --git a/drivers/usb/gadget/f_rmnet_sdio.c b/drivers/usb/gadget/f_rmnet_sdio.c index 46e6b1224dd..2f2e9e5d8cc 100644 --- a/drivers/usb/gadget/f_rmnet_sdio.c +++ b/drivers/usb/gadget/f_rmnet_sdio.c @@ -1550,7 +1550,7 @@ int rmnet_sdio_function_add(struct usb_configuration *c) dev->function.name = "rmnet_sdio"; dev->function.strings = rmnet_sdio_strings; - dev->function.descriptors = rmnet_sdio_fs_function; + dev->function.fs_descriptors = rmnet_sdio_fs_function; dev->function.hs_descriptors = rmnet_sdio_hs_function; dev->function.bind = rmnet_sdio_bind; dev->function.unbind = rmnet_sdio_unbind; diff --git a/drivers/usb/gadget/f_rmnet_smd.c b/drivers/usb/gadget/f_rmnet_smd.c index 74a8062e0e3..15e7564a32f 100644 --- a/drivers/usb/gadget/f_rmnet_smd.c +++ b/drivers/usb/gadget/f_rmnet_smd.c @@ -1370,7 +1370,7 @@ int rmnet_smd_bind_config(struct usb_configuration *c) dev->function.name = "rmnet"; dev->function.strings = rmnet_smd_strings; - dev->function.descriptors = rmnet_smd_fs_function; + dev->function.fs_descriptors = rmnet_smd_fs_function; dev->function.hs_descriptors = rmnet_smd_hs_function; dev->function.bind = rmnet_smd_bind; dev->function.unbind = rmnet_smd_unbind; diff --git a/drivers/usb/gadget/f_rmnet_smd_sdio.c b/drivers/usb/gadget/f_rmnet_smd_sdio.c index 2fc758de994..2e671b27301 100644 --- a/drivers/usb/gadget/f_rmnet_smd_sdio.c +++ b/drivers/usb/gadget/f_rmnet_smd_sdio.c @@ -1989,7 +1989,7 @@ static int rmnet_smd_sdio_function_add(struct usb_configuration *c) dev->function.name = "rmnet_smd_sdio"; dev->function.strings = rmnet_mux_strings; - dev->function.descriptors = rmnet_mux_fs_function; + dev->function.fs_descriptors = rmnet_mux_fs_function; dev->function.hs_descriptors = rmnet_mux_hs_function; dev->function.bind = rmnet_mux_bind; dev->function.unbind = rmnet_mux_unbind; diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 7d503c8a8a8..64473783c1b 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -831,42 +831,22 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->notify_req->context = rndis; rndis->notify_req->complete = rndis_response_complete; - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(eth_fs_function); - if (!f->descriptors) - goto fail; - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_in_desc.bEndpointAddress = - fs_in_desc.bEndpointAddress; - hs_out_desc.bEndpointAddress = - fs_out_desc.bEndpointAddress; - hs_notify_desc.bEndpointAddress = - fs_notify_desc.bEndpointAddress; + hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; + hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; + hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(eth_hs_function); - if (!f->hs_descriptors) - goto fail; - } + ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; + ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; + ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; - if (gadget_is_superspeed(c->cdev->gadget)) { - ss_in_desc.bEndpointAddress = - fs_in_desc.bEndpointAddress; - ss_out_desc.bEndpointAddress = - fs_out_desc.bEndpointAddress; - ss_notify_desc.bEndpointAddress = - fs_notify_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(eth_ss_function); - if (!f->ss_descriptors) - goto fail; - } + status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, + eth_ss_function); + if (status) + goto fail; rndis->port.open = rndis_open; rndis->port.close = rndis_close; @@ -898,12 +878,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); if (rndis->notify_req) { kfree(rndis->notify_req->buf); @@ -931,11 +906,7 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) rndis_deregister(rndis->config); rndis_exit(); - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 57cbc033bc4..1fb45192191 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -818,49 +818,33 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) gser->notify_req->context = gser; #endif - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(gser_fs_function); - - if (!f->descriptors) - goto fail; - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ + gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress; + gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; + + gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress; + gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; + if (gadget_is_dualspeed(c->cdev->gadget)) { - gser_hs_in_desc.bEndpointAddress = - 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); - - if (!f->hs_descriptors) - goto fail; - } if (gadget_is_superspeed(c->cdev->gadget)) { - gser_ss_in_desc.bEndpointAddress = - gser_fs_in_desc.bEndpointAddress; - gser_ss_out_desc.bEndpointAddress = - gser_fs_out_desc.bEndpointAddress; #ifdef CONFIG_MODEM_SUPPORT gser_ss_notify_desc.bEndpointAddress = gser_fs_notify_desc.bEndpointAddress; #endif - - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(gser_ss_function); - if (!f->ss_descriptors) - goto fail; } + status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function, + gser_ss_function); + if (status) + goto fail; DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", gser->port_num, gadget_is_superspeed(c->cdev->gadget) ? "super" : @@ -869,12 +853,6 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (f->ss_descriptors) - usb_free_descriptors(f->ss_descriptors); - if (f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - if (f->descriptors) - usb_free_descriptors(f->descriptors); #ifdef CONFIG_MODEM_SUPPORT if (gser->notify_req) gs_free_req(gser->notify, gser->notify_req); @@ -900,11 +878,7 @@ 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); - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); #ifdef CONFIG_MODEM_SUPPORT gs_free_req(gser->notify, gser->notify_req); #endif diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 7aa7ac82c02..d8d3fa764e7 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -191,6 +191,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_sourcesink *ss = func_to_ss(f); int id; + int ret; /* allocate interface ID(s) */ id = usb_interface_id(c, f); @@ -214,22 +215,57 @@ autoconf_fail: ss->out_ep->driver_data = cdev; /* claim */ /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - hs_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; - f->hs_descriptors = hs_source_sink_descs; - } + hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; + + /* + * Fill in the HS isoc descriptors from the module parameters. + * We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ + hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; + hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; + hs_iso_source_desc.bInterval = isoc_interval; + hs_iso_source_desc.bEndpointAddress = + fs_iso_source_desc.bEndpointAddress; + + hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; + hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11; + hs_iso_sink_desc.bInterval = isoc_interval; + hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; /* support super speed hardware */ - if (gadget_is_superspeed(c->cdev->gadget)) { - ss_source_desc.bEndpointAddress = - fs_source_desc.bEndpointAddress; - ss_sink_desc.bEndpointAddress = - fs_sink_desc.bEndpointAddress; - f->ss_descriptors = ss_source_sink_descs; - } + ss_source_desc.bEndpointAddress = + fs_source_desc.bEndpointAddress; + ss_sink_desc.bEndpointAddress = + fs_sink_desc.bEndpointAddress; + + /* + * Fill in the SS isoc descriptors from the module parameters. + * We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ + ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; + ss_iso_source_desc.bInterval = isoc_interval; + ss_iso_source_comp_desc.bmAttributes = isoc_mult; + ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst; + ss_iso_source_comp_desc.wBytesPerInterval = + isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); + ss_iso_source_desc.bEndpointAddress = + fs_iso_source_desc.bEndpointAddress; + + ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; + ss_iso_sink_desc.bInterval = isoc_interval; + ss_iso_sink_comp_desc.bmAttributes = isoc_mult; + ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst; + ss_iso_sink_comp_desc.wBytesPerInterval = + isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); + ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_source_sink_descs, + hs_source_sink_descs, ss_source_sink_descs); + if (ret) + return ret; DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", (gadget_is_superspeed(c->cdev->gadget) ? "super" : @@ -241,6 +277,7 @@ autoconf_fail: static void sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) { + usb_free_all_descriptors(f); kfree(func_to_ss(f)); } @@ -461,7 +498,6 @@ static int __init sourcesink_bind_config(struct usb_configuration *c) return -ENOMEM; ss->function.name = "source/sink"; - ss->function.descriptors = fs_source_sink_descs; ss->function.bind = sourcesink_bind; ss->function.unbind = sourcesink_unbind; ss->function.set_alt = sourcesink_set_alt; diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index e5bb9662863..fc61998344d 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -319,38 +319,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) geth->port.out_ep = ep; ep->driver_data = cdev; /* claim */ - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(fs_eth_function); - if (!f->descriptors) - goto fail; - /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - hs_subset_in_desc.bEndpointAddress = - fs_subset_in_desc.bEndpointAddress; - hs_subset_out_desc.bEndpointAddress = - fs_subset_out_desc.bEndpointAddress; + hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress; + hs_subset_out_desc.bEndpointAddress = + fs_subset_out_desc.bEndpointAddress; - /* copy descriptors, and track endpoint copies */ - f->hs_descriptors = usb_copy_descriptors(hs_eth_function); - if (!f->hs_descriptors) - goto fail; - } + ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress; + ss_subset_out_desc.bEndpointAddress = + fs_subset_out_desc.bEndpointAddress; - if (gadget_is_superspeed(c->cdev->gadget)) { - ss_subset_in_desc.bEndpointAddress = - fs_subset_in_desc.bEndpointAddress; - ss_subset_out_desc.bEndpointAddress = - fs_subset_out_desc.bEndpointAddress; - - /* copy descriptors, and track endpoint copies */ - f->ss_descriptors = usb_copy_descriptors(ss_eth_function); - if (!f->ss_descriptors) - goto fail; - } + status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function, + ss_eth_function); + if (status) + goto fail; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -364,11 +348,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (f->descriptors) - usb_free_descriptors(f->descriptors); - if (f->hs_descriptors) - usb_free_descriptors(f->hs_descriptors); - + usb_free_all_descriptors(f); /* we might as well release our claims on endpoints */ if (geth->port.out_ep) geth->port.out_ep->driver_data = NULL; @@ -383,11 +363,7 @@ fail: static void geth_unbind(struct usb_configuration *c, struct usb_function *f) { - if (gadget_is_superspeed(c->cdev->gadget)) - usb_free_descriptors(f->ss_descriptors); - if (gadget_is_dualspeed(c->cdev->gadget)) - usb_free_descriptors(f->hs_descriptors); - usb_free_descriptors(f->descriptors); + usb_free_all_descriptors(f); geth_string_defs[1].s = NULL; kfree(func_to_geth(f)); } diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c index 29e32538b4d..ea7636f1f4d 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/f_uac1.c @@ -1059,7 +1059,7 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_audio *audio = func_to_audio(f); int status; - struct usb_ep *ep; + struct usb_ep *ep = NULL; u8 epaddr; @@ -1127,22 +1127,15 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) epaddr = speaker_as_ep_out_desc.bEndpointAddress & ~USB_DIR_IN; speaker_as_iso_out.id = epaddr; - /* support all relevant hardware speeds. we expect that when - * hardware is dual speed, all bulk-capable endpoints work at - * both speeds - */ - /* copy descriptors, and track endpoint copies */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - c->highspeed = true; - f->hs_descriptors = usb_copy_descriptors(f_audio_desc); - } else { - f->descriptors = usb_copy_descriptors(f_audio_desc); - } - + status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL); + if (status) + goto fail; return 0; fail: + if (ep) + ep->driver_data = NULL; return status; } @@ -1151,8 +1144,7 @@ f_audio_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_audio *audio = func_to_audio(f); - usb_free_descriptors(f->descriptors); - usb_free_descriptors(f->hs_descriptors); + usb_free_all_descriptors(f); kfree(audio); } diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c index e7cc4de93e3..d533f531159 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/f_uac2.c @@ -994,9 +994,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; hs_epin_desc.wMaxPacketSize = fs_epin_desc.wMaxPacketSize; - fn->descriptors = usb_copy_descriptors(fs_audio_desc); - if (gadget_is_dualspeed(gadget)) - fn->hs_descriptors = usb_copy_descriptors(hs_audio_desc); + ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL); + if (ret) + goto err; prm = &agdev->uac2.c_prm; prm->max_psize = hs_epout_desc.wMaxPacketSize; @@ -1016,15 +1016,25 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) "%s:%d Error!\n", __func__, __LINE__); } - return alsa_uac2_init(agdev); + ret = alsa_uac2_init(agdev); + if (ret) + goto err; + return 0; +err: + kfree(agdev->uac2.p_prm.rbuf); + kfree(agdev->uac2.c_prm.rbuf); + usb_free_all_descriptors(fn); + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; + return -EINVAL; } static void afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) { struct audio_dev *agdev = func_to_agdev(fn); - struct usb_composite_dev *cdev = cfg->cdev; - struct usb_gadget *gadget = cdev->gadget; struct uac2_rtd_params *prm; alsa_uac2_exit(agdev); @@ -1034,10 +1044,7 @@ afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) prm = &agdev->uac2.c_prm; kfree(prm->rbuf); - - if (gadget_is_dualspeed(gadget)) - usb_free_descriptors(fn->hs_descriptors); - usb_free_descriptors(fn->descriptors); + usb_free_all_descriptors(fn); if (agdev->in_ep) agdev->in_ep->driver_data = NULL; diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index a0abc65ecbe..96c03237fc3 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -468,8 +468,7 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); - kfree(f->descriptors); - kfree(f->hs_descriptors); + usb_free_all_descriptors(f); kfree(uvc); } @@ -514,9 +513,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; - /* Copy descriptors. */ - f->descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); - f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); + /* Copy descriptors */ + f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); + if (gadget_is_dualspeed(cdev->gadget)) + f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); + if (gadget_is_superspeed(c->cdev->gadget)) + f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); @@ -564,9 +566,7 @@ error: kfree(uvc->control_buf); } - kfree(f->descriptors); - kfree(f->hs_descriptors); - kfree(f->ss_descriptors); + usb_free_all_descriptors(f); return ret; } diff --git a/drivers/usb/gadget/multi_config.c b/drivers/usb/gadget/multi_config.c index d5175ad2a7c..78947de355c 100644 --- a/drivers/usb/gadget/multi_config.c +++ b/drivers/usb/gadget/multi_config.c @@ -174,7 +174,7 @@ int change_conf(struct usb_function *f, if (speed == USB_SPEED_HIGH) descriptors = f->hs_descriptors; else - descriptors = f->descriptors; + descriptors = f->fs_descriptors; if (!descriptors) { USB_DBG_ESS("descriptor is not available\n"); return -EFAULT; @@ -215,7 +215,7 @@ int change_conf(struct usb_function *f, if (speed == USB_SPEED_HIGH) descriptors = f->hs_descriptors; else - descriptors = f->descriptors; + descriptors = f->fs_descriptors; status = usb_descriptor_fillbuf( next, len, (const struct usb_descriptor_header **) diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 4e4dc1f5f38..a4c0f9a80d1 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1276,88 +1276,6 @@ unknown: /* host either stalls (value < 0) or reports success */ return value; -} - -static void -printer_disconnect(struct usb_gadget *gadget) -{ - struct printer_dev *dev = get_gadget_data(gadget); - unsigned long flags; - - DBG(dev, "%s\n", __func__); - - spin_lock_irqsave(&dev->lock, flags); - - printer_reset_interface(dev); - - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void -printer_unbind(struct usb_gadget *gadget) -{ - struct printer_dev *dev = get_gadget_data(gadget); - struct usb_request *req; - - - DBG(dev, "%s\n", __func__); - - /* Remove sysfs files */ - device_destroy(usb_gadget_class, g_printer_devno); - - /* Remove Character Device */ - cdev_del(&dev->printer_cdev); - - /* we must already have been disconnected ... no i/o may be active */ - WARN_ON(!list_empty(&dev->tx_reqs_active)); - WARN_ON(!list_empty(&dev->rx_reqs_active)); - - /* Free all memory for this driver. */ - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - - if (dev->current_rx_req != NULL) - printer_req_free(dev->out_ep, dev->current_rx_req); - - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - - while (!list_empty(&dev->rx_buffers)) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - - if (dev->req) { - printer_req_free(gadget->ep0, dev->req); - dev->req = NULL; - } - - set_gadget_data(gadget, NULL); -} - -static int __init -printer_bind(struct usb_gadget *gadget) -{ - struct printer_dev *dev; - struct usb_ep *in_ep, *out_ep; - int status = -ENOMEM; - int gcnum; - size_t len; - u32 i; - struct usb_request *req; - - dev = &usb_printer_gadget; - /* Setup the sysfs files for the printer gadget. */ dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 72c4a992594..0b09d5b9768 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -833,9 +833,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; - /* throttle highspeed IRQ rate back slightly */ + /* throttle high/super speed IRQ rate back slightly */ if (gadget_is_dualspeed(dev->gadget) && - (dev->gadget->speed == USB_SPEED_HIGH)) { + (dev->gadget->speed == USB_SPEED_HIGH || + dev->gadget->speed == USB_SPEED_SUPER)) { dev->tx_qlen++; if (dev->tx_qlen == (qmult/2)) { req->no_interrupt = 0; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 5865e472f91..f7e97e10323 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -112,7 +112,6 @@ struct usb_configuration; struct usb_function { const char *name; struct usb_gadget_strings **strings; - struct usb_descriptor_header **descriptors; struct usb_descriptor_header **hs_descriptors; struct usb_descriptor_header **ss_descriptors; struct usb_descriptor_header **fs_descriptors; diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index 63cedcd6d31..c814f7bbe2f 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h @@ -13,6 +13,7 @@ enum { FUNCTIONFS_STRINGS_MAGIC = 2 }; +#define FUNCTIONFS_SS_DESC_MAGIC 0x0055DE5C #ifndef __KERNEL__ @@ -50,7 +51,11 @@ struct usb_functionfs_descs_head { * | 12 | hs_count | LE32 | number of high-speed descriptors | * | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors | * | | hs_descrs | Descriptor[] | list of high-speed descriptors | + * | | ss_magic | LE32 | FUNCTIONFS_SS_DESC_MAGIC | + * | | ss_count | LE32 | number of super-speed descriptors | + * | | ss_descrs | Descriptor[] | list of super-speed descriptors | * + * ss_magic: if present then it implies that SS_DESCs are also present * descs are just valid USB descriptors and have the following format: * * | off | name | type | description | diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 1a949109eb8..f6f2ab13c50 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -956,6 +956,13 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v) kfree(v); } +struct usb_function; +int usb_assign_descriptors(struct usb_function *f, + struct usb_descriptor_header **fs, + struct usb_descriptor_header **hs, + struct usb_descriptor_header **ss); +void usb_free_all_descriptors(struct usb_function *f); + /*-------------------------------------------------------------------------*/ /* utility to simplify map/unmap of usb_requests to/from DMA */