diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-20 08:25:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-20 08:25:49 -0700 |
commit | 52a23685f37c06d0cd00eeb8f517a90de3f2c338 (patch) | |
tree | 1449bf12db013962c98b2b8cd9b4b5d1cf359b01 /drivers/usb | |
parent | d046943cbaf332f75284ad99f4b3e60bae7ffff2 (diff) | |
parent | d20da3c39b9d5b04f0258ba74643533268f56e30 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (44 commits)
USB: drivers/usb/storage/dpcm.c whitespace cleanup
USB: r8a66597-hcd: fixes some problem
USB: change name of spinlock in hcd.c
USB: move routines in hcd.c
USB: misc: uss720: clean up urb->status usage
USB: misc: usbtest: clean up urb->status usage
USB: misc: usblcd: clean up urb->status usage
USB: misc: phidgetmotorcontrol: clean up urb->status usage
USB: misc: phidgetkit: clean up urb->status usage
USB: misc: legousbtower: clean up urb->status usage
USB: misc: ldusb: clean up urb->status usage
USB: misc: iowarrior: clean up urb->status usage
USB: misc: ftdi-elan: clean up urb->status usage
USB: misc: auerswald: clean up urb->status usage
USB: misc: appledisplay: clean up urb->status usage
USB: misc: adtux: clean up urb->status usage
USB: core: message: clean up urb->status usage
USB: image: microtek: clean up urb->status usage
USB: image: mdc800: clean up urb->status usage
USB: storage: onetouch: clean up urb->status usage
...
Diffstat (limited to 'drivers/usb')
46 files changed, 5344 insertions, 1079 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 1bc884051e0..02c52f8d5db 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -456,7 +456,7 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, int* actual_length) { struct timer_list timer; - int status; + int status = urb->status; init_timer(&timer); timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT); @@ -464,7 +464,6 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, timer.function = cxacru_timeout_kill; add_timer(&timer); wait_for_completion(done); - status = urb->status; del_timer_sync(&timer); if (actual_length) diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 638b8009b3b..eb0615abff6 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -612,7 +612,8 @@ static void speedtch_handle_int(struct urb *int_urb) struct speedtch_instance_data *instance = int_urb->context; struct usbatm_data *usbatm = instance->usbatm; unsigned int count = int_urb->actual_length; - int ret = int_urb->status; + int status = int_urb->status; + int ret; /* The magic interrupt for "up state" */ static const unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; @@ -621,8 +622,8 @@ static void speedtch_handle_int(struct urb *int_urb) atm_dbg(usbatm, "%s entered\n", __func__); - if (ret < 0) { - atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret); + if (status < 0) { + atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, status); goto fail; } diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 8f046659b4e..a1a1c9d467e 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -1308,11 +1308,13 @@ static void uea_intr(struct urb *urb) { struct uea_softc *sc = urb->context; struct intr_pkt *intr = urb->transfer_buffer; + int status = urb->status; + uea_enters(INS_TO_USBDEV(sc)); - if (unlikely(urb->status < 0)) { + if (unlikely(status < 0)) { uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", - urb->status); + status); return; } diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 11e9b15ca45..e717f5b1cae 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -257,9 +257,10 @@ static void usbatm_complete(struct urb *urb) { struct usbatm_channel *channel = urb->context; unsigned long flags; + int status = urb->status; vdbg("%s: urb 0x%p, status %d, actual_length %d", - __func__, urb, urb->status, urb->actual_length); + __func__, urb, status, urb->actual_length); /* usually in_interrupt(), but not always */ spin_lock_irqsave(&channel->lock, flags); @@ -269,16 +270,16 @@ static void usbatm_complete(struct urb *urb) spin_unlock_irqrestore(&channel->lock, flags); - if (unlikely(urb->status) && + if (unlikely(status) && (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) || - urb->status != -EILSEQ )) + status != -EILSEQ )) { - if (urb->status == -ESHUTDOWN) + if (status == -ESHUTDOWN) return; if (printk_ratelimit()) atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n", - __func__, urb, urb->status); + __func__, urb, status); /* throttle processing in case of an error */ mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS)); } else diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index cd51520c7e7..fe940e0536e 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -257,9 +257,10 @@ static void acm_ctrl_irq(struct urb *urb) struct usb_cdc_notification *dr = urb->transfer_buffer; unsigned char *data; int newctrl; - int status; + int retval; + int status = urb->status; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -267,10 +268,10 @@ static void acm_ctrl_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", __FUNCTION__, status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", __FUNCTION__, status); goto exit; } @@ -311,10 +312,10 @@ static void acm_ctrl_irq(struct urb *urb) break; } exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, status); + __FUNCTION__, retval); } /* data interface returns incoming bytes, or we got unthrottled */ @@ -324,7 +325,8 @@ static void acm_read_bulk(struct urb *urb) struct acm_ru *rcv = urb->context; struct acm *acm = rcv->instance; int status = urb->status; - dbg("Entering acm_read_bulk with status %d", urb->status); + + dbg("Entering acm_read_bulk with status %d", status); if (!ACM_READY(acm)) return; diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 9a1478972bf..5192cd9356d 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -289,16 +289,17 @@ static int proto_bias = -1; static void usblp_bulk_read(struct urb *urb) { struct usblp *usblp = urb->context; + int status = urb->status; if (usblp->present && usblp->used) { - if (urb->status) + if (status) printk(KERN_WARNING "usblp%d: " "nonzero read bulk status received: %d\n", - usblp->minor, urb->status); + usblp->minor, status); } spin_lock(&usblp->lock); - if (urb->status < 0) - usblp->rstatus = urb->status; + if (status < 0) + usblp->rstatus = status; else usblp->rstatus = urb->actual_length; usblp->rcomplete = 1; @@ -311,16 +312,17 @@ static void usblp_bulk_read(struct urb *urb) static void usblp_bulk_write(struct urb *urb) { struct usblp *usblp = urb->context; + int status = urb->status; if (usblp->present && usblp->used) { - if (urb->status) + if (status) printk(KERN_WARNING "usblp%d: " "nonzero write bulk status received: %d\n", - usblp->minor, urb->status); + usblp->minor, status); } spin_lock(&usblp->lock); - if (urb->status < 0) - usblp->wstatus = urb->status; + if (status < 0) + usblp->wstatus = status; else usblp->wstatus = urb->actual_length; usblp->wcomplete = 1; @@ -741,10 +743,11 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t */ rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK)); if (rv < 0) { - /* - * If interrupted, we simply leave the URB to dangle, - * so the ->release will call usb_kill_urb(). - */ + if (rv == -EAGAIN) { + /* Presume that it's going to complete well. */ + writecount += transfer_length; + } + /* Leave URB dangling, to be cleaned on close. */ goto collect_error; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 963520fbef9..42ef1d5f6c8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -99,12 +99,17 @@ EXPORT_SYMBOL_GPL (usb_bus_list_lock); /* used for controlling access to virtual root hubs */ static DEFINE_SPINLOCK(hcd_root_hub_lock); -/* used when updating hcd data */ -static DEFINE_SPINLOCK(hcd_data_lock); +/* used when updating an endpoint's URB list */ +static DEFINE_SPINLOCK(hcd_urb_list_lock); /* wait queue for synchronous unlinks */ DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); +static inline int is_root_hub(struct usb_device *udev) +{ + return (udev->parent == NULL); +} + /*-------------------------------------------------------------------------*/ /* @@ -906,14 +911,13 @@ EXPORT_SYMBOL (usb_calc_bus_time); static void urb_unlink(struct usb_hcd *hcd, struct urb *urb) { unsigned long flags; - int at_root_hub = (urb->dev == hcd->self.root_hub); /* clear all state linking urb to this dev (and hcd) */ - spin_lock_irqsave (&hcd_data_lock, flags); + spin_lock_irqsave(&hcd_urb_list_lock, flags); list_del_init (&urb->urb_list); - spin_unlock_irqrestore (&hcd_data_lock, flags); + spin_unlock_irqrestore(&hcd_urb_list_lock, flags); - if (hcd->self.uses_dma && !at_root_hub) { + if (hcd->self.uses_dma && !is_root_hub(urb->dev)) { if (usb_pipecontrol (urb->pipe) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) dma_unmap_single (hcd->self.controller, urb->setup_dma, @@ -955,7 +959,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) // FIXME: verify that quiescing hc works right (RH cleans up) - spin_lock_irqsave (&hcd_data_lock, flags); + spin_lock_irqsave(&hcd_urb_list_lock, flags); ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out) [usb_pipeendpoint(urb->pipe)]; if (unlikely (!ep)) @@ -972,7 +976,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) status = -ESHUTDOWN; break; } - spin_unlock_irqrestore (&hcd_data_lock, flags); + spin_unlock_irqrestore(&hcd_urb_list_lock, flags); if (status) { INIT_LIST_HEAD (&urb->urb_list); usbmon_urb_submit_error(&hcd->self, urb, status); @@ -986,7 +990,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) urb = usb_get_urb (urb); atomic_inc (&urb->use_count); - if (urb->dev == hcd->self.root_hub) { + if (is_root_hub(urb->dev)) { /* NOTE: requirement on hub callers (usbfs and the hub * driver, for now) that URBs' urb->transfer_buffer be * valid and usb_buffer_{sync,unmap}() not be needed, since @@ -1033,18 +1037,6 @@ done: /*-------------------------------------------------------------------------*/ -/* called in any context */ -int usb_hcd_get_frame_number (struct usb_device *udev) -{ - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - if (!HC_IS_RUNNING (hcd->state)) - return -ESHUTDOWN; - return hcd->driver->get_frame_number (hcd); -} - -/*-------------------------------------------------------------------------*/ - /* this makes the hcd giveback() the urb more quickly, by kicking it * off hardware queues (which may take a while) and returning it as * soon as practical. we've already set up the urb's return status, @@ -1055,7 +1047,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) { int value; - if (urb->dev == hcd->self.root_hub) + if (is_root_hub(urb->dev)) value = usb_rh_urb_dequeue (hcd, urb); else { @@ -1103,11 +1095,11 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) * that it was submitted. But as a rule it can't know whether or * not it's already been unlinked ... so we respect the reversed * lock sequence needed for the usb_hcd_giveback_urb() code paths - * (urb lock, then hcd_data_lock) in case some other CPU is now + * (urb lock, then hcd_urb_list_lock) in case some other CPU is now * unlinking it. */ spin_lock_irqsave (&urb->lock, flags); - spin_lock (&hcd_data_lock); + spin_lock(&hcd_urb_list_lock); sys = &urb->dev->dev; hcd = bus_to_hcd(urb->dev->bus); @@ -1139,17 +1131,16 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) * finish unlinking the initial failed usb_set_address() * or device descriptor fetch. */ - if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) - && hcd->self.root_hub != urb->dev) { + if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) && + !is_root_hub(urb->dev)) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " - "Controller is probably using the wrong IRQ." - "\n"); + "Controller is probably using the wrong IRQ.\n"); set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); } urb->status = status; - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); spin_unlock_irqrestore (&urb->lock, flags); retval = unlink1 (hcd, urb); @@ -1158,7 +1149,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) return retval; done: - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); spin_unlock_irqrestore (&urb->lock, flags); if (retval != -EIDRM && sys && sys->driver) dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval); @@ -1167,6 +1158,35 @@ done: /*-------------------------------------------------------------------------*/ +/** + * usb_hcd_giveback_urb - return URB from HCD to device driver + * @hcd: host controller returning the URB + * @urb: urb being returned to the USB device driver. + * Context: in_interrupt() + * + * This hands the URB from HCD to its USB device driver, using its + * completion function. The HCD has freed all per-urb resources + * (and is done using urb->hcpriv). It also released all HCD locks; + * the device driver won't cause problems if it frees, modifies, + * or resubmits this URB. + */ +void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) +{ + urb_unlink(hcd, urb); + usbmon_urb_complete (&hcd->self, urb); + usb_unanchor_urb(urb); + + /* pass ownership to the completion handler */ + urb->complete (urb); + atomic_dec (&urb->use_count); + if (unlikely (urb->reject)) + wake_up (&usb_kill_urb_queue); + usb_put_urb (urb); +} +EXPORT_SYMBOL (usb_hcd_giveback_urb); + +/*-------------------------------------------------------------------------*/ + /* disables the endpoint: cancels any pending urbs, then synchronizes with * the hcd to make sure all endpoint state is gone from hardware, and then * waits until the endpoint's queue is completely drained. use for @@ -1186,7 +1206,7 @@ void usb_hcd_endpoint_disable (struct usb_device *udev, /* ep is already gone from udev->ep_{in,out}[]; no more submits */ rescan: - spin_lock (&hcd_data_lock); + spin_lock(&hcd_urb_list_lock); list_for_each_entry (urb, &ep->urb_list, urb_list) { int tmp; @@ -1194,7 +1214,7 @@ rescan: if (urb->status != -EINPROGRESS) continue; usb_get_urb (urb); - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); spin_lock (&urb->lock); tmp = urb->status; @@ -1223,7 +1243,7 @@ rescan: /* list contents may have changed */ goto rescan; } - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); local_irq_enable (); /* synchronize with the hardware, so old configuration state @@ -1240,7 +1260,7 @@ rescan: * endpoint_disable methods. */ while (!list_empty (&ep->urb_list)) { - spin_lock_irq (&hcd_data_lock); + spin_lock_irq(&hcd_urb_list_lock); /* The list may have changed while we acquired the spinlock */ urb = NULL; @@ -1249,7 +1269,7 @@ rescan: urb_list); usb_get_urb (urb); } - spin_unlock_irq (&hcd_data_lock); + spin_unlock_irq(&hcd_urb_list_lock); if (urb) { usb_kill_urb (urb); @@ -1260,6 +1280,18 @@ rescan: /*-------------------------------------------------------------------------*/ +/* called in any context */ +int usb_hcd_get_frame_number (struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HC_IS_RUNNING (hcd->state)) + return -ESHUTDOWN; + return hcd->driver->get_frame_number (hcd); +} + +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_PM int hcd_bus_suspend(struct usb_device *rhdev) @@ -1395,35 +1427,6 @@ EXPORT_SYMBOL (usb_bus_start_enum); /*-------------------------------------------------------------------------*/ /** - * usb_hcd_giveback_urb - return URB from HCD to device driver - * @hcd: host controller returning the URB - * @urb: urb being returned to the USB device driver. - * Context: in_interrupt() - * - * This hands the URB from HCD to its USB device driver, using its - * completion function. The HCD has freed all per-urb resources - * (and is done using urb->hcpriv). It also released all HCD locks; - * the device driver won't cause problems if it frees, modifies, - * or resubmits this URB. - */ -void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) -{ - urb_unlink(hcd, urb); - usbmon_urb_complete (&hcd->self, urb); - usb_unanchor_urb(urb); - - /* pass ownership to the completion handler */ - urb->complete (urb); - atomic_dec (&urb->use_count); - if (unlikely (urb->reject)) - wake_up (&usb_kill_urb_queue); - usb_put_urb (urb); -} -EXPORT_SYMBOL (usb_hcd_giveback_urb); - -/*-------------------------------------------------------------------------*/ - -/** * usb_hcd_irq - hook IRQs to HCD framework (bus glue) * @irq: the IRQ being raised * @__hcd: pointer to the HCD whose IRQ is being signaled diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index fd74c50b180..e341a1da517 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1335,6 +1335,10 @@ int usb_new_device(struct usb_device *udev) udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); + /* Increment the parent's count of unsuspended children */ + if (udev->parent) + usb_autoresume_device(udev->parent); + /* Register the device. The device driver is responsible * for adding the device files to sysfs and for configuring * the device. @@ -1342,13 +1346,11 @@ int usb_new_device(struct usb_device *udev) err = device_add(&udev->dev); if (err) { dev_err(&udev->dev, "can't device_add, error %d\n", err); + if (udev->parent) + usb_autosuspend_device(udev->parent); goto fail; } - /* Increment the parent's count of unsuspended children */ - if (udev->parent) - usb_autoresume_device(udev->parent); - exit: return err; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 530e854961c..25f63f1096b 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -34,13 +34,14 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) { struct completion done; unsigned long expire; - int status; + int retval; + int status = urb->status; init_completion(&done); urb->context = &done; urb->actual_length = 0; - status = usb_submit_urb(urb, GFP_NOIO); - if (unlikely(status)) + retval = usb_submit_urb(urb, GFP_NOIO); + if (unlikely(retval)) goto out; expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; @@ -55,15 +56,15 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) urb->transfer_buffer_length); usb_kill_urb(urb); - status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status; + retval = status == -ENOENT ? -ETIMEDOUT : status; } else - status = urb->status; + retval = status; out: if (actual_length) *actual_length = urb->actual_length; usb_free_urb(urb); - return status; + return retval; } /*-------------------------------------------------------------------*/ @@ -250,6 +251,7 @@ static void sg_clean (struct usb_sg_request *io) static void sg_complete (struct urb *urb) { struct usb_sg_request *io = urb->context; + int status = urb->status; spin_lock (&io->lock); @@ -265,21 +267,21 @@ static void sg_complete (struct urb *urb) */ if (io->status && (io->status != -ECONNRESET - || urb->status != -ECONNRESET) + || status != -ECONNRESET) && urb->actual_length) { dev_err (io->dev->bus->controller, "dev %s ep%d%s scatterlist error %d/%d\n", io->dev->devpath, usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", - urb->status, io->status); + status, io->status); // BUG (); } - if (io->status == 0 && urb->status && urb->status != -ECONNRESET) { - int i, found, status; + if (io->status == 0 && status && status != -ECONNRESET) { + int i, found, retval; - io->status = urb->status; + io->status = status; /* the previous urbs, and this one, completed already. * unlink pending urbs so they won't rx/tx bad data. @@ -290,13 +292,13 @@ static void sg_complete (struct urb *urb) if (!io->urbs [i] || !io->urbs [i]->dev) continue; if (found) { - status = usb_unlink_urb (io->urbs [i]); - if (status != -EINPROGRESS - && status != -ENODEV - && status != -EBUSY) + retval = usb_unlink_urb (io->urbs [i]); + if (retval != -EINPROGRESS && + retval != -ENODEV && + retval != -EBUSY) dev_err (&io->dev->dev, "%s, unlink --> %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } else if (urb == io->urbs [i]) found = 1; } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index d47ae89154a..2ab222be8fd 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -441,6 +441,54 @@ static struct attribute_group dev_attr_grp = { .attrs = dev_attrs, }; +/* Binary descriptors */ + +static ssize_t +read_descriptors(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct usb_device *udev = to_usb_device( + container_of(kobj, struct device, kobj)); + size_t nleft = count; + size_t srclen, n; + + usb_lock_device(udev); + + /* The binary attribute begins with the device descriptor */ + srclen = sizeof(struct usb_device_descriptor); + if (off < srclen) { + n = min_t(size_t, nleft, srclen - off); + memcpy(buf, off + (char *) &udev->descriptor, n); + nleft -= n; + buf += n; + off = 0; + } else { + off -= srclen; + } + + /* Then follows the raw descriptor entry for the current + * configuration (con |