diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-10-18 20:50:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-10-18 20:50:22 -0700 |
| commit | 098fc560ef2bbd1bde80845c898fa95db616eb6c (patch) | |
| tree | ca722c6fdbdffe9b7cfd31d61e8f4aae906a319c /drivers/usb/core | |
| parent | bffe01870598b7a0a77073e25ee94e026bc98e6b (diff) | |
| parent | 2a136606fe21b603a0ce484fc578f862f8e8384d (diff) | |
Trivial Makefile merge
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/devices.c | 18 | ||||
| -rw-r--r-- | drivers/usb/core/devio.c | 57 | ||||
| -rw-r--r-- | drivers/usb/core/hcd-pci.c | 6 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.c | 33 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.h | 4 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 397 | ||||
| -rw-r--r-- | drivers/usb/core/inode.c | 121 | ||||
| -rw-r--r-- | drivers/usb/core/message.c | 50 | ||||
| -rw-r--r-- | drivers/usb/core/sysfs.c | 64 | ||||
| -rw-r--r-- | drivers/usb/core/urb.c | 5 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 200 | ||||
| -rw-r--r-- | drivers/usb/core/usb.h | 10 |
12 files changed, 569 insertions, 396 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 8e535789fce7..50009ed51e8d 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -149,7 +149,7 @@ static const struct class_info clas_info[] = /*****************************************************************/ -void usbdevfs_conn_disc_event(void) +void usbfs_conn_disc_event(void) { conndiscevcnt++; wake_up(&deviceconndiscwq); @@ -451,7 +451,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de * nbytes - the maximum number of bytes to write * skip_bytes - the number of bytes to skip before writing anything * file_offset - the offset into the devices file on completion - * The caller must own the usbdev->serialize semaphore. + * The caller must own the device lock. */ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) @@ -569,7 +569,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct list_head *buslist; struct usb_bus *bus; ssize_t ret, total_written = 0; loff_t skip_bytes = *ppos; @@ -581,18 +580,15 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; - /* enumerate busses */ down (&usb_bus_list_lock); - list_for_each(buslist, &usb_bus_list) { - /* print devices for this bus */ - bus = list_entry(buslist, struct usb_bus, bus_list); - + /* print devices for all busses */ + list_for_each_entry(bus, &usb_bus_list, bus_list) { /* recurse through all children of the root hub */ if (!bus->root_hub) continue; - down(&bus->root_hub->serialize); + usb_lock_device(bus->root_hub); ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); - up(&bus->root_hub->serialize); + usb_unlock_device(bus->root_hub); if (ret < 0) { up(&usb_bus_list_lock); return ret; @@ -682,7 +678,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) return ret; } -struct file_operations usbdevfs_devices_fops = { +struct file_operations usbfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 776c1bf0df9b..0e0ea0806606 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -21,7 +21,7 @@ * * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ * - * This file implements the usbdevfs/x/y files, where + * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * * It allows user space programs/"drivers" to communicate directly @@ -113,7 +113,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l int i; pos = *ppos; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { ret = -ENODEV; goto err; @@ -175,7 +175,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l } err: - up(&dev->serialize); + usb_unlock_device(dev); return ret; } @@ -286,9 +286,10 @@ static void destroy_async (struct dev_state *ps, struct list_head *list) while (!list_empty(list)) { as = list_entry(list->next, struct async, asynclist); list_del_init(&as->asynclist); + + /* drop the spinlock so the completion handler can run */ spin_unlock_irqrestore(&ps->lock, flags); - /* usb_unlink_urb calls the completion handler with status == -ENOENT */ - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); @@ -353,7 +354,7 @@ static void driver_disconnect(struct usb_interface *intf) destroy_async_on_interface(ps, ifnum); } -struct usb_driver usbdevfs_driver = { +struct usb_driver usbfs_driver = { .owner = THIS_MODULE, .name = "usbfs", .probe = driver_probe, @@ -378,7 +379,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else - err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps); + err = usb_driver_claim_interface(&usbfs_driver, intf, ps); up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); @@ -401,7 +402,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { - usb_driver_release_interface(&usbdevfs_driver, intf); + usb_driver_release_interface(&usbfs_driver, intf); err = 0; } up_write(&usb_bus_type.subsys.rwsem); @@ -516,7 +517,7 @@ static int usbdev_release(struct inode *inode, struct file *file) struct usb_device *dev = ps->dev; unsigned int ifnum; - down(&dev->serialize); + usb_lock_device(dev); list_del_init(&ps->list); if (connected(dev)) { @@ -525,7 +526,7 @@ static int usbdev_release(struct inode *inode, struct file *file) releaseintf(ps, ifnum); destroy_all_async(ps); } - up(&dev->serialize); + usb_unlock_device(dev); usb_put_dev(dev); ps->dev = NULL; kfree(ps); @@ -557,10 +558,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); - up(&dev->serialize); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - down(&dev->serialize); + usb_lock_device(dev); if ((i > 0) && ctrl.wLength) { if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); @@ -588,10 +589,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) printk ("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - down(&dev->serialize); + usb_lock_device(dev); } free_page((unsigned long)tbuf); if (i<0) { @@ -635,9 +636,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - down(&dev->serialize); + usb_lock_device(dev); if (!i && len2) { if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); @@ -651,9 +652,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - down(&dev->serialize); + usb_lock_device(dev); } kfree(tbuf); if (i < 0) { @@ -734,7 +735,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return __usb_reset_device(ps->dev); + return usb_reset_device(ps->dev); } @@ -976,7 +977,7 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg) as = async_getpending(ps, arg); if (!as) return -EINVAL; - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); return 0; } @@ -1024,9 +1025,9 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) break; if (signal_pending(current)) break; - up(&dev->serialize); + usb_unlock_device(dev); schedule(); - down(&dev->serialize); + usb_lock_device(dev); } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); @@ -1149,7 +1150,11 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: + usb_unlock_device(ps->dev); + usb_lock_all_devices(); bus_rescan_devices(intf->dev.bus); + usb_unlock_all_devices(); + usb_lock_device(ps->dev); break; /* talk directly to the interface's driver */ @@ -1192,9 +1197,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!(file->f_mode & FMODE_WRITE)) return -EPERM; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { - up(&dev->serialize); + usb_unlock_device(dev); return -ENODEV; } @@ -1294,7 +1299,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ret = proc_ioctl(ps, p); break; } - up(&dev->serialize); + usb_unlock_device(dev); if (ret >= 0) inode->i_atime = CURRENT_TIME; return ret; @@ -1314,7 +1319,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai return mask; } -struct file_operations usbdevfs_device_file_operations = { +struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 5fdaae079cf6..837bb267f194 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -188,9 +188,9 @@ clean_3: } hcd->irq = dev->irq; - dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp, + dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp, (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", - base); + resource); usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; @@ -260,6 +260,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev) } usb_deregister_bus (&hcd->self); + + pci_disable_device(dev); } EXPORT_SYMBOL (usb_hcd_pci_remove); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d970e2c0c066..8ab1163e59b9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -798,9 +798,9 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev return (retval < 0) ? retval : -EMSGSIZE; } - down (&usb_dev->serialize); + usb_lock_device (usb_dev); retval = usb_new_device (usb_dev); - up (&usb_dev->serialize); + usb_unlock_device (usb_dev); if (retval) { usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", @@ -1264,7 +1264,7 @@ static int hcd_unlink_urb (struct urb *urb, int status) * never get completion IRQs ... maybe even the ones we need to * finish unlinking the initial failed usb_set_address(). */ - if (!hcd->saw_irq) { + if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " "Different ACPI or APIC settings may help." "\n"); @@ -1573,13 +1573,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) struct usb_hcd *hcd = __hcd; int start = hcd->state; - if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ + if (start == USB_STATE_HALT) return IRQ_NONE; - - hcd->saw_irq = 1; if (hcd->driver->irq (hcd, r) == IRQ_NONE) return IRQ_NONE; + hcd->saw_irq = 1; if (hcd->state != start && hcd->state == USB_STATE_HALT) usb_hc_died (hcd); return IRQ_HANDLED; @@ -1588,22 +1587,6 @@ EXPORT_SYMBOL (usb_hcd_irq); /*-------------------------------------------------------------------------*/ -static void hcd_panic (void *_hcd) -{ - struct usb_hcd *hcd = _hcd; - struct usb_device *hub = hcd->self.root_hub; - unsigned i; - - /* hc's root hub is removed later removed in hcd->stop() */ - down (&hub->serialize); - usb_set_device_state(hub, USB_STATE_NOTATTACHED); - for (i = 0; i < hub->maxchild; i++) { - if (hub->children [i]) - usb_disconnect (&hub->children [i]); - } - up (&hub->serialize); -} - /** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller @@ -1616,9 +1599,9 @@ void usb_hc_died (struct usb_hcd *hcd) { dev_err (hcd->self.controller, "HC died; cleaning up\n"); - /* clean up old urbs and devices; needs a task context */ - INIT_WORK (&hcd->work, hcd_panic, hcd); - (void) schedule_work (&hcd->work); + /* make khubd clean up old urbs and devices */ + usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED); + mod_timer(&hcd->rh_timer, jiffies); } EXPORT_SYMBOL (usb_hc_died); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 340977b72925..eb8ae109096e 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ struct timer_list rh_timer; /* drives root hub */ struct list_head dev_list; /* devices on this bus */ - struct work_struct work; /* * hardware info/state @@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev, return usb_register_root_hub (usb_dev, hcd->self.controller); } +extern void usb_set_device_state(struct usb_device *udev, + enum usb_device_state new_state); + /*-------------------------------------------------------------------------*/ /* exported only within usbcore */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7f72ee96c7fe..10baa8f52566 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -36,15 +36,18 @@ #include "hcd.h" #include "hub.h" -/* Protect struct usb_device state and children members */ +/* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->serialize, except that ->state can + * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED; -/* Wakes up khubd */ +/* khubd's worklist and its lock */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; - static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ +/* Wakes up khubd */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); + static pid_t khubd_pid = 0; /* PID of khubd */ static DECLARE_COMPLETION(khubd_exited); @@ -226,6 +229,19 @@ static int get_port_status(struct usb_device *hdev, int port, data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT); } +static void kick_khubd(struct usb_hub *hub) +{ + unsigned long flags; + + spin_lock_irqsave(&hub_event_lock, flags); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); +} + + /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb, struct pt_regs *regs) { @@ -261,12 +277,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs) hub->nerrors = 0; /* Something happened, let khubd figure it out */ - spin_lock(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock(&hub_event_lock); + kick_khubd(hub); resubmit: if (hub->quiescing) @@ -384,6 +395,33 @@ static void hub_power_on(struct usb_hub *hub) msleep(hub->descriptor->bPwrOn2PwrGood * 2); } +static void hub_quiesce(struct usb_hub *hub) +{ + /* stop khubd and related activity */ + hub->quiescing = 1; + usb_kill_urb(hub->urb); + if (hub->has_indicators) + cancel_delayed_work(&hub->leds); + if (hub->has_indicators || hub->tt.hub) + flush_scheduled_work(); +} + +static void hub_activate(struct usb_hub *hub) +{ + int status; + + hub->quiescing = 0; + status = usb_submit_urb(hub->urb, GFP_NOIO); + if (status < 0) + dev_err(&hub->intf->dev, "activate --> %d\n", status); + if (hub->has_indicators && blinkenlights) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + + /* scan all ports ASAP */ + hub->event_bits[0] = ~0; + kick_khubd(hub); +} + static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { @@ -579,7 +617,7 @@ static int hub_configure(struct usb_hub *hub, dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); - /* Start the interrupt endpoint */ + /* set up the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); @@ -597,24 +635,13 @@ static int hub_configure(struct usb_hub *hub, hub, endpoint->bInterval); hub->urb->transfer_dma = hub->buffer_dma; hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - ret = usb_submit_urb(hub->urb, GFP_KERNEL); - if (ret) { - message = "couldn't submit status urb"; - goto fail; - } - - /* Wake up khubd */ - wake_up(&khubd_wait); - /* maybe start cycling the hub leds */ - if (hub->has_indicators && blinkenlights) { - set_port_led(hdev, 1, HUB_LED_GREEN); + /* maybe cycle the hub leds */ + if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); - } hub_power_on(hub); - + hub_activate(hub); return 0; fail: @@ -626,33 +653,6 @@ fail: static unsigned highspeed_hubs; -static void hub_quiesce(struct usb_hub *hub) -{ - /* stop khubd and related activity */ - hub->quiescing = 1; - usb_kill_urb(hub->urb); - if (hub->has_indicators) - cancel_delayed_work(&hub->leds); - if (hub->has_indicators || hub->tt.hub) - flush_scheduled_work(); -} - -#ifdef CONFIG_USB_SUSPEND - -static void hub_reactivate(struct usb_hub *hub) -{ - int status; - - hub->quiescing = 0; - status = usb_submit_urb(hub->urb, GFP_NOIO); - if (status < 0) - dev_err(&hub->intf->dev, "reactivate --> %d\n", status); - if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); -} - -#endif - static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -794,68 +794,29 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } } -/* caller has locked the hub and must own the device lock */ -static int hub_reset(struct usb_hub *hub) +/* caller has locked the hub device */ +static void hub_pre_reset(struct usb_device *hdev) { - struct usb_device *hdev = hub->hdev; + struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); int i; - /* Disconnect any attached devices */ - for (i = 0; i < hub->descriptor->bNbrPorts; i++) { + for (i = 0; i < hdev->maxchild; ++i) { if (hdev->children[i]) usb_disconnect(&hdev->children[i]); } - - /* Attempt to reset the hub */ - if (hub->urb) - usb_kill_urb(hub->urb); - else - return -1; - - if (__usb_reset_device(hdev)) - return -1; - - hub->urb->dev = hdev; - if (usb_submit_urb(hub->urb, GFP_KERNEL)) - return -1; - - hub_power_on(hub); - - return 0; + hub_quiesce(hub); } -/* caller has locked the hub */ -/* FIXME! This routine should be subsumed into hub_reset */ -static void hub_start_disconnect(struct usb_device *hdev) +/* caller has locked the hub device */ +static void hub_post_reset(struct usb_device *hdev) { - struct usb_device *parent = hdev->parent; - int i; - - /* Find the device pointer to disconnect */ - if (parent) { - for (i = 0; i < parent->maxchild; i++) { - if (parent->children[i] == hdev) { - usb_disconnect(&parent->children[i]); - return; - } - } - } + struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); - dev_err(&hdev->dev, "cannot disconnect hub!\n"); + hub_activate(hub); + hub_power_on(hub); } -static void recursively_mark_NOTATTACHED(struct usb_device *udev) -{ - int i; - - for (i = 0; i < udev->maxchild; ++i) { - if (udev->children[i]) - recursively_mark_NOTATTACHED(udev->children[i]); - } - udev->state = USB_STATE_NOTATTACHED; -} - /* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent * tree operations like suspend, resume, reset, and disconnect, which @@ -872,21 +833,16 @@ static int locktree(struct usb_device *udev) /* root hub is always the first lock in the series */ hdev = udev->parent; if (!hdev) { - down(&udev->serialize); + usb_lock_device(udev); return 0; } /* on the path from root to us, lock everything from * top down, dropping parent locks when not needed - * - * NOTE: if disconnect were to ignore the locking, we'd need - * to get extra refcounts to everything since hdev->children - * and udev->parent could be invalidated while we work... */ t = locktree(hdev); if (t < 0) return t; - spin_lock_irq(&device_state_lock); for (t = 0; t < hdev->maxchild; t++) { if (hdev->children[t] == udev) { /* everything is fail-fast once disconnect @@ -898,33 +854,45 @@ static int locktree(struct usb_device *udev) /* when everyone grabs locks top->bottom, * non-overlapping work may be concurrent */ - spin_unlock_irq(&device_state_lock); down(&udev->serialize); up(&hdev->serialize); return t; } } - spin_unlock_irq(&device_state_lock); - up(&hdev->serialize); + usb_unlock_device(hdev); return -ENODEV; } +static void recursively_mark_NOTATTACHED(struct usb_device *udev) +{ + int i; + + for (i = 0; i < udev->maxchild; ++i) { + if (udev->children[i]) + recursively_mark_NOTATTACHED(udev->children[i]); + } + udev->state = USB_STATE_NOTATTACHED; +} + /** - * usb_set_device_state - change a device's current state (usbcore-internal) + * usb_set_device_state - change a device's current state (usbcore, hcds) * @udev: pointer to device whose state should be changed * @new_state: new state value to be stored * - * udev->state is _not_ protected by the device lock. This + * udev->state is _not_ fully protected by the device lock. Although + * most transitions are made only while holding the lock, the state can + * can change to USB_STATE_NOTATTACHED at almost any time. This * is so that devices can be marked as disconnected as soon as possible, - * without having to wait for the semaphore to be released. Instead, - * changes to the state must be protected by the device_state_lock spinlock. + * without having to wait for any semaphores to be released. As a result, + * all changes to any device's state must be protected by the + * device_state_lock spinlock. * * Once a device has been added to the device tree, all changes to its state * should be made using this routine. The state should _not_ be set directly. * * If udev->state is already USB_STATE_NOTATTACHED then no change is made. * Otherwise udev->state is set to new_state, and if new_state is - * USB_STATE_NOTATTACHED then all of udev's descendant's states are also set + * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set * to USB_STATE_NOTATTACHED. */ void usb_set_device_state(struct usb_device *udev, @@ -941,6 +909,7 @@ void usb_set_device_state(struct usb_device *udev, recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); } +EXPORT_SYMBOL(usb_set_device_state); static void choose_address(struct usb_device *udev) @@ -974,11 +943,12 @@ static void release_address(struct usb_device *udev) /** * usb_disconnect - disconnect a device (usbcore-internal) - * @pdev: pointer to device being disconnected, into a locked hub + * @pdev: pointer to device being disconnected * Context: !in_interrupt () * - * Something got disconnected. Get rid of it, and all of its children. - * If *pdev is a normal device then the parent hub should be locked. + * Something got disconnected. Get rid of it and all of its children. + * + * If *pdev is a normal device then the parent hub must already be locked. * If *pdev is a root hub then this routine will acquire the * usb_bus_list_lock on behalf of the caller. * @@ -1004,9 +974,11 @@ void usb_disconnect(struct usb_device **pdev) usb_set_device_state(udev, USB_STATE_NOTATTACHED); /* lock the bus list on behalf of HCDs unregistering their root hubs */ - if (!udev->parent) + if (!udev->parent) { down(&usb_bus_list_lock); - down(&udev->serialize); + usb_lock_device(udev); + } else + down(&udev->serialize); dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); @@ -1031,14 +1003,16 @@ void usb_disconnect(struct usb_device **pdev) usbfs_remove_device(udev); usb_remove_sysfs_dev_files(udev); - /* Avoid races with recursively_mark_NOTATTACHED() and locktree() */ + /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); *pdev = NULL; spin_unlock_irq(&device_state_lock); - up(&udev->serialize); - if (!udev->parent) + if (!udev->parent) { + usb_unlock_device(udev); up(&usb_bus_list_lock); + } else + up(&udev->serialize); device_unregister(&udev->dev); } @@ -1061,11 +1035,19 @@ static int choose_configuration(struct usb_device *udev) ->altsetting->desc; if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) continue; - /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */ + /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS. + * MSFT needs this to be the first config; never use + * it as the default unless Linux has host-side RNDIS. + * A second config would ideally be CDC-Ethernet, but + * may instead be the "vendor specific" CDC subset + * long used by ARM Linux for sa1100 or pxa255. + */ if (desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) + && desc->bInterfaceProtocol == 0xff) { + c = udev->config[1].desc.bConfigurationValue; continue; + } c = udev->config[i].desc.bConfigurationValue; break; } @@ -1384,7 +1366,6 @@ static int hub_port_disable(struct usb_device *hdev, int port) int ret; if (hdev->children[port]) { - /* FIXME need disconnect() for NOTATTACHED device */ usb_set_device_state(hdev->children[port], USB_STATE_NOTATTACHED); } @@ -1396,6 +1377,33 @@ static int hub_port_disable(struct usb_device *hdev, int port) return ret; } +/* + * Disable a port and mark a logical connnect-change event, so that some + * time later khubd will disconnect() any existing usb_device on the port + * and will re-enumerate if there actually is a device attached. + */ +static void hub_port_logical_disconnect(struct usb_device *hdev, int port) +{ + struct usb_hub *hub; + + dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1); + hub_port_disable(hdev, port); + + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - usb_suspend_device(dev,PM_SUSPEND_DISK) + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + + hub = usb_get_intfdata(hdev->actconfig->interface[0]); + set_bit(port, hub->change_bits); + kick_khubd(hub); +} + + #ifdef CONFIG_USB_SUSPEND /* @@ -1413,8 +1421,8 @@ static int hub_port_suspend(struct usb_device *hdev, int port) int status; struct usb_device *udev; - udev = hdev->children[port - 1]; - // dev_dbg(hubdev(hdev), "suspend port %d\n", port); + udev = hdev->children[port]; + // dev_dbg(hubdev(hdev), "suspend port %d\n", port + 1); /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). @@ -1439,11 +1447,11 @@ static int hub_port_suspend(struct usb_device *hdev, int port) } /* see 7.1.7.6 */ - status = set_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hubdev(hdev), "can't suspend port %d, status %d\n", - port, status); + port + 1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1477,16 +1485,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) { int status; + /* caller owns the udev device lock */ if (port < 0) return port; - /* NOTE: udev->serialize released on all real returns! */ - if (state <= udev->dev.power.power_state || state < PM_SUSPEND_MEM || udev->state == USB_STATE_SUSPENDED || udev->state == USB_STATE_NOTATTACHED) { - up(&udev->serialize); return 0; } @@ -1562,11 +1568,10 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) else status = -EOPNOTSUPP; } else - status = hub_port_suspend(udev->parent, port + 1); + status = hub_port_suspend(udev->parent, port); if (status == 0) udev->dev.power.power_state = state; - up(&udev->serialize); return status; } @@ -1590,7 +1595,15 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) */ int usb_suspend_device(struct usb_device *udev, u32 state) { - return __usb_suspend_device(udev, locktree(udev), state); + int port, status; + + port = locktree(udev); + if (port < 0) + return port; + + status = __usb_suspend_device(udev, port, state); + usb_unlock_device(udev); + return status; } /* @@ -1603,7 +1616,7 @@ static int finish_port_resume(struct usb_device *udev) int status; u16 devstatus; - /* caller owns udev->serialize */ + /* caller owns the udev device lock */ dev_dbg(&udev->dev, "usb resume\n"); udev->dev.power.power_state = PM_SUSPEND_ON; @@ -1687,15 +1700,15 @@ hub_port_resume(struct usb_device *hdev, int port) int status; struct usb_device *udev; - udev = hdev->children[port - 1]; - // dev_dbg(hubdev(hdev), "resume port %d\n", port); + udev = hdev->children[port]; + // dev_dbg(hubdev(hdev), "resume port %d\n", port + 1); /* see 7.1.7.7; affects power usage, but not budgeting */ - status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + status = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(&hdev->actconfig->interface[0]->dev, "can't resume port %d, status %d\n", - port, status); + port + 1, status); } else { u16 devstatus; u16 portchange; @@ -1713,7 +1726,7 @@ hub_port_resume(struct usb_device *hdev, int port) * sequence. */ devstatus = portchange = 0; - status = hub_port_status(hdev, port - 1, + status = hub_port_status(hdev, port, &devstatus, &portchange); if (status < 0 || (devstatus & LIVE_FLAGS) != LIVE_FLAGS @@ -1721,7 +1734,7 @@ hub_port_resume(struct usb_device *hdev, int port) ) { dev_dbg(&hdev->actconfig->interface[0]->dev, "port %d status %04x.%04x after resume, %d\n", - port, portchange, devstatus, status); + port + 1, portchange, devstatus, status); } else { /* TRSMRCY = 10 msec */ msleep(10); @@ -1729,7 +1742,7 @@ hub_port_resume(struct usb_device *hdev, int port) } } if (status < 0) - status = hub_port_disable(hdev, port); + hub_port_logical_disconnect(hdev, port); return status; } @@ -1773,7 +1786,7 @@ int usb_resume_device(struct usb_device *udev) ->actconfig->interface[0]); } } else if (udev->state == USB_STATE_SUSPENDED) { - status = hub_port_resume(udev->parent, port + 1); + status = hub_port_resume(udev->parent, port); } else { status = 0; udev->dev.power.power_state = PM_SUSPEND_ON; @@ -1783,10 +1796,12 @@ int usb_resume_device(struct usb_device *udev) status); } - up(&udev->serialize); + usb_unlock_device(udev); /* rebind drivers that had no suspend() */ + usb_lock_all_devices(); bus_rescan_devices(&usb_bus_type); + usb_unlock_all_devices(); return status; } @@ -1828,6 +1843,7 @@ static int hub_suspend(struct usb_interface *intf, u32 state) continue; down(&udev->serialize); status = __usb_suspend_device(udev, port, state); + up(&udev->serialize); if (status < 0) dev_dbg(&intf->dev, "suspend port %d --> %d\n", port, status); @@ -1866,20 +1882,20 @@ static int hub_resume(struct usb_interface *intf) continue; down (&udev->serialize); if (portstat & USB_PORT_STAT_SUSPEND) - status = hub_port_resume(hdev, port + 1); + status = hub_port_resume(hdev, port); else { status = finish_port_resume(udev); - if (status < 0) - status = hub_port_disable(hdev, port); - if (status < 0) + if (status < 0) { dev_dbg(&intf->dev, "resume port %d --> %d\n", - port, status); + port + 1, status); + hub_port_logical_disconnect(hdev, port); + } } up(&udev->serialize); } intf->dev.power.power_state = PM_SUSPEND_ON; - hub_reactivate(hub); + hub_activate(hub); return 0; } @@ -2011,7 +2027,7 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port) hdev->bus->b_hnp_enable = 0; } - retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + retval = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (retval < 0 && retval != -EPIPE) dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); @@ -2443,10 +2459,10 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - if (hub_reset(hub)) { + ret = usb_reset_device(hdev); + if (ret) { dev_dbg (hub_dev, - "can't reset; disconnecting\n"); - hub_start_disconnect(hdev); + "error resetting hub: %d\n", ret); goto loop; } @@ -2502,15 +2518,17 @@ static void hub_events(void) if (portchange & USB_PORT_STAT_C_SUSPEND) { clear_port_feature(hdev, i + 1, USB_PORT_FEAT_C_SUSPEND); - if (hdev->children[i]) + if (hdev->children[i]) { ret = remote_wakeup(hdev->children[i]); - else + if (ret < 0) + connect_change = 1; + } else { ret = -ENODEV; + hub_port_disable(hdev, i); + } dev_dbg (hub_dev, "resume on port %d, status %d\n", i + 1, ret); - if (ret < 0) - ret = hub_port_disable(hdev, i); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { @@ -2554,7 +2572,7 @@ static void hub_events(void) } loop: - up(&hdev->serialize); + usb_unlock_device(hdev); usb_put_dev(hdev); } /* end while (1) */ @@ -2707,13 +2725,15 @@ static int config_descriptors_changed(struct usb_device *udev) * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. + * For calls that might not occur during probe(), drivers should lock + * the device using usb_lock_device_for_reset(). */ -int __usb_reset_device(struct usb_device *udev) +int usb_reset_device(struct usb_device *udev) { struct usb_device *parent = udev->parent; struct usb_device_descriptor descriptor = udev->descriptor; int i, ret, port = -1; - struct usb_hub *hub; + int udev_is_a_hub = 0; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) { @@ -2722,13 +2742,9 @@ int __usb_reset_device(struct usb_device *udev) return -EINVAL; } - /* FIXME: This should be legal for regular hubs. Root hubs may - * have special requirements. */ - if (udev->maxchild) { - /* this requires hub- or hcd-specific logic; - * see hub_reset() and OHCI hc_restart() - */ - dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__); + if (!parent) { + /* this requires hcd-specific logic; see OHCI hc_restart() */ + dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__); return -EISDIR; } @@ -2744,6 +2760,19 @@ int __usb_reset_device(struct usb_device *udev) return -ENOENT; } + /* If we're resetting an active hub, take some special actions */ + if (udev->actconfig && + udev->actconfig->interface[0]->dev.driver == + &hub_driver.driver) { + udev_is_a_hub = 1; + hub_pre_reset(udev); + } + + /* ep0 maxpacket size may change; let the HCD know about it. + * Other endpoints will be handled by re-enumeration. */ + usb_disable_endpoint(udev, 0); + usb_disable_endpoint(udev, 0 + USB_DIR_IN); + ret = hub_port_init(parent, udev, port); if (ret < 0) goto re_enumerate; @@ -2757,7 +2786,7 @@ int __usb_reset_device(struct usb_device *udev) } if (!udev->actconfig) - return 0; + goto done; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_CONFIGURATION, 0, @@ -2791,32 +2820,12 @@ int __usb_reset_device(struct usb_device *udev) } } +done: + if (udev_is_a_hub) + hub_post_reset(udev); return 0; re_enumerate: - hub_port_disable(parent, port); - - hub = usb_get_intfdata(parent->actconfig->interface[0]); - set_bit(port, hub->change_bits); - - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock_irq(&hub_event_lock); - + hub_port_logical_disconnect(parent, port); return -ENODEV; } -EXPORT_SYMBOL(__usb_reset_device); - -int usb_reset_device(struct usb_device *udev) -{ - int r; - - down(&udev->serialize); - r = __usb_reset_device(udev); - up(&udev->serialize); - - return r; -} diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index c9f57e334a4f..24452d66d576 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -4,7 +4,7 @@ * inode.c -- Inode/Dentry functions for the USB device file system. * * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,17 +40,15 @@ #include <linux/smp_lock.h> #include <linux/parser.h> #include <asm/byteorder.h> +#include "usb.h" static struct super_operations usbfs_ops; static struct file_operations default_file_operations; static struct inode_operations usbfs_dir_inode_operations; -static struct vfsmount *usbdevfs_mount; static struct vfsmount *usbfs_mount; -static int usbdevfs_mount_count; /* = 0 */ static int usbfs_mount_count; /* = 0 */ static int ignore_mount = 0; -static struct dentry *devices_usbdevfs_dentry; static struct dentry *devices_usbfs_dentry; static int num_buses; /* = 0 */ @@ -240,9 +238,6 @@ static int remount(struct super_block *sb, int *flags, char *data) if (usbfs_mount && usbfs_mount->mnt_sb) update_sb(usbfs_mount->mnt_sb); - if (usbdevfs_mount && usbdevfs_mount->mnt_sb) - update_sb(usbdevfs_mount->mnt_sb); - return 0; } @@ -561,28 +556,12 @@ static void fs_remove_file (struct dentry *dentry) /* --------------------------------------------------------------------- */ - - -/* - * The usbdevfs name is now deprecated (as of 2.5.1). - * It will be removed when the 2.7.x development cycle is started. - * You have been warned :) - */ -static struct file_system_type usbdevice_fs_type; - static struct super_block *usb_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, usbfs_fill_super); } -static struct file_system_type usbdevice_fs_type = { - .owner = THIS_MODULE, - .name = "usbdevfs", - .get_sb = usb_get_sb, - .kill_sb = kill_litter_super, -}; - static struct file_system_type usb_fs_type = { .owner = THIS_MODULE, .name = "usbfs", @@ -603,16 +582,10 @@ static int create_special_files (void) ignore_mount = 1; /* create the devices special file */ - retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count); - if (retval) { - err ("Unable to get usbdevfs mount"); - goto exit; - } - retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); if (retval) { err ("Unable to get usbfs mount"); - goto error_clean_usbdevfs_mount; + goto exit; } ignore_mount = 0; @@ -620,7 +593,7 @@ static int create_special_files (void) parent = usbfs_mount->mnt_sb->s_root; devices_usbfs_dentry = fs_create_file ("devices", listmode | S_IFREG, parent, - NULL, &usbdevfs_devices_fops, + NULL, &usbfs_devices_fops, listuid, listgid); if (devices_usbfs_dentry == NULL) { err ("Unable to create devices usbfs file"); @@ -628,42 +601,19 @@ static int create_special_files (void) goto error_clean_mounts; } - parent = usbdevfs_mount->mnt_sb->s_root; - devices_usbdevfs_dentry = fs_create_file ("devices", - listmode | S_IFREG, parent, - NULL, &usbdevfs_devices_fops, - listuid, listgid); - if (devices_usbdevfs_dentry == NULL) { - err ("Unable to create devices usbfs file"); - retval = -ENODEV; - goto error_remove_file; - } - goto exit; -error_remove_file: - fs_remove_file (devices_usbfs_dentry); - devices_usbfs_dentry = NULL; - error_clean_mounts: simple_release_fs(&usbfs_mount, &usbfs_mount_count); - -error_clean_usbdevfs_mount: - simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); - exit: return retval; } static void remove_special_files (void) { - if (devices_usbdevfs_dentry) - fs_remove_file (devices_usbdevfs_dentry); if (devices_usbfs_dentry) fs_remove_file (devices_usbfs_dentry); - devices_usbdevfs_dentry = NULL; devices_usbfs_dentry = NULL; - simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); simple_release_fs(&usbfs_mount, &usbfs_mount_count); } @@ -671,11 +621,6 @@ void usbfs_update_special (void) { struct inode *inode; - if (devices_usbdevfs_dentry) { - inode = devices_usbdevfs_dentry->d_inode; - if (inode) - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - } if (devices_usbfs_dentry) { inode = devices_usbfs_dentry->d_inode; if (inode) @@ -707,29 +652,16 @@ void usbfs_add_bus(struct usb_bus *bus) return; } - parent = usbdevfs_mount->mnt_sb->s_root; - bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, - bus, NULL, busuid, busgid); - if (bus->usbdevfs_dentry == NULL) { - err ("error creating usbdevfs bus entry"); - return; - } - usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } - void usbfs_remove_bus(struct usb_bus *bus) { if (bus->usbfs_dentry) { fs_remove_file (bus->usbfs_dentry); bus->usbfs_dentry = NULL; } - if (bus->usbdevfs_dentry) { - fs_remove_file (bus->usbdevfs_dentry); - bus->usbdevfs_dentry = NULL; - } --num_buses; if (num_buses <= 0) { @@ -738,7 +670,7 @@ void usbfs_remove_bus(struct usb_bus *bus) } usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } void usbfs_add_device(struct usb_device *dev) @@ -750,20 +682,12 @@ void usbfs_add_device(struct usb_device *dev) sprintf (name, "%03d", dev->devnum); dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, dev->bus->usbfs_dentry, dev, - &usbdevfs_device_file_operations, + &usbfs_device_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { err ("error creating usbfs device entry"); return; } - dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG, - dev->bus->usbdevfs_dentry, dev, - &usbdevfs_device_file_operations, - devuid, devgid); - if (dev->usbdevfs_dentry == NULL) { - err ("error creating usbdevfs device entry"); - return; - } /* Set the size of the device's file to be * equal to the size of the device descriptors. */ @@ -775,11 +699,9 @@ void usbfs_add_device(struct usb_device *dev) } if (dev->usbfs_dentry->d_inode) dev->usbfs_dentry->d_inode->i_size = i_size; - if (dev->usbdevfs_dentry->d_inode) - dev->usbdevfs_dentry->d_inode->i_size = i_size; usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } void usbfs_remove_device(struct usb_device *dev) @@ -791,10 +713,6 @@ void usbfs_remove_device(struct usb_device *dev) fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } - if (dev->usbdevfs_dentry) { - fs_remove_file (dev->usbdevfs_dentry); - dev->usbdevfs_dentry = NULL; - } while (!list_empty(&dev->filelist)) { ds = list_entry(dev->filelist.next, struct dev_state, list); list_del_init(&ds->list); @@ -807,51 +725,38 @@ void usbfs_remove_device(struct usb_device *dev) } } usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } /* --------------------------------------------------------------------- */ -#ifdef CONFIG_PROC_FS static struct proc_dir_entry *usbdir = NULL; -#endif int __init usbfs_init(void) { int retval; - retval = usb_register(&usbdevfs_driver); + retval = usb_register(&usbfs_driver); if (retval) return retval; retval = register_filesystem(&usb_fs_type); if (retval) { - usb_deregister(&usbdevfs_driver); - return retval; - } - retval = register_filesystem(&usbdevice_fs_type); - if (retval) { - unregister_filesystem(&usb_fs_type); - usb_deregister(&usbdevfs_driver); + usb_deregister(&usbfs_driver); return retval; } -#ifdef CONFIG_PROC_FS - /* create mount point for usbdevfs */ + /* create mount point for usbfs */ usbdir = proc_mkdir("usb", proc_bus); -#endif return 0; } void usbfs_cleanup(void) { - usb_deregister(&usbdevfs_driver); + usb_deregister(&usbfs_driver); unregister_filesystem(&usb_fs_type); - unregister_filesystem(&usbdevice_fs_type); -#ifdef CONFIG_PROC_FS if (usbdir) remove_proc_entry("usb", proc_bus); -#endif } diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 98695c68d687..f2cd4770eb4f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -17,6 +17,8 @@ #include <linux/init.h> #include <linux/mm.h> #include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/device.h> #include <asm/byteorder.h> #include "hcd.h" /* for usbcore internals */ @@ -623,6 +625,20 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, return result; } +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + static int usb_string_sub(struct usb_device *dev, unsigned int langid, unsigned int index, unsigned char *buf) { @@ -634,19 +650,26 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, /* If that failed try to read the descriptor length, then * ask for just that many bytes */ - if (rc < 0) { + if (rc < 2) { rc = usb_get_string(dev, langid, index, buf, 2); if (rc == 2) rc = usb_get_string(dev, langid, index, buf, buf[0]); } - if (rc >= 0) { + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + /* There might be extra junk at the end of the descriptor */ if (buf[0] < rc) rc = buf[0]; - if (rc < 2) - rc = -EINVAL; + + rc = rc - (rc & 1); /* force a multiple of two */ } + + if (rc < 2) + rc = (rc < 0 ? rc : -EINVAL); + return rc; } @@ -724,6 +747,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) buf[idx] = 0; err = idx; + if (tbuf[1] != USB_DT_STRING) + dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf); + errout: kfree(tbuf); return err; @@ -1132,6 +1158,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * use usb_set_interface() on the interfaces it claims. Resetting the whole * configuration would affect other drivers' interfaces. * + * The caller must own the device lock. + * * Returns zero on success, else a negative error code. */ int usb_reset_configuration(struct usb_device *dev) @@ -1142,9 +1170,9 @@ int usb_reset_configuration(struct usb_device *dev) if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - /* caller must own dev->serialize (config won't change) - * and the usb bus readlock (so driver bindings are stable); - * so calls during probe() are fine + /* caller must have locked the device and must own + * the usb bus readlock (so driver bindings are stable); + * calls during probe() are fine */ for (i = 1; i < 16; ++i) { @@ -1199,7 +1227,7 @@ static void release_interface(struct device *dev) * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt(), caller holds dev->serialize + * Context: !in_interrupt(), caller owns the device lock * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one @@ -1220,8 +1248,8 @@ static void release_interface(struct device *dev) * usb_set_interface(). * * This call is synchronous. The calling context must be able to sleep, - * and must not hold the driver model lock for USB; usb device driver - * probe() methods may not use this routine. + * must own the device lock, and must not hold the driver model's USB + * bus rwsem; usb device driver probe() methods cannot use this routine. * * Returns zero on success, or else the status code returned by the * underlying call that failed. On succesful completion, each interface @@ -1236,8 +1264,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_interface **new_interfaces = NULL; int n, nintf; - /* dev->serialize guards all config changes */ - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 78c5ca2f1051..bae974d587ae 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -27,11 +27,13 @@ static ssize_t show_##field (struct device *dev, char *buf) \ { \ struct usb_device *udev; \ + struct usb_host_config *actconfig; \ \ udev = to_usb_device (dev); \ - if (udev->actconfig) \ + actconfig = udev->actconfig; \ + if (actconfig) \ return sprintf (buf, format_string, \ - udev->actconfig->desc.field * multiplier); \ + actconfig->desc.field * multiplier); \ else \ return 0; \ } \ @@ -44,6 +46,28 @@ usb_actconfig_attr (bNumInterfaces, 1, "%2d\n") usb_actconfig_attr (bmAttributes, 1, "%2x\n") usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") +#define usb_actconfig_str(name, field) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + struct usb_host_config *actconfig; \ + int len; \ + \ + udev = to_usb_device (dev); \ + actconfig = udev->actconfig; \ + if (!actconfig) \ + return 0; \ + len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_actconfig_str (configuration, iConfiguration) + /* configuration value is always present, and r/w */ usb_actconfig_show(bConfigurationValue, 1, "%u\n"); @@ -55,9 +79,9 @@ set_bConfigurationValue (struct device *dev, const char *buf, size_t count) if (sscanf (buf, "%u", &config) != 1 || config > 255) return -EINVAL; - down(&udev->serialize); + usb_lock_device(udev); value = usb_set_configuration (udev, config); - up(&udev->serialize); + usb_unlock_device(udev); return (value < 0) ? value : count; } @@ -198,6 +222,7 @@ void usb_create_sysfs_dev_files (struct usb_device *udev) device_create_file (dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_create_file (dev, &dev_attr_serial); + device_create_file (dev, &dev_attr_configuration); } void usb_remove_sysfs_dev_files (struct usb_device *udev) @@ -212,6 +237,7 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev) device_remove_file(dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_remove_file(dev, &dev_attr_serial); + device_remove_file (dev, &dev_attr_configuration); } /* Interface fields */ @@ -231,7 +257,26 @@ usb_intf_attr (bNumEndpoints, "%02x\n") usb_intf_attr (bInterfaceClass, "%02x\n") usb_intf_attr (bInterfaceSubClass, "%02x\n") usb_intf_attr (bInterfaceProtocol, "%02x\n") -usb_intf_attr (iInterface, "%02x\n") + +#define usb_intf_str(name, field) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf; \ + struct usb_device *udev; \ + int len; \ + \ + intf = to_usb_interface (dev); \ + udev = interface_to_usbdev (intf); \ + len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_intf_str (interface, iInterface); static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, @@ -240,7 +285,6 @@ static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceClass.attr, &dev_attr_bInterfaceSubClass.attr, &dev_attr_bInterfaceProtocol.attr, - &dev_attr_iInterface.attr, NULL, }; static struct attribute_group intf_attr_grp = { @@ -250,9 +294,17 @@ static struct attribute_group intf_attr_grp = { void usb_create_sysfs_intf_files (struct usb_interface *intf) { sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->desc.iInterface) + device_create_file(&intf->dev, &dev_attr_interface); + } void usb_remove_sysfs_intf_files (struct usb_interface *intf) { sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->desc.iInterface) + device_remove_file(&intf->dev, &dev_attr_interface); + } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3c14361bbeb3..f57de4dca2bf 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -451,6 +451,11 @@ int usb_unlink_urb(struct urb *urb) if (!urb) return -EINVAL; if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { +#ifdef CONFIG_DEBUG_KERNEL + printk(KERN_NOTICE "usb_unlink_urb() is deprecated for " + "synchronous unlinks. Use usb_kill_urb() instead.\n"); + WARN_ON(1); +#endif usb_kill_urb(urb); return 0; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 138541225604..ca0b29d0ac1f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -39,6 +39,7 @@ #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/smp_lock.h> +#include <linux/rwsem.h> #include <linux/usb.h> #include <asm/io.h> @@ -62,6 +63,8 @@ const char *usbcore_name = "usbcore"; int nousb; /* Disable USB when built into kernel image */ /* Not honored on modular build */ +static DECLARE_RWSEM(usb_all_devices_rwsem); + static int generic_probe (struct device *dev) { @@ -100,7 +103,10 @@ int usb_probe_interface(struct device *dev) id = usb_match_id (intf, driver->id_table); if (id) { dev_dbg (dev, "%s - got id\n", __FUNCTION__); + intf->condition = USB_INTERFACE_BINDING; error = driver->probe (intf, id); + intf->condition = error ? USB_INTERFACE_UNBOUND : + USB_INTERFACE_BOUND; } return error; @@ -112,6 +118,8 @@ int usb_unbind_interface(struct device *dev) struct usb_interface *intf = to_usb_interface(dev); struct usb_driver *driver = to_usb_driver(intf->dev.driver); + intf->condition = USB_INTERFACE_UNBINDING; + /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -123,6 +131,7 @@ int usb_unbind_interface(struct device *dev) intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); + intf->condition = USB_INTERFACE_UNBOUND; return 0; } @@ -153,7 +162,9 @@ int usb_register(struct usb_driver *new_driver) new_driver->driver.remove = usb_unbind_interface; new_driver->driver.owner = new_driver->owner; + usb_lock_all_devices(); retval = driver_register(&new_driver->driver); + usb_unlock_all_devices(); if (!retval) { pr_info("%s: registered new driver %s\n", @@ -182,7 +193,9 @@ void usb_deregister(struct usb_driver *driver) { pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); + usb_lock_all_devices(); driver_unregister (&driver->driver); + usb_unlock_all_devices(); usbfs_update_special(); } @@ -204,7 +217,7 @@ void usb_deregister(struct usb_driver *driver) * alternate settings available for this interfaces. * * Don't call this function unless you are bound to one of the interfaces - * on this device or you own the dev->serialize semaphore! + * on this device or you have locked the device! */ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) { @@ -237,7 +250,7 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) * drivers avoid such mistakes. * * Don't call this function unless you are bound to the intf interface - * or you own the device's ->serialize semaphore! + * or you have locked the device! */ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, unsigned int altnum) @@ -305,11 +318,12 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) * way to bind to an interface is to return the private data from * the driver's probe() method. * - * Callers must own the driver model's usb bus writelock. So driver - * probe() entries don't need extra locking, but other call contexts - * may need to explicitly claim that lock. + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver probe() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. */ -int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) +int usb_driver_claim_interface(struct usb_driver *driver, + struct usb_interface *iface, void* priv) { struct device *dev = &iface->dev; @@ -318,6 +332,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface * dev->driver = &driver->driver; usb_set_intfdata(iface, priv); + iface->condition = USB_INTERFACE_BOUND; /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -338,8 +353,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface * * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the usb_device serialize semaphore and the driver model's - * usb bus writelock. So driver disconnect() entries don't need extra locking, + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver disconnect() entries don't need extra locking, * but other call contexts may need to explicitly claim those locks. */ void usb_driver_release_interface(struct usb_driver *driver, @@ -357,6 +372,7 @@ void usb_driver_release_interface(struct usb_driver *driver, dev->driver = NULL; usb_set_intfdata(iface, NULL); + iface->condition = USB_INTERFACE_UNBOUND; } /** @@ -748,7 +764,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port) init_MUTEX(&dev->serialize); if (dev->bus->op->allocate) - dev->bus->op->allocate(dev); + if (dev->bus->op->allocate(dev)) { + kfree(dev); + return NULL; + } return dev; } @@ -819,6 +838,160 @@ void usb_put_intf(struct usb_interface *intf) put_device(&intf->dev); } + +/* USB device locking + * + * Although locking USB devices should be straightforward, it is + * complicated by the way the driver-model core works. When a new USB + * driver is registered or unregistered, the core will automatically + * probe or disconnect all matching interfaces on all USB devices while + * holding the USB subsystem writelock. There's no good way for us to + * tell which devices will be used or to lock them beforehand; our only + * option is to effectively lock all the USB devices. + * + * We do that by using a private rw-semaphore, usb_all_devices_rwsem. + * When locking an individual device you must first acquire the rwsem's + * readlock. When a driver is registered or unregistered the writelock + * must be held. These actions are encapsulated in the subroutines + * below, so all a driver needs to do is call usb_lock_device() and + * usb_unlock_device(). + * + * Complications arise when several devices are to be locked at the same + * time. Only hub-aware drivers that are part of usbcore ever have to + * do this; nobody else needs to worry about it. The problem is that + * usb_lock_device() must not be called to lock a second device since it + * would acquire the rwsem's readlock reentrantly, leading to deadlock if + * another thread was waiting for the writelock. The solution is simple: + * + * When locking more than one device, call usb_lock_device() + * to lock the first one. Lock the others by calling + * down(&udev->serialize) directly. + * + * When unlocking multiple devices, use up(&udev->serialize) + * to unlock all but the last one. Unlock the last one by + * calling usb_unlock_device(). + * + * When locking both a device and its parent, always lock the + * the parent first. + */ + +/** + * usb_lock_device - acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Use this routine when you don't hold any other device locks; + * to acquire nested inner locks call down(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_lock_device(struct usb_device *udev) +{ + down_read(&usb_all_devices_rwsem); + down(&udev->serialize); +} + +/** + * usb_trylock_device - attempt to acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Don't use this routine if you already hold a device lock; + * use down_trylock(&udev->serialize) instead. + * This is necessary for proper interaction with usb_lock_all_devices(). + * + * Returns 1 if successful, 0 if contention. + */ +int usb_trylock_device(struct usb_device *udev) +{ + if (!down_read_trylock(&usb_all_devices_rwsem)) + return 0; + if (down_trylock(&udev->serialize)) { + up_read(&usb_all_devices_rwsem); + return 0; + } + return 1; +} + +/** + * usb_lock_device_for_reset - cautiously acquire the lock for a + * usb device structure + * @udev: device that's being locked + * @iface: interface bound to the driver making the request (optional) + * + * Attempts to acquire the device lock, but fails if the device is + * NOTATTACHED or SUSPENDED, or if iface is specified and the interface + * is neither BINDING nor BOUND. Rather than sleeping to wait for the + * lock, the routine polls repeatedly. This is to prevent deadlock with + * disconnect; in some drivers (such as usb-storage) the disconnect() + * callback will block waiting for a device reset to complete. + * + * Returns a negative error code for failure, otherwise 1 or 0 to indicate + * that the device will or will not have to be unlocked. (0 can be + * returned when an interface is given and is BINDING, because in that + * case the driver already owns the device lock.) + */ +int usb_lock_device_for_reset(struct usb_device *udev, + struct usb_interface *iface) +{ + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface) { + switch (iface->condition) { + case USB_INTERFACE_BINDING: + return 0; + case USB_INTERFACE_BOUND: + break; + default: + return -EINTR; + } + } + + while (!usb_trylock_device(udev)) { + msleep(15); + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface && iface->condition != USB_INTERFACE_BOUND) + return -EINTR; + } + return 1; +} + +/** + * usb_unlock_device - release the lock for a usb device structure + * @udev: device that's being unlocked + * + * Use this routine when releasing the only device lock you hold; + * to release inner nested locks call up(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_unlock_device(struct usb_device *udev) +{ + up(&udev->serialize); + up_read(&usb_all_devices_rwsem); +} + +/** + * usb_lock_all_devices - acquire the lock for all usb device structures + * + * This is necessary when registering a new driver or probing a bus, + * since the driver-model core may try to use any usb_device. + */ +void usb_lock_all_devices(void) +{ + down_write(&usb_all_devices_rwsem); +} + +/** + * usb_unlock_all_devices - release the lock for all usb device structures + */ +void usb_unlock_all_devices(void) +{ + up_write(&usb_all_devices_rwsem); +} + + static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) { @@ -840,8 +1013,10 @@ static struct usb_device *match_device(struct usb_device *dev, /* look through all of the children of this device */ for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { + down(&dev->children[child]->serialize); ret_dev = match_device(dev->children[child], vendor_id, product_id); + up(&dev->children[child]->serialize); if (ret_dev) goto exit; } @@ -876,7 +1051,9 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) bus = container_of(buslist, struct usb_bus, bus_list); if (!bus->root_hub) continue; + usb_lock_device(bus->root_hub); dev = match_device(bus->root_hub, vendor_id, product_id); + usb_unlock_device(bus->root_hub); if (dev) goto exit; } @@ -1362,6 +1539,11 @@ EXPORT_SYMBOL(usb_put_dev); EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL(usb_lock_device); +EXPORT_SYMBOL(usb_trylock_device); +EXPORT_SYMBOL(usb_lock_device_for_reset); +EXPORT_SYMBOL(usb_unlock_device); + EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_match_id); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index f1dff4f4d5d6..30d2cf7dd762 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -22,8 +22,14 @@ extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); extern int usb_set_configuration(struct usb_device *dev, int configuration); -extern void usb_set_device_state(struct usb_device *udev, - enum usb_device_state new_state); +extern void usb_lock_all_devices(void); +extern void usb_unlock_all_devices(void); /* for labeling diagnostics */ extern const char *usbcore_name; + +/* usbfs stuff */ +extern struct usb_driver usbfs_driver; +extern struct file_operations usbfs_devices_fops; +extern struct file_operations usbfs_device_file_operations; +extern void usbfs_conn_disc_event(void); |
