diff options
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 157 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.h | 1 | ||||
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 195 | ||||
-rw-r--r-- | drivers/usb/class/usbtmc.c | 11 |
4 files changed, 196 insertions, 168 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 94a14f5dc4d4..78f0f85bebdc 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -368,17 +368,17 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) if (!test_and_clear_bit(index, &acm->read_urbs_free)) return 0; - dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); - res = usb_submit_urb(acm->read_urbs[index], mem_flags); if (res) { if (res != -EPERM) { dev_err(&acm->data->dev, - "%s - usb_submit_urb failed: %d\n", - __func__, res); + "urb %d failed submission with %d\n", + index, res); } set_bit(index, &acm->read_urbs_free); return res; + } else { + dev_vdbg(&acm->data->dev, "submitted urb %d\n", index); } return 0; @@ -415,8 +415,9 @@ static void acm_read_bulk_callback(struct urb *urb) unsigned long flags; int status = urb->status; - dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, - rb->index, urb->actual_length); + dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", + rb->index, urb->actual_length, + status); if (!acm->dev) { set_bit(rb->index, &acm->read_urbs_free); @@ -426,8 +427,6 @@ static void acm_read_bulk_callback(struct urb *urb) if (status) { set_bit(rb->index, &acm->read_urbs_free); - dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", - __func__, status); if ((status != -ENOENT) || (urb->actual_length == 0)) return; } @@ -462,8 +461,7 @@ static void acm_write_bulk(struct urb *urb) int status = urb->status; if (status || (urb->actual_length != urb->transfer_buffer_length)) - dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", - __func__, + dev_vdbg(&acm->data->dev, "wrote len %d/%d, status %d\n", urb->actual_length, urb->transfer_buffer_length, status); @@ -478,8 +476,6 @@ static void acm_softint(struct work_struct *work) { struct acm *acm = container_of(work, struct acm, work); - dev_vdbg(&acm->data->dev, "%s\n", __func__); - tty_port_tty_wakeup(&acm->port); } @@ -492,8 +488,6 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) struct acm *acm; int retval; - dev_dbg(tty->dev, "%s\n", __func__); - acm = acm_get_by_minor(tty->index); if (!acm) return -ENODEV; @@ -515,8 +509,6 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); - return tty_port_open(&acm->port, tty, filp); } @@ -545,8 +537,6 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) int retval = -ENODEV; int i; - dev_dbg(&acm->control->dev, "%s\n", __func__); - mutex_lock(&acm->mutex); if (acm->disconnected) goto disconnected; @@ -607,8 +597,6 @@ static void acm_port_destruct(struct tty_port *port) { struct acm *acm = container_of(port, struct acm, port); - dev_dbg(&acm->control->dev, "%s\n", __func__); - acm_release_minor(acm); usb_put_intf(acm->control); kfree(acm->country_codes); @@ -622,8 +610,6 @@ static void acm_port_shutdown(struct tty_port *port) struct acm_wb *wb; int i; - dev_dbg(&acm->control->dev, "%s\n", __func__); - /* * Need to grab write_lock to prevent race with resume, but no need to * hold it due to the tty-port initialised flag. @@ -654,21 +640,21 @@ static void acm_port_shutdown(struct tty_port *port) static void acm_tty_cleanup(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_put(&acm->port); } static void acm_tty_hangup(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_hangup(&acm->port); } static void acm_tty_close(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; - dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_close(&acm->port, tty, filp); } @@ -684,7 +670,7 @@ static int acm_tty_write(struct tty_struct *tty, if (!count) return 0; - dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "%d bytes from tty layer\n", count); spin_lock_irqsave(&acm->write_lock, flags); wbn = acm_wb_alloc(acm); @@ -701,7 +687,7 @@ static int acm_tty_write(struct tty_struct *tty, } count = (count > acm->writesize) ? acm->writesize : count; - dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "writing %d bytes\n", count); memcpy(wb->buf, buf, count); wb->len = count; @@ -946,7 +932,7 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) DECLARE_WAITQUEUE(wait, current); struct async_icount old, new; - if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD )) + if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD)) return -EINVAL; do { spin_lock_irq(&acm->read_lock); @@ -1146,7 +1132,7 @@ static int acm_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; - struct usb_cdc_country_functional_desc *cfd = NULL; + struct usb_cdc_call_mgmt_descriptor *cmgmd = NULL; unsigned char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -1155,18 +1141,16 @@ static int acm_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *epread = NULL; struct usb_endpoint_descriptor *epwrite = NULL; struct usb_device *usb_dev = interface_to_usbdev(intf); + struct usb_cdc_parsed_header h; struct acm *acm; int minor; int ctrlsize, readsize; u8 *buf; - u8 ac_management_function = 0; - u8 call_management_function = 0; - int call_interface_num = -1; - int data_interface_num = -1; + int call_intf_num = -1; + int data_intf_num = -1; unsigned long quirks; int num_rx_buf; int i; - unsigned int elength = 0; int combined_interfaces = 0; struct device *tty_dev; int rv = -ENOMEM; @@ -1195,6 +1179,9 @@ static int acm_probe(struct usb_interface *intf, return -EINVAL; } + if (!intf->cur_altsetting) + return -EINVAL; + if (!buflen) { if (intf->cur_altsetting->endpoint && intf->cur_altsetting->endpoint->extralen && @@ -1210,70 +1197,22 @@ static int acm_probe(struct usb_interface *intf, } } - while (buflen > 0) { - elength = buffer[0]; - if (!elength) { - dev_err(&intf->dev, "skipping garbage byte\n"); - elength = 1; - goto next_desc; - } - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - - switch (buffer[2]) { - case USB_CDC_UNION_TYPE: /* we've found it */ - if (elength < sizeof(struct usb_cdc_union_desc)) - goto next_desc; - if (union_header) { - dev_err(&intf->dev, "More than one " - "union descriptor, skipping ...\n"); - goto next_desc; - } - union_header = (struct usb_cdc_union_desc *)buffer; - break; - case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ - if (elength < sizeof(struct usb_cdc_country_functional_desc)) - goto next_desc; - cfd = (struct usb_cdc_country_functional_desc *)buffer; - break; - case USB_CDC_HEADER_TYPE: /* maybe check version */ - break; /* for now we ignore it */ - case USB_CDC_ACM_TYPE: - if (elength < 4) - goto next_desc; - ac_management_function = buffer[3]; - break; - case USB_CDC_CALL_MANAGEMENT_TYPE: - if (elength < 5) - goto next_desc; - call_management_function = buffer[3]; - call_interface_num = buffer[4]; - break; - default: - /* - * there are LOTS more CDC descriptors that - * could legitimately be found here. - */ - dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %ud\n", - buffer[2], elength); - break; - } -next_desc: - buflen -= elength; - buffer += elength; - } + cdc_parse_cdc_header(&h, intf, buffer, buflen); + union_header = h.usb_cdc_union_desc; + cmgmd = h.usb_cdc_call_mgmt_descriptor; + if (cmgmd) + call_intf_num = cmgmd->bDataInterface; if (!union_header) { - if (call_interface_num > 0) { + if (call_intf_num > 0) { dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); /* quirks for Droids MuIn LCD */ - if (quirks & NO_DATA_INTERFACE) + if (quirks & NO_DATA_INTERFACE) { data_interface = usb_ifnum_to_if(usb_dev, 0); - else - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); + } else { + data_intf_num = call_intf_num; + data_interface = usb_ifnum_to_if(usb_dev, data_intf_num); + } control_interface = intf; } else { if (intf->cur_altsetting->desc.bNumEndpoints != 3) { @@ -1287,16 +1226,19 @@ next_desc: } } } else { + data_intf_num = union_header->bSlaveInterface0; control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); + data_interface = usb_ifnum_to_if(usb_dev, data_intf_num); } if (!control_interface || !data_interface) { dev_dbg(&intf->dev, "no interfaces\n"); return -ENODEV; } + if (!data_interface->cur_altsetting || !control_interface->cur_altsetting) + return -ENODEV; - if (data_interface_num != call_interface_num) + if (data_intf_num != call_intf_num) dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); if (control_interface == data_interface) { @@ -1379,11 +1321,8 @@ made_compressed_probe: goto alloc_fail; minor = acm_alloc_minor(acm); - if (minor < 0) { - dev_err(&intf->dev, "no more free acm devices\n"); - kfree(acm); - return -ENODEV; - } + if (minor < 0) + goto alloc_fail1; ctrlsize = usb_endpoint_maxp(epctrl); readsize = usb_endpoint_maxp(epread) * @@ -1394,7 +1333,8 @@ made_compressed_probe: acm->data = data_interface; acm->minor = minor; acm->dev = usb_dev; - acm->ctrl_caps = ac_management_function; + if (h.usb_cdc_acm_descriptor) + acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities; if (quirks & NO_CAP_LINE) acm->ctrl_caps &= ~USB_CDC_CAP_LINE; acm->ctrlsize = ctrlsize; @@ -1405,7 +1345,6 @@ made_compressed_probe: spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); - acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); acm->is_int_ep = usb_endpoint_xfer_int(epread); if (acm->is_int_ep) acm->bInterval = epread->bInterval; @@ -1445,14 +1384,14 @@ made_compressed_probe: urb->transfer_dma = rb->dma; if (acm->is_int_ep) { usb_fill_int_urb(urb, acm->dev, - acm->rx_endpoint, + usb_rcvintpipe(usb_dev, epread->bEndpointAddress), rb->base, acm->readsize, acm_read_bulk_callback, rb, acm->bInterval); } else { usb_fill_bulk_urb(urb, acm->dev, - acm->rx_endpoint, + usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress), rb->base, acm->readsize, acm_read_bulk_callback, rb); @@ -1488,7 +1427,10 @@ made_compressed_probe: if (i < 0) goto alloc_fail7; - if (cfd) { /* export the country data */ + if (h.usb_cdc_country_functional_desc) { /* export the country data */ + struct usb_cdc_country_functional_desc * cfd = + h.usb_cdc_country_functional_desc; + acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); if (!acm->country_codes) goto skip_countries; @@ -1572,6 +1514,7 @@ alloc_fail4: usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail2: acm_release_minor(acm); +alloc_fail1: kfree(acm); alloc_fail: return rv; @@ -1581,8 +1524,6 @@ static void stop_data_traffic(struct acm *acm) { int i; - dev_dbg(&acm->control->dev, "%s\n", __func__); - usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); @@ -1599,8 +1540,6 @@ static void acm_disconnect(struct usb_interface *intf) struct tty_struct *tty; int i; - dev_dbg(&intf->dev, "%s\n", __func__); - /* sibling interface is already cleaning up */ if (!acm) return; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 05ce308d5d2a..1f1eabfd8462 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -96,7 +96,6 @@ struct acm { struct acm_rb read_buffers[ACM_NR]; struct acm_wb *putbuffer; /* for acm_tty_put_char() */ int rx_buflimit; - int rx_endpoint; spinlock_t read_lock; int write_used; /* number of non-empty write buffers */ int transmitting; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 61ea87917433..0a6369510f2d 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -58,6 +58,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_SUSPENDING 8 #define WDM_RESETTING 9 #define WDM_OVERFLOW 10 +#define WDM_DRAIN_ON_OPEN 11 #define WDM_MAX 16 @@ -154,6 +155,9 @@ static void wdm_out_callback(struct urb *urb) wake_up(&desc->wait); } +/* forward declaration */ +static int service_outstanding_interrupt(struct wdm_device *desc); + static void wdm_in_callback(struct urb *urb) { struct wdm_device *desc = urb->context; @@ -167,18 +171,18 @@ static void wdm_in_callback(struct urb *urb) switch (status) { case -ENOENT: dev_dbg(&desc->intf->dev, - "nonzero urb status received: -ENOENT"); + "nonzero urb status received: -ENOENT\n"); goto skip_error; case -ECONNRESET: dev_dbg(&desc->intf->dev, - "nonzero urb status received: -ECONNRESET"); + "nonzero urb status received: -ECONNRESET\n"); goto skip_error; case -ESHUTDOWN: dev_dbg(&desc->intf->dev, - "nonzero urb status received: -ESHUTDOWN"); + "nonzero urb status received: -ESHUTDOWN\n"); goto skip_error; case -EPIPE: - dev_err(&desc->intf->dev, + dev_dbg(&desc->intf->dev, "nonzero urb status received: -EPIPE\n"); break; default: @@ -188,7 +192,13 @@ static void wdm_in_callback(struct urb *urb) } } - desc->rerr = status; + /* + * only set a new error if there is no previous error. + * Errors are only cleared during read/open + */ + if (desc->rerr == 0) + desc->rerr = status; + if (length + desc->length > desc->wMaxCommand) { /* The buffer would overflow */ set_bit(WDM_OVERFLOW, &desc->flags); @@ -200,10 +210,40 @@ static void wdm_in_callback(struct urb *urb) desc->reslength = length; } } + + /* + * Handling devices with the WDM_DRAIN_ON_OPEN flag set: + * If desc->resp_count is unset, then the urb was submitted + * without a prior notification. If the device returned any + * data, then this implies that it had messages queued without + * notifying us. Continue reading until that queue is flushed. + */ + if (!desc->resp_count) { + if (!length) { + /* do not propagate the expected -EPIPE */ + desc->rerr = 0; + goto unlock; + } + dev_dbg(&desc->intf->dev, "got %d bytes without notification\n", length); + set_bit(WDM_RESPONDING, &desc->flags); + usb_submit_urb(desc->response, GFP_ATOMIC); + } + skip_error: + set_bit(WDM_READ, &desc->flags); wake_up(&desc->wait); - set_bit(WDM_READ, &desc->flags); + if (desc->rerr) { + /* + * Since there was an error, userspace may decide to not read + * any data after poll'ing. + * We should respond to further attempts from the device to send + * data, so that we can get unstuck. + */ + service_outstanding_interrupt(desc); + } + +unlock: spin_unlock(&desc->iuspin); } @@ -244,18 +284,18 @@ static void wdm_int_callback(struct urb *urb) switch (dr->bNotificationType) { case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: dev_dbg(&desc->intf->dev, - "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d", + "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d\n", le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength)); break; case USB_CDC_NOTIFY_NETWORK_CONNECTION: dev_dbg(&desc->intf->dev, - "NOTIFY_NETWORK_CONNECTION %s network", + "NOTIFY_NETWORK_CONNECTION %s network\n", dr->wValue ? "connected to" : "disconnected from"); goto exit; case USB_CDC_NOTIFY_SPEED_CHANGE: - dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)", + dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n", urb->actual_length); goto exit; default: @@ -274,8 +314,7 @@ static void wdm_int_callback(struct urb *urb) && !test_bit(WDM_DISCONNECTING, &desc->flags) && !test_bit(WDM_SUSPENDING, &desc->flags)) { rv = usb_submit_urb(desc->response, GFP_ATOMIC); - dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", - __func__, rv); + dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv); } spin_unlock(&desc->iuspin); if (rv < 0) { @@ -417,7 +456,7 @@ static ssize_t wdm_write rv = usb_translate_errors(rv); goto out_free_mem_pm; } else { - dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", + dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n", le16_to_cpu(req->wIndex)); } @@ -436,17 +475,14 @@ out_free_mem: } /* - * clear WDM_READ flag and possibly submit the read urb if resp_count - * is non-zero. + * Submit the read urb if resp_count is non-zero. * * Called with desc->iuspin locked */ -static int clear_wdm_read_flag(struct wdm_device *desc) +static int service_outstanding_interrupt(struct wdm_device *desc) { int rv = 0; - clear_bit(WDM_READ, &desc->flags); - /* submit read urb only if the device is waiting for it */ if (!desc->resp_count || !--desc->resp_count) goto out; @@ -537,8 +573,9 @@ retry: } if (!desc->reslength) { /* zero length read */ - dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); - rv = clear_wdm_read_flag(desc); + dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n"); + clear_bit(WDM_READ, &desc->flags); + rv = service_outstanding_interrupt(desc); spin_unlock_irq(&desc->iuspin); if (rv < 0) goto err; @@ -563,8 +600,10 @@ retry: desc->length -= cntr; /* in case we had outstanding data */ - if (!desc->length) - clear_wdm_read_flag(desc); + if (!desc->length) { + clear_bit(WDM_READ, &desc->flags); + service_outstanding_interrupt(desc); + } spin_unlock_irq(&desc->iuspin); rv = cntr; @@ -647,6 +686,17 @@ static int wdm_open(struct inode *inode, struct file *file) dev_err(&desc->intf->dev, "Error submitting int urb - %d\n", rv); rv = usb_translate_errors(rv); + } else if (test_bit(WDM_DRAIN_ON_OPEN, &desc->flags)) { + /* + * Some devices keep pending messages queued + * without resending notifications. We must + * flush the message queue before we can + * assume a one-to-one relationship between + * notifications and messages in the queue + */ + dev_dbg(&desc->intf->dev, "draining queued data\n"); + set_bit(WDM_RESPONDING, &desc->flags); + rv = usb_submit_urb(desc->response, GFP_KERNEL); } } else { rv = 0; @@ -673,7 +723,7 @@ static int wdm_release(struct inode *inode, struct file *file) if (!desc->count) { if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { - dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); + dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n"); kill_urbs(desc); spin_lock_irq(&desc->iuspin); desc->resp_count = 0; @@ -753,7 +803,8 @@ static void wdm_rxwork(struct work_struct *work) /* --- hotplug --- */ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, - u16 bufsize, int (*manage_power)(struct usb_interface *, int)) + u16 bufsize, int (*manage_power)(struct usb_interface *, int), + bool drain_on_open) { int rv = -ENOMEM; struct wdm_device *desc; @@ -840,6 +891,68 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor desc->manage_power = manage_power; + /* + * "drain_on_open" enables a hack to work around a firmware + * issue observed on network functions, in particular MBIM + * functions. + * + * Quoting section 7 of the CDC-WMC r1.1 specification: + * + * "The firmware shall interpret GetEncapsulatedResponse as a + * request to read response bytes. The firmware shall send + * the next wLength bytes from the response. The firmware + * shall allow the host to retrieve data using any number of + * GetEncapsulatedResponse requests. The firmware shall + * return a zero- length reply if there are no data bytes + * available. + * + * The firmware shall send ResponseAvailable notifications + * periodically, using any appropriate algorithm, to inform + * the host that there is data available in the reply + * buffer. The firmware is allowed to send ResponseAvailable + * notifications even if there is no data available, but + * this will obviously reduce overall performance." + * + * These requirements, although they make equally sense, are + * often not implemented by network functions. Some firmwares + * will queue data indefinitely, without ever resending a + * notification. The result is that the driver and firmware + * loses "syncronization" if the driver ever fails to respond + * to a single notification, something which easily can happen + * on release(). When this happens, the driver will appear to + * never receive notifications for the most current data. Each + * notification will only cause a single read, which returns + * the oldest data in the firmware's queue. + * + * The "drain_on_open" hack resolves the situation by draining + * data from the firmware until none is returned, without a + * prior notification. + * + * This will inevitably race with the firmware, risking that + * we read data from the device before handling the associated + * notification. To make things worse, some of the devices + * needing the hack do not implement the "return zero if no + * data is available" requirement either. Instead they return + * an error on the subsequent read in this case. This means + * that "winning" the race can cause an unexpected EIO to + * userspace. + * + * "winning" the race is more likely on resume() than on + * open(), and the unexpected error is more harmful in the + * middle of an open session. The hack is therefore only + * applied on open(), and not on resume() where it logically + * would be equally necessary. So we define open() as the only + * driver <-> device "syncronization point". Should we happen + * to lose a notification after open(), then syncronization + * will be lost until release() + * + * The hack should not be enabled for CDC WDM devices + * conforming to the CDC-WMC r1.1 specification. This is + * ensured by setting drain_on_open to false in wdm_probe(). + */ + if (drain_on_open) + set_bit(WDM_DRAIN_ON_OPEN, &desc->flags); + spin_lock(&wdm_device_list_lock); list_add(&desc->device_list, &wdm_device_list); spin_unlock(&wdm_device_list_lock); @@ -875,45 +988,25 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) int rv = -EINVAL; struct usb_host_interface *iface; struct usb_endpoint_descriptor *ep; - struct usb_cdc_dmm_desc *dmhd; + struct usb_cdc_parsed_header hdr; u8 *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; u16 maxcom = WDM_DEFAULT_BUFSIZE; if (!buffer) goto err; - while (buflen > 2) { - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - switch (buffer[2]) { - case USB_CDC_HEADER_TYPE: - break; - case USB_CDC_DMM_TYPE: - dmhd = (struct usb_cdc_dmm_desc *)buffer; - maxcom = le16_to_cpu(dmhd->wMaxCommand); - dev_dbg(&intf->dev, - "Finding maximum buffer length: %d", maxcom); - break; - default: - dev_err(&intf->dev, - "Ignoring extra header, type %d, length %d\n", - buffer[2], buffer[0]); - break; - } -next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; - } + cdc_parse_cdc_header(&hdr, intf, buffer, buflen); + + if (hdr.usb_cdc_dmm_desc) + maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand); iface = intf->cur_altsetting; if (iface->desc.bNumEndpoints != 1) goto err; ep = &iface->endpoint[0].desc; - rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); + rv = wdm_create(intf, ep, maxcom, &wdm_manage_power, false); err: return rv; @@ -945,7 +1038,7 @@ struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, { int rv = -EINVAL; - rv = wdm_create(intf, ep, bufsize, manage_power); + rv = wdm_create(intf, ep, bufsize, manage_power, true); if (rv < 0) goto err; @@ -987,7 +1080,7 @@ static void wdm_disconnect(struct usb_interface *intf) if (!desc->count) cleanup(desc); else - dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count); + dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count); mutex_unlock(&wdm_mutex); } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 917a55c4480d..a6c1fae7d52a 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -141,6 +141,7 @@ static void usbtmc_delete(struct kref *kref) struct usbtmc_device_data *data = to_usbtmc_data(kref); usb_put_dev(data->usb_dev); + kfree(data); } static int usbtmc_open(struct inode *inode, struct file *filp) @@ -1379,7 +1380,7 @@ static int usbtmc_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "%s called\n", __func__); - data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); + data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1467,10 +1468,8 @@ static int usbtmc_probe(struct usb_interface *intf, if (data->iin_ep_present) { /* allocate int urb */ data->iin_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!data->iin_urb) { - dev_err(&intf->dev, "Failed to allocate int urb\n"); + if (!data->iin_urb) goto error_register; - } /* will reference data in int urb */ kref_get(&data->kref); @@ -1478,10 +1477,8 @@ static int usbtmc_probe(struct usb_interface *intf, /* allocate buffer for interrupt in */ data->iin_buffer = kmalloc(data->iin_wMaxPacketSize, GFP_KERNEL); - if (!data->iin_buffer) { - dev_err(&intf->dev, "Failed to allocate int buf\n"); + if (!data->iin_buffer) goto error_register; - } /* fill interrupt urb */ usb_fill_int_urb(data->iin_urb, data->usb_dev, |