diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Makefile | 4 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 102 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 8 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 115 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 7 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 26 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 35 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 5 |
10 files changed, 232 insertions, 87 deletions
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 9e8c377b8161..d5503cf0bf74 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -3,14 +3,14 @@ # usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ - config.o file.o buffer.o sysfs.o + config.o file.o buffer.o sysfs.o devio.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o endif ifeq ($(CONFIG_USB_DEVICEFS),y) - usbcore-objs += devio.o inode.o devices.o + usbcore-objs += inode.o devices.o endif obj-$(CONFIG_USB) += usbcore.o diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index f86bf1454e21..b4265aa7d45e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -43,6 +43,7 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/usbdevice_fs.h> +#include <linux/cdev.h> #include <asm/uaccess.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> @@ -50,6 +51,10 @@ #include "hcd.h" /* for usbcore internals */ #include "usb.h" +#define USB_MAXBUS 64 +#define USB_DEVICE_MAX USB_MAXBUS * 128 +static struct class *usb_device_class; + struct async { struct list_head asynclist; struct dev_state *ps; @@ -71,6 +76,8 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic"); dev_info( dev , format , ## arg); \ } while (0) +#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) + #define MAX_USBFS_BUFFER_SIZE 16384 @@ -487,7 +494,7 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig */ static int usbdev_open(struct inode *inode, struct file *file) { - struct usb_device *dev; + struct usb_device *dev = NULL; struct dev_state *ps; int ret; @@ -501,11 +508,16 @@ static int usbdev_open(struct inode *inode, struct file *file) lock_kernel(); ret = -ENOENT; - dev = usb_get_dev(inode->u.generic_ip); + /* check if we are called from a real node or usbfs */ + if (imajor(inode) == USB_DEVICE_MAJOR) + dev = usbdev_lookup_minor(iminor(inode)); + if (!dev) + dev = inode->u.generic_ip; if (!dev) { kfree(ps); goto out; } + usb_get_dev(dev); ret = 0; ps->dev = dev; ps->file = file; @@ -1226,7 +1238,6 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) int retval = 0; struct usb_interface *intf = NULL; struct usb_driver *driver = NULL; - int i; /* get input parameters and alloc buffer */ if (copy_from_user(&ctrl, arg, sizeof (ctrl))) @@ -1258,15 +1269,6 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) /* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: - /* don't allow the user to unbind the hub driver from - * a hub with children to manage */ - for (i = 0; i < ps->dev->maxchild; ++i) { - if (ps->dev->children[i]) - retval = -EBUSY; - } - if (retval) - break; - down_write(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) { driver = to_usb_driver(intf->dev.driver); @@ -1477,3 +1479,79 @@ struct file_operations usbfs_device_file_operations = { .open = usbdev_open, .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) +{ + int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); + + dev->class_dev = class_device_create(usb_device_class, + 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) +{ + class_device_unregister(dev->class_dev); +} + +static struct cdev usb_device_cdev = { + .kobj = {.name = "usb_device", }, + .owner = THIS_MODULE, +}; + +int __init usbdev_init(void) +{ + int retval; + + retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX, + "usb_device"); + if (retval) { + err("unable to register minors for usb_device"); + goto out; + } + cdev_init(&usb_device_cdev, &usbfs_device_file_operations); + 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; + } + 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); + } + +out: + return retval; +} + +void usbdev_cleanup(void) +{ + 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/hcd.h b/drivers/usb/core/hcd.h index 28055f95645b..ac451fa7e4d2 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -339,11 +339,11 @@ extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); * to preallocate bandwidth) */ #define USB2_HOST_DELAY 5 /* nsec, guess */ -#define HS_NSECS(bytes) ( ((55 * 8 * 2083)/1000) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ +#define HS_NSECS(bytes) ( ((55 * 8 * 2083) \ + + (2083UL * (3 + BitTime(bytes))))/1000 \ + USB2_HOST_DELAY) -#define HS_NSECS_ISO(bytes) ( ((38 * 8 * 2083)/1000) \ - + ((2083UL * (3167 + BitTime (bytes)))/1000) \ +#define HS_NSECS_ISO(bytes) ( ((38 * 8 * 2083) \ + + (2083UL * (3 + BitTime(bytes))))/1000 \ + USB2_HOST_DELAY) #define HS_USECS(bytes) NS_TO_US (HS_NSECS(bytes)) #define HS_USECS_ISO(bytes) NS_TO_US (HS_NSECS_ISO(bytes)) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c9412daff682..758c7f0ed159 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -492,6 +492,23 @@ static int hub_hub_status(struct usb_hub *hub, return ret; } +static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) +{ + struct usb_device *hdev = hub->hdev; + int ret; + + if (hdev->children[port1-1] && set_state) { + usb_set_device_state(hdev->children[port1-1], + USB_STATE_NOTATTACHED); + } + ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + if (ret) + dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", + port1, ret); + + return ret; +} + static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { @@ -610,19 +627,33 @@ static int hub_configure(struct usb_hub *hub, break; } + /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */ switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) { - case 0x00: - if (hdev->descriptor.bDeviceProtocol != 0) - dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n"); + case HUB_TTTT_8_BITS: + if (hdev->descriptor.bDeviceProtocol != 0) { + hub->tt.think_time = 666; + dev_dbg(hub_dev, "TT requires at most %d " + "FS bit times (%d ns)\n", + 8, hub->tt.think_time); + } break; - case 0x20: - dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n"); + case HUB_TTTT_16_BITS: + hub->tt.think_time = 666 * 2; + dev_dbg(hub_dev, "TT requires at most %d " + "FS bit times (%d ns)\n", + 16, hub->tt.think_time); break; - case 0x40: - dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n"); + case HUB_TTTT_24_BITS: + hub->tt.think_time = 666 * 3; + dev_dbg(hub_dev, "TT requires at most %d " + "FS bit times (%d ns)\n", + 24, hub->tt.think_time); break; - case 0x60: - dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n"); + case HUB_TTTT_32_BITS: + hub->tt.think_time = 666 * 4; + dev_dbg(hub_dev, "TT requires at most %d " + "FS bit times (%d ns)\n", + 32, hub->tt.think_time); break; } @@ -712,20 +743,36 @@ fail: static unsigned highspeed_hubs; +/* Called after the hub driver is unbound from a hub with children */ +static void hub_remove_children_work(void *__hub) +{ + struct usb_hub *hub = __hub; + struct usb_device *hdev = hub->hdev; + int i; + + kfree(hub); + + usb_lock_device(hdev); + for (i = 0; i < hdev->maxchild; ++i) { + if (hdev->children[i]) + usb_disconnect(&hdev->children[i]); + } + usb_unlock_device(hdev); + usb_put_dev(hdev); +} + static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev; + int n, port1; - if (!hub) - return; + usb_set_intfdata (intf, NULL); hdev = hub->hdev; if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; - usb_set_intfdata (intf, NULL); - hub_quiesce(hub); usb_free_urb(hub->urb); hub->urb = NULL; @@ -746,8 +793,27 @@ static void hub_disconnect(struct usb_interface *intf) hub->buffer = NULL; } - /* Free the memory */ - kfree(hub); + /* If there are any children then this is an unbind only, not a + * physical disconnection. The active ports must be disabled + * and later on we must call usb_disconnect(). We can't call + * it now because we may not hold the hub's device lock. + */ + n = 0; + for (port1 = 1; port1 <= hdev->maxchild; ++port1) { + if (hdev->children[port1 - 1]) { + ++n; + hub_port_disable(hub, port1, 1); + } + } + + if (n == 0) + kfree(hub); + else { + /* Reuse the hub->leds work_struct for our own purposes */ + INIT_WORK(&hub->leds, hub_remove_children_work, hub); + schedule_work(&hub->leds); + usb_get_dev(hdev); + } } static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1051,6 +1117,7 @@ void usb_disconnect(struct usb_device **pdev) dev_dbg (&udev->dev, "unregistering device\n"); release_address(udev); usbfs_remove_device(udev); + usbdev_remove(udev); usb_remove_sysfs_dev_files(udev); /* Avoid races with recursively_mark_NOTATTACHED() */ @@ -1290,6 +1357,7 @@ int usb_new_device(struct usb_device *udev) /* USB device state == configured ... usable */ /* add a /proc/bus/usb entry */ + usbdev_add(udev); usbfs_add_device(udev); return 0; @@ -1428,23 +1496,6 @@ static int hub_port_reset(struct usb_hub *hub, int port1, return status; } -static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) -{ - struct usb_device *hdev = hub->hdev; - int ret; - - if (hdev->children[port1-1] && set_state) { - usb_set_device_state(hdev->children[port1-1], - USB_STATE_NOTATTACHED); - } - ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); - if (ret) - dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", - port1, ret); - - 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 diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 53bf5649621e..e7fa9b5a521e 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -157,6 +157,12 @@ enum hub_led_mode { struct usb_device; +/* Transaction Translator Think Times, in bits */ +#define HUB_TTTT_8_BITS 0x00 +#define HUB_TTTT_16_BITS 0x20 +#define HUB_TTTT_24_BITS 0x40 +#define HUB_TTTT_32_BITS 0x60 + /* * As of USB 2.0, full/low speed devices are segregated into trees. * One type grows from USB 1.1 host controllers (OHCI, UHCI etc). @@ -170,6 +176,7 @@ struct usb_device; struct usb_tt { struct usb_device *hub; /* upstream highspeed hub */ int multi; /* true means one TT per port */ + unsigned think_time; /* think time in ns */ /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ spinlock_t lock; diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index c3e3a95d3804..640f41e47029 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -728,15 +728,9 @@ int __init usbfs_init(void) { int retval; - retval = usb_register(&usbfs_driver); - if (retval) - return retval; - retval = register_filesystem(&usb_fs_type); - if (retval) { - usb_deregister(&usbfs_driver); + if (retval) return retval; - } /* create mount point for usbfs */ usbdir = proc_mkdir("usb", proc_bus); @@ -746,7 +740,6 @@ int __init usbfs_init(void) void usbfs_cleanup(void) { - usb_deregister(&usbfs_driver); unregister_filesystem(&usb_fs_type); if (usbdir) remove_proc_entry("usb", proc_bus); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 88d1b376f67c..c47c8052b486 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -48,7 +48,6 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) init_completion(&done); urb->context = &done; - urb->transfer_flags |= URB_ASYNC_UNLINK; urb->actual_length = 0; status = usb_submit_urb(urb, GFP_NOIO); @@ -266,7 +265,9 @@ static void sg_complete (struct urb *urb, struct pt_regs *regs) continue; if (found) { status = usb_unlink_urb (io->urbs [i]); - if (status != -EINPROGRESS && status != -EBUSY) + if (status != -EINPROGRESS + && status != -ENODEV + && status != -EBUSY) dev_err (&io->dev->dev, "%s, unlink --> %d\n", __FUNCTION__, status); @@ -357,8 +358,7 @@ int usb_sg_init ( if (!io->urbs) goto nomem; - urb_flags = URB_ASYNC_UNLINK | URB_NO_TRANSFER_DMA_MAP - | URB_NO_INTERRUPT; + urb_flags = URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT; if (usb_pipein (pipe)) urb_flags |= URB_SHORT_NOT_OK; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index c0feee25ff0a..c846fefb7386 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -309,9 +309,8 @@ int usb_submit_urb(struct urb *urb, unsigned mem_flags) unsigned int allowed; /* enforce simple/standard policy */ - allowed = URB_ASYNC_UNLINK; // affects later unlinks - allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - allowed |= URB_NO_INTERRUPT; + allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP | + URB_NO_INTERRUPT); switch (temp) { case PIPE_BULK: if (is_out) @@ -400,14 +399,8 @@ int usb_submit_urb(struct urb *urb, unsigned mem_flags) * canceled (rather than any other code) and will quickly be removed * from host controller data structures. * - * In the past, clearing the URB_ASYNC_UNLINK transfer flag for the - * URB indicated that the request was synchronous. This usage is now - * deprecated; if the flag is clear the call will be forwarded to - * usb_kill_urb() and the return value will be 0. In the future, drivers - * should call usb_kill_urb() directly for synchronous unlinking. - * - * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this - * request is asynchronous. Success is indicated by returning -EINPROGRESS, + * This request is always asynchronous. + * Success is indicated by returning -EINPROGRESS, * at which time the URB will normally have been unlinked but not yet * given back to the device driver. When it is called, the completion * function will see urb->status == -ECONNRESET. Failure is indicated @@ -453,17 +446,6 @@ int usb_unlink_urb(struct urb *urb) { if (!urb) return -EINVAL; - if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { -#ifdef CONFIG_DEBUG_KERNEL - if (printk_ratelimit()) { - 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; - } if (!(urb->dev && urb->dev->bus && urb->dev->bus->op)) return -ENODEV; return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 2cddd8a00437..087af73a59dd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -65,6 +65,16 @@ static int generic_probe (struct device *dev) } static int generic_remove (struct device *dev) { + struct usb_device *udev = to_usb_device(dev); + + /* if this is only an unbind, not a physical disconnect, then + * unconfigure the device */ + if (udev->state == USB_STATE_CONFIGURED) + usb_set_configuration(udev, 0); + + /* in case the call failed or the device was suspended */ + if (udev->state >= USB_STATE_CONFIGURED) + usb_disable_device(udev, 0); return 0; } @@ -912,7 +922,7 @@ int usb_trylock_device(struct usb_device *udev) * 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. + * or suspend() method 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 @@ -922,6 +932,8 @@ int usb_trylock_device(struct usb_device *udev) int usb_lock_device_for_reset(struct usb_device *udev, struct usb_interface *iface) { + unsigned long jiffies_expire = jiffies + HZ; + if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; if (udev->state == USB_STATE_SUSPENDED) @@ -938,6 +950,12 @@ int usb_lock_device_for_reset(struct usb_device *udev, } while (!usb_trylock_device(udev)) { + + /* If we can't acquire the lock after waiting one second, + * we're probably deadlocked */ + if (time_after(jiffies, jiffies_expire)) + return -EBUSY; + msleep(15); if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; @@ -1478,13 +1496,18 @@ static int __init usb_init(void) retval = usb_major_init(); if (retval) goto major_init_failed; + retval = usb_register(&usbfs_driver); + if (retval) + goto driver_register_failed; + retval = usbdev_init(); + if (retval) + goto usbdevice_init_failed; retval = usbfs_init(); if (retval) goto fs_init_failed; retval = usb_hub_init(); if (retval) goto hub_init_failed; - retval = driver_register(&usb_generic_driver); if (!retval) goto out; @@ -1493,7 +1516,11 @@ static int __init usb_init(void) hub_init_failed: usbfs_cleanup(); fs_init_failed: - usb_major_cleanup(); + usbdev_cleanup(); +usbdevice_init_failed: + usb_deregister(&usbfs_driver); +driver_register_failed: + usb_major_cleanup(); major_init_failed: usb_host_cleanup(); host_init_failed: @@ -1514,6 +1541,8 @@ static void __exit usb_exit(void) driver_unregister(&usb_generic_driver); usb_major_cleanup(); usbfs_cleanup(); + usb_deregister(&usbfs_driver); + usbdev_cleanup(); usb_hub_cleanup(); usb_host_cleanup(); bus_unregister(&usb_bus_type); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 2c690f6d4c18..83d48c8133af 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -37,6 +37,11 @@ extern struct file_operations usbfs_devices_fops; extern struct file_operations usbfs_device_file_operations; extern void usbfs_conn_disc_event(void); +extern int usbdev_init(void); +extern void usbdev_cleanup(void); +extern void usbdev_add(struct usb_device *dev); +extern void usbdev_remove(struct usb_device *dev); +extern struct usb_device *usbdev_lookup_minor(int minor); struct dev_state { struct list_head list; /* state list */ |