diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 11 | ||||
-rw-r--r-- | drivers/usb/core/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/buffer.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 22 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 192 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 23 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 106 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 156 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 77 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 426 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 2 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 44 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 90 | ||||
-rw-r--r-- | drivers/usb/core/notify.c | 120 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 319 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 167 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 33 |
18 files changed, 1176 insertions, 623 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 1a9ff618494..ff03184da40 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -61,14 +61,17 @@ config USB_DYNAMIC_MINORS If you are unsure about this, say N here. config USB_SUSPEND - bool "USB suspend/resume (EXPERIMENTAL)" + bool "USB selective suspend/resume and wakeup (EXPERIMENTAL)" depends on USB && PM && EXPERIMENTAL help If you say Y here, you can use driver calls or the sysfs "power/state" file to suspend or resume individual USB - peripherals. There are many related features, such as - remote wakeup and driver-specific suspend processing, that - may not yet work as expected. + peripherals. + + Also, USB "remote wakeup" signaling is supported, whereby some + USB devices (like keyboards and network adapters) can wake up + their parent hub. That wakeup cascades up the USB tree, and + could wake the system from states like suspend-to-RAM. If you are unsure about this, say N here. diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index d5503cf0bf7..dd1c4d2a0c3 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -3,7 +3,7 @@ # usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ - config.o file.o buffer.o sysfs.o devio.o + config.o file.o buffer.o sysfs.o devio.o notify.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index fc15b4acc8a..57e800ac3ce 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -106,7 +106,7 @@ void hcd_buffer_destroy (struct usb_hcd *hcd) void *hcd_buffer_alloc ( struct usb_bus *bus, size_t size, - unsigned mem_flags, + gfp_t mem_flags, dma_addr_t *dma ) { diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 99595e07b65..993019500cc 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -112,8 +112,12 @@ void usb_release_interface_cache(struct kref *ref) struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref); int j; - for (j = 0; j < intfc->num_altsetting; j++) - kfree(intfc->altsetting[j].endpoint); + for (j = 0; j < intfc->num_altsetting; j++) { + struct usb_host_interface *alt = &intfc->altsetting[j]; + + kfree(alt->endpoint); + kfree(alt->string); + } kfree(intfc); } @@ -188,10 +192,9 @@ static int usb_parse_interface(struct device *ddev, int cfgno, } len = sizeof(struct usb_host_endpoint) * num_ep; - alt->endpoint = kmalloc(len, GFP_KERNEL); + alt->endpoint = kzalloc(len, GFP_KERNEL); if (!alt->endpoint) return -ENOMEM; - memset(alt->endpoint, 0, len); /* Parse all the endpoint descriptors */ n = 0; @@ -353,10 +356,9 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, } len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j; - config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL); + config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL); if (!intfc) return -ENOMEM; - memset(intfc, 0, len); kref_init(&intfc->ref); } @@ -422,8 +424,6 @@ void usb_destroy_configuration(struct usb_device *dev) struct usb_host_config *cf = &dev->config[c]; kfree(cf->string); - cf->string = NULL; - for (i = 0; i < cf->desc.bNumInterfaces; i++) { if (cf->intf_cache[i]) kref_put(&cf->intf_cache[i]->ref, @@ -459,16 +459,14 @@ int usb_get_configuration(struct usb_device *dev) } length = ncfg * sizeof(struct usb_host_config); - dev->config = kmalloc(length, GFP_KERNEL); + dev->config = kzalloc(length, GFP_KERNEL); if (!dev->config) goto err2; - memset(dev->config, 0, length); length = ncfg * sizeof(char *); - dev->rawdescriptors = kmalloc(length, GFP_KERNEL); + dev->rawdescriptors = kzalloc(length, GFP_KERNEL); if (!dev->rawdescriptors) goto err2; - memset(dev->rawdescriptors, 0, length); buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); if (!buffer) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 487ff672b10..942cd437dc4 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -46,6 +46,7 @@ #include <linux/usb.h> #include <linux/usbdevice_fs.h> #include <linux/cdev.h> +#include <linux/notifier.h> #include <asm/uaccess.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> @@ -209,10 +210,10 @@ err: static struct async *alloc_async(unsigned int numisoframes) { unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor); - struct async *as = kmalloc(assize, GFP_KERNEL); + struct async *as = kzalloc(assize, GFP_KERNEL); + if (!as) return NULL; - memset(as, 0, assize); as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL); if (!as->urb) { kfree(as); @@ -279,6 +280,28 @@ static inline struct async *async_getpending(struct dev_state *ps, void __user * return NULL; } +static void snoop_urb(struct urb *urb, void __user *userurb) +{ + int j; + unsigned char *data = urb->transfer_buffer; + + if (!usbfs_snoop) + return; + + if (urb->pipe & USB_DIR_IN) + dev_info(&urb->dev->dev, "direction=IN\n"); + else + dev_info(&urb->dev->dev, "direction=OUT\n"); + dev_info(&urb->dev->dev, "userurb=%p\n", userurb); + dev_info(&urb->dev->dev, "transfer_buffer_length=%d\n", + urb->transfer_buffer_length); + dev_info(&urb->dev->dev, "actual_length=%d\n", urb->actual_length); + dev_info(&urb->dev->dev, "data: "); + for (j = 0; j < urb->transfer_buffer_length; ++j) + printk ("%02x ", data[j]); + printk("\n"); +} + static void async_completed(struct urb *urb, struct pt_regs *regs) { struct async *as = (struct async *)urb->context; @@ -296,7 +319,9 @@ static void async_completed(struct urb *urb, struct pt_regs *regs) kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, as->euid); } - wake_up(&ps->wait); + snoop(&urb->dev->dev, "urb complete\n"); + snoop_urb(urb, as->userurb); + wake_up(&ps->wait); } static void destroy_async (struct dev_state *ps, struct list_head *list) @@ -493,6 +518,23 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig return ret; } +static struct usb_device *usbdev_lookup_minor(int minor) +{ + struct class_device *class_dev; + struct usb_device *dev = NULL; + + down(&usb_device_class->sem); + list_for_each_entry(class_dev, &usb_device_class->children, node) { + if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { + dev = class_dev->class_data; + break; + } + } + up(&usb_device_class->sem); + + return dev; +}; + /* * file operations */ @@ -601,7 +643,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); for (j = 0; j < i; ++j) - printk ("%02x ", (unsigned char)(tbuf)[j]); + printk("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } if (copy_to_user(ctrl.data, tbuf, i)) { @@ -624,7 +666,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) if (usbfs_snoop) { dev_info(&dev->dev, "control write: data: "); for (j = 0; j < ctrl.wLength; ++j) - printk ("%02x ", (unsigned char)(tbuf)[j]); + printk("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } usb_unlock_device(dev); @@ -649,7 +691,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) unsigned int tmo, len1, pipe; int len2; unsigned char *tbuf; - int i, ret; + int i, j, ret; if (copy_from_user(&bulk, arg, sizeof(bulk))) return -EFAULT; @@ -674,10 +716,18 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } + snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n", + bulk.len, bulk.timeout); usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); if (!i && len2) { + if (usbfs_snoop) { + dev_info(&dev->dev, "bulk read: data "); + for (j = 0; j < len2; ++j) + printk("%02x ", (unsigned char)(tbuf)[j]); + printk("\n"); + } if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); return -EFAULT; @@ -690,6 +740,14 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } + snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n", + bulk.len, bulk.timeout); + if (usbfs_snoop) { + dev_info(&dev->dev, "bulk write: data: "); + for (j = 0; j < len1; ++j) + printk("%02x ", (unsigned char)(tbuf)[j]); + printk("\n"); + } usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); @@ -835,7 +893,6 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) return status; } - static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usbdevfs_iso_packet_desc __user *iso_frame_desc, void __user *arg) @@ -896,6 +953,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, kfree(dr); return -EFAULT; } + snoop(&ps->dev->dev, "control urb\n"); break; case USBDEVFS_URB_TYPE_BULK: @@ -910,6 +968,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; + snoop(&ps->dev->dev, "bulk urb\n"); break; case USBDEVFS_URB_TYPE_ISO: @@ -939,6 +998,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; } uurb->buffer_length = totlen; + snoop(&ps->dev->dev, "iso urb\n"); break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -954,6 +1014,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; + snoop(&ps->dev->dev, "interrupt urb\n"); break; default: @@ -1003,6 +1064,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EFAULT; } } + snoop(&as->urb->dev->dev, "submit urb\n"); + snoop_urb(as->urb, as->userurb); async_newpending(as); if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) { dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret); @@ -1238,23 +1301,20 @@ static int proc_releaseinterface(struct dev_state *ps, void __user *arg) return 0; } -static int proc_ioctl (struct dev_state *ps, void __user *arg) +static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) { - struct usbdevfs_ioctl ctrl; int size; void *buf = NULL; int retval = 0; struct usb_interface *intf = NULL; struct usb_driver *driver = NULL; - /* get input parameters and alloc buffer */ - if (copy_from_user(&ctrl, arg, sizeof (ctrl))) - return -EFAULT; - if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { + /* alloc buffer */ + if ((size = _IOC_SIZE (ctl->ioctl_code)) > 0) { if ((buf = kmalloc (size, GFP_KERNEL)) == NULL) return -ENOMEM; - if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) { - if (copy_from_user (buf, ctrl.data, size)) { + if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) { + if (copy_from_user (buf, ctl->data, size)) { kfree(buf); return -EFAULT; } @@ -1270,9 +1330,9 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) if (ps->dev->state != USB_STATE_CONFIGURED) retval = -EHOSTUNREACH; - else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + else if (!(intf = usb_ifnum_to_if (ps->dev, ctl->ifno))) retval = -EINVAL; - else switch (ctrl.ioctl_code) { + else switch (ctl->ioctl_code) { /* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: @@ -1304,7 +1364,7 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) if (driver == NULL || driver->ioctl == NULL) { retval = -ENOTTY; } else { - retval = driver->ioctl (intf, ctrl.ioctl_code, buf); + retval = driver->ioctl (intf, ctl->ioctl_code, buf); if (retval == -ENOIOCTLCMD) retval = -ENOTTY; } @@ -1313,15 +1373,42 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) /* cleanup and return */ if (retval >= 0 - && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0 + && (_IOC_DIR (ctl->ioctl_code) & _IOC_READ) != 0 && size > 0 - && copy_to_user (ctrl.data, buf, size) != 0) + && copy_to_user (ctl->data, buf, size) != 0) retval = -EFAULT; kfree(buf); return retval; } +static int proc_ioctl_default(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_ioctl ctrl; + + if (copy_from_user(&ctrl, arg, sizeof (ctrl))) + return -EFAULT; + return proc_ioctl(ps, &ctrl); +} + +#ifdef CONFIG_COMPAT +static int proc_ioctl_compat(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_ioctl32 __user *uioc; + struct usbdevfs_ioctl ctrl; + u32 udata; + + uioc = compat_ptr(arg); + if (get_user(ctrl.ifno, &uioc->ifno) || + get_user(ctrl.ioctl_code, &uioc->ioctl_code) || + __get_user(udata, &uioc->data)) + return -EFAULT; + ctrl.data = compat_ptr(udata); + + return proc_ioctl(ps, &ctrl); +} +#endif + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1422,6 +1509,10 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ret = proc_reapurbnonblock_compat(ps, p); break; + case USBDEVFS_IOCTL32: + snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__); + ret = proc_ioctl_compat(ps, p); + break; #endif case USBDEVFS_DISCARDURB: @@ -1456,7 +1547,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd case USBDEVFS_IOCTL: snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__); - ret = proc_ioctl(ps, p); + ret = proc_ioctl_default(ps, p); break; } usb_unlock_device(dev); @@ -1488,39 +1579,40 @@ struct file_operations usbfs_device_file_operations = { .release = usbdev_release, }; -struct usb_device *usbdev_lookup_minor(int minor) -{ - struct class_device *class_dev; - struct usb_device *dev = NULL; - - down(&usb_device_class->sem); - list_for_each_entry(class_dev, &usb_device_class->children, node) { - if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { - dev = class_dev->class_data; - break; - } - } - up(&usb_device_class->sem); - - return dev; -}; - -void usbdev_add(struct usb_device *dev) +static void usbdev_add(struct usb_device *dev) { int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); - dev->class_dev = class_device_create(usb_device_class, + dev->class_dev = class_device_create(usb_device_class, NULL, MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev, "usbdev%d.%d", dev->bus->busnum, dev->devnum); dev->class_dev->class_data = dev; } -void usbdev_remove(struct usb_device *dev) +static void usbdev_remove(struct usb_device *dev) { class_device_unregister(dev->class_dev); } +static int usbdev_notify(struct notifier_block *self, unsigned long action, + void *dev) +{ + switch (action) { + case USB_DEVICE_ADD: + usbdev_add(dev); + break; + case USB_DEVICE_REMOVE: + usbdev_remove(dev); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block usbdev_nb = { + .notifier_call = usbdev_notify, +}; + static struct cdev usb_device_cdev = { .kobj = {.name = "usb_device", }, .owner = THIS_MODULE, @@ -1540,24 +1632,32 @@ int __init usbdev_init(void) retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX); if (retval) { err("unable to get usb_device major %d", USB_DEVICE_MAJOR); - unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); - goto out; + goto error_cdev; } usb_device_class = class_create(THIS_MODULE, "usb_device"); if (IS_ERR(usb_device_class)) { err("unable to register usb_device class"); retval = PTR_ERR(usb_device_class); - usb_device_class = NULL; - cdev_del(&usb_device_cdev); - unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); + goto error_class; } + usb_register_notify(&usbdev_nb); + out: return retval; + +error_class: + usb_device_class = NULL; + cdev_del(&usb_device_cdev); + +error_cdev: + unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); + goto out; } void usbdev_cleanup(void) { + usb_unregister_notify(&usbdev_nb); class_destroy(usb_device_class); cdev_del(&usb_device_cdev); unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 65ca131cc44..e695308095a 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -17,7 +17,6 @@ #include <linux/config.h> #include <linux/module.h> -#include <linux/devfs_fs_kernel.h> #include <linux/spinlock.h> #include <linux/errno.h> @@ -88,8 +87,6 @@ int usb_major_init(void) goto out; } - devfs_mk_dir("usb"); - out: return error; } @@ -97,7 +94,6 @@ out: void usb_major_cleanup(void) { class_destroy(usb_class); - devfs_remove("usb"); unregister_chrdev(USB_MAJOR, "usb"); } @@ -112,8 +108,7 @@ void usb_major_cleanup(void) * enabled, the minor number will be based on the next available free minor, * starting at the class_driver->minor_base. * - * This function also creates the devfs file for the usb device, if devfs - * is enabled, and creates a usb class device in the sysfs tree. + * This function also creates a usb class device in the sysfs tree. * * usb_deregister_dev() must be called when the driver is done with * the minor numbers given out by this function. @@ -162,22 +157,20 @@ int usb_register_dev(struct usb_interface *intf, intf->minor = minor; - /* handle the devfs registration */ - snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base); - devfs_mk_cdev(MKDEV(USB_MAJOR, minor), class_driver->mode, name); - /* create a usb class device for this usb interface */ + snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base); temp = strrchr(name, '/'); if (temp && (temp[1] != 0x00)) ++temp; else temp = name; - intf->class_dev = class_device_create(usb_class, MKDEV(USB_MAJOR, minor), &intf->dev, "%s", temp); + intf->class_dev = class_device_create(usb_class, NULL, + MKDEV(USB_MAJOR, minor), + &intf->dev, "%s", temp); if (IS_ERR(intf->class_dev)) { spin_lock (&minor_lock); usb_minors[intf->minor] = NULL; spin_unlock (&minor_lock); - devfs_remove (name); retval = PTR_ERR(intf->class_dev); } exit: @@ -195,9 +188,8 @@ EXPORT_SYMBOL(usb_register_dev); * call to usb_register_dev() (usually when the device is disconnected * from the system.) * - * This function also cleans up the devfs file for the usb device, if devfs - * is enabled, and removes the usb class device from the sysfs tree. - * + * This function also removes the usb class device from the sysfs tree. + * * This should be called by all drivers that use the USB major number. */ void usb_deregister_dev(struct usb_interface *intf, @@ -220,7 +212,6 @@ void usb_deregister_dev(struct usb_interface *intf, spin_unlock (&minor_lock); snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); - devfs_remove (name); class_device_destroy(usb_class, MKDEV(USB_MAJOR, intf->minor)); intf->class_dev = NULL; intf->minor = -1; diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 6385d1a99b6..84d9e69329b 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -30,6 +30,8 @@ #include <asm/io.h> #include <asm/irq.h> #include <linux/usb.h> + +#include "usb.h" #include "hcd.h" @@ -197,6 +199,26 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) hcd = pci_get_drvdata(dev); + /* Root hub suspend should have stopped all downstream traffic, + * and all bus master traffic. And done so for both the interface + * and the stub usb_device (which we check here). But maybe it + * didn't; writing sysfs power/state files ignores such rules... + * + * We must ignore the FREEZE vs SUSPEND distinction here, because + * otherwise the swsusp will save (and restore) garbage state. + */ + if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON) + return -EBUSY; + + if (hcd->driver->suspend) { + retval = hcd->driver->suspend(hcd, message); + if (retval) { + dev_dbg (&dev->dev, "PCI pre-suspend fail, %d\n", + retval); + goto done; + } + } + /* FIXME until the generic PM interfaces change a lot more, this * can't use PCI D1 and D2 states. For example, the confusion * between messages and states will need to vanish, and messages @@ -215,31 +237,13 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) */ has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); - switch (hcd->state) { - - /* entry if root hub wasn't yet suspended ... from sysfs, - * without autosuspend, or if USB_SUSPEND isn't configured. + /* Downstream ports from this root hub should already be quiesced, so + * there will be no DMA activity. Now we can shut down the upstream + * link (except maybe for PME# resume signaling) and enter some PCI + * low power state, if the hardware allows. */ - case HC_STATE_RUNNING: - hcd->state = HC_STATE_QUIESCING; - retval = hcd->driver->suspend (hcd, message); - if (retval) { - dev_dbg (hcd->self.controller, - "suspend fail, retval %d\n", - retval); - break; - } - hcd->state = HC_STATE_SUSPENDED; - /* FALLTHROUGH */ + if (hcd->state == HC_STATE_SUSPENDED) { - /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the - * controller and/or root hub will already have been suspended, - * but it won't be ready for a PCI resume call. - * - * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will - * have been called, otherwise root hub timers still run ... - */ - case HC_STATE_SUSPENDED: /* no DMA or IRQs except when HC is active */ if (dev->current_state == PCI_D0) { pci_save_state (dev); @@ -248,7 +252,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) if (!has_pci_pm) { dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); - break; + goto done; } /* NOTE: dev->current_state becomes nonzero only here, and @@ -259,28 +263,29 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) retval = pci_set_power_state (dev, PCI_D3hot); if (retval == 0) { dev_dbg (hcd->self.controller, "--> PCI D3\n"); - retval = pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); - if (retval) - break; - retval = pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); - } else if (retval < 0) { + + /* Ignore these return values. We rely on pci code to + * reject requests the hardware can't implement, rather + * than coding the same thing. + */ + (void) pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); + (void) pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); + } else { dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", retval); (void) usb_hcd_pci_resume (dev); - break; } - break; - default: + + } else { dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", hcd->state); WARN_ON(1); retval = -EINVAL; - break; } - /* update power_state **ONLY** to make sysfs happier */ +done: if (retval == 0) - dev->dev.power.power_state = message; + dev->dev.power.power_state = PMSG_SUSPEND; return retval; } EXPORT_SYMBOL (usb_hcd_pci_suspend); @@ -336,20 +341,9 @@ int usb_hcd_pci_resume (struct pci_dev *dev) dev->current_state); } #endif - retval = pci_enable_wake (dev, dev->current_state, 0); - if (retval) { - dev_err(hcd->self.controller, - "can't enable_wake to %d, %d!\n", - dev->current_state, retval); - return retval; - } - retval = pci_enable_wake (dev, PCI_D3cold, 0); - if (retval) { - dev_err(hcd->self.controller, - "can't enable_wake to %d, %d!\n", - PCI_D3cold, retval); - return retval; - } + /* yes, ignore these results too... */ + (void) pci_enable_wake (dev, dev->current_state, 0); + (void) pci_enable_wake (dev, PCI_D3cold, 0); } else { /* Same basic cases: clean (powered/not), dirty */ dev_dbg(hcd->self.controller, "PCI legacy resume\n"); @@ -371,17 +365,17 @@ int usb_hcd_pci_resume (struct pci_dev *dev) dev->dev.power.power_state = PMSG_ON; - hcd->state = HC_STATE_RESUMING; hcd->saw_irq = 0; - retval = hcd->driver->resume (hcd); - if (!HC_IS_RUNNING (hcd->state)) { - dev_dbg (hcd->self.controller, - "resume fail, retval %d\n", retval); - usb_hc_died (hcd); + if (hcd->driver->resume) { + retval = hcd->driver->resume(hcd); + if (retval) { + dev_err (hcd->self.controller, + "PCI post-resume error %d!\n", retval); + usb_hc_died (hcd); + } } - retval = pci_enable_device(dev); return retval; } EXPORT_SYMBOL (usb_hcd_pci_resume); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 1017a97a418..6c7ca5b08cd 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -130,7 +130,7 @@ static const u8 usb2_rh_dev_descriptor [18] = { 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ 0x00, 0x00, /* __le16 idVendor; */ 0x00, 0x00, /* __le16 idProduct; */ @@ -153,7 +153,7 @@ static const u8 usb11_rh_dev_descriptor [18] = { 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ 0x00, 0x00, /* __le16 idVendor; */ 0x00, 0x00, /* __le16 idProduct; */ @@ -458,22 +458,18 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) default: /* non-generic request */ - if (HC_IS_SUSPENDED (hcd->state)) - status = -EAGAIN; - else { - switch (typeReq) { - case GetHubStatus: - case GetPortStatus: - len = 4; - break; - case GetHubDescriptor: - len = sizeof (struct usb_hub_descriptor); - break; - } - status = hcd->driver->hub_control (hcd, - typeReq, wValue, wIndex, - tbuf, wLength); + switch (typeReq) { + case GetHubStatus: + case GetPortStatus: + len = 4; + break; + case GetHubDescriptor: + len = sizeof (struct usb_hub_descriptor); + break; } + status = hcd->driver->hub_control (hcd, + typeReq, wValue, wIndex, + tbuf, wLength); break; error: /* "protocol stall" on error */ @@ -487,7 +483,7 @@ error: "CTRL: TypeReq=0x%x val=0x%x " "idx=0x%x len=%d ==> %d\n", typeReq, wValue, wIndex, - wLength, urb->status); + wLength, status); } } if (len) { @@ -748,10 +744,9 @@ struct usb_bus *usb_alloc_bus (struct usb_operations *op) { struct usb_bus *bus; - bus = kmalloc (sizeof *bus, GFP_KERNEL); + bus = kzalloc (sizeof *bus, GFP_KERNEL); if (!bus) return NULL; - memset(bus, 0, sizeof(struct usb_bus)); usb_bus_init (bus); bus->op = op; return bus; @@ -782,7 +777,8 @@ static int usb_register_bus(struct usb_bus *bus) return -E2BIG; } - bus->class_dev = class_device_create(usb_host_class, MKDEV(0,0), bus->controller, "usb_host%d", busnum); + bus->class_dev = class_device_create(usb_host_class, NULL, MKDEV(0,0), + bus->controller, "usb_host%d", busnum); if (IS_ERR(bus->class_dev)) { clear_bit(busnum, busmap.busmap); up(&usb_bus_list_lock); @@ -795,8 +791,7 @@ static int usb_register_bus(struct usb_bus *bus) list_add (&bus->bus_list, &usb_bus_list); up (&usb_bus_list_lock); - usbfs_add_bus (bus); - usbmon_notify_bus_add (bus); + usb_notify_add_bus(bus); dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum); return 0; @@ -823,8 +818,7 @@ static void usb_deregister_bus (struct usb_bus *bus) list_del (&bus->bus_list); up (&usb_bus_list_lock); - usbmon_notify_bus_remove (bus); - usbfs_remove_bus (bus); + usb_notify_remove_bus(bus); clear_bit (bus->busnum, busmap.busmap); @@ -1112,7 +1106,7 @@ static void urb_unlink (struct urb *urb) * expects usb_submit_urb() to have sanity checked and conditioned all * inputs in the urb */ -static int hcd_submit_urb (struct urb *urb, unsigned mem_flags) +static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags) { int status; struct usb_hcd *hcd = urb->dev->bus->hcpriv; @@ -1142,10 +1136,20 @@ static int hcd_submit_urb (struct urb *urb, unsigned mem_flags) else switch (hcd->state) { case HC_STATE_RUNNING: case HC_STATE_RESUMING: +doit: usb_get_dev (urb->dev); list_add_tail (&urb->urb_list, &ep->urb_list); status = 0; break; + case HC_STATE_SUSPENDED: + /* HC upstream links (register access, wakeup signaling) can work + * even when the downstream links (and DMA etc) are quiesced; let + * usbcore talk to the root hub. + */ + if (hcd->self.controller->power.power_state.event == PM_EVENT_ON + && urb->dev->parent == NULL) + goto doit; + /* FALL THROUGH */ default: status = -ESHUTDOWN; break; @@ -1293,12 +1297,6 @@ static int hcd_unlink_urb (struct urb *urb, int status) goto done; } - /* running ~= hc unlink handshake works (irq, timer, etc) - * halted ~= no unlink handshake is needed - * suspended, resuming == should never happen - */ - WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT); - /* insist the urb is still queued */ list_for_each(tmp, &ep->urb_list) { |