From fbf82fd2e1f4e679c60516d772d1862c941ca845 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sun, 31 Jul 2005 01:05:53 +0200 Subject: [PATCH] USB: real nodes instead of usbfs This patch introduces a /sys/class/usb_device/ class where every connected usb-device will show up: tree /sys/class/usb_device/ /sys/class/usb_device/ |-- usb1.1 | |-- dev | `-- device -> ../../../devices/pci0000:00/0000:00:1d.0/usb1 |-- usb2.1 | |-- dev | `-- device -> ../../../devices/pci0000:00/0000:00:1d.1/usb2 ... The presence of the "dev" file lets udev create real device nodes. kay@pim:~/src/linux-2.6> tree /dev/bus/usb/ /dev/bus/usb/ |-- 1 | `-- 1 |-- 2 | `-- 1 ... udev rule: SUBSYSTEM="usb_device", PROGRAM="/sbin/usb_device %k", NAME="%c" (echo $1 | /bin/sed 's/usb\([0-9]*\)\.\([0-9]*\)/bus\/usb\/\1\/\2/') This makes libusb pick up the real nodes instead of the mounted usbfs: export USB_DEVFS_PATH=/dev/bus/usb Background: All this makes it possible to manage usb devices with udev instead of the devfs solution. We are currently working on a pam_console/resmgr replacement driven by udev and a pam-helper. It applies ACL's to device nodes, which is required for modern desktop functionalty like "Fast User Switching" or multiple local login support. New patch with its own major. I've succesfully disabled usbfs and use real nodes only on my box. With: "export USB_DEVFS_PATH=/dev/bus/usb" libusb picks up the udev managed nodes instead of reading usbfs files. This makes udev to provide symlinks for libusb to pick up: SUBSYSTEM="usb_device", PROGRAM="/sbin/usbdevice %k", SYMLINK="%c" /sbin/usbdevice: #!/bin/sh echo $1 | /bin/sed 's/usbdev\([0-9]*\)\.\([0-9]*\)/bus\/usb\/\1\/\2/' Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/Makefile | 4 +-- drivers/usb/core/devio.c | 91 +++++++++++++++++++++++++++++++++++++++++++++-- drivers/usb/core/hub.c | 2 ++ drivers/usb/core/inode.c | 9 +---- drivers/usb/core/usb.c | 15 ++++++-- drivers/usb/core/usb.h | 5 +++ 6 files changed, 112 insertions(+), 14 deletions(-) (limited to 'drivers/usb/core') 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..d12bc5e84a1a 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -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; @@ -487,7 +492,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 +506,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; @@ -1477,3 +1487,80 @@ 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(MKDEV(USB_DEVICE_MAJOR, 0), + 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, + MKDEV(USB_DEVICE_MAJOR, 0), USB_DEVICE_MAX); + if (retval) { + err("unable to get usb_device major %d", USB_DEVICE_MAJOR); + unregister_chrdev_region(USB_DEVICE_MAJOR, 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_MAJOR, 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_MAJOR, USB_DEVICE_MAX); +} + diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c9412daff682..a220a5e7f4a5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1051,6 +1051,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 +1291,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; 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/usb.c b/drivers/usb/core/usb.c index 2cddd8a00437..bc966dbc6021 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1478,13 +1478,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 +1498,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 +1523,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 */ -- cgit v1.2.1 From fad21bdf56a25e1cb3e92bba33349de368e8f0b0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 10 Aug 2005 15:15:57 -0400 Subject: [PATCH] USB: Fix regression in core/devio.c This patch (as551) fixes another little problem recently added to the USB core. Someone didn't fix the type of the first argument to unregister_chrdev_region. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index d12bc5e84a1a..56c082f34927 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -76,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 @@ -1530,18 +1532,17 @@ int __init usbdev_init(void) { int retval; - retval = register_chrdev_region(MKDEV(USB_DEVICE_MAJOR, 0), - USB_DEVICE_MAX, "usb_device"); + 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, - MKDEV(USB_DEVICE_MAJOR, 0), USB_DEVICE_MAX); + 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_MAJOR, USB_DEVICE_MAX); + unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); goto out; } usb_device_class = class_create(THIS_MODULE, "usb_device"); @@ -1550,7 +1551,7 @@ int __init usbdev_init(void) retval = PTR_ERR(usb_device_class); usb_device_class = NULL; cdev_del(&usb_device_cdev); - unregister_chrdev_region(USB_DEVICE_MAJOR, USB_DEVICE_MAX); + unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); } out: @@ -1561,6 +1562,6 @@ void usbdev_cleanup(void) { class_destroy(usb_device_class); cdev_del(&usb_device_cdev); - unregister_chrdev_region(USB_DEVICE_MAJOR, USB_DEVICE_MAX); + unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); } -- cgit v1.2.1 From b375a0495fd622037560c73c05f23ae6f127bb0c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 29 Jul 2005 16:11:07 -0400 Subject: [PATCH] USB: URB_ASYNC_UNLINK flag removed from the kernel 29 July 2005, Cambridge, MA: This afternoon Alan Stern submitted a patch to remove the URB_ASYNC_UNLINK flag from the Linux kernel. Mr. Stern explained, "This flag is a relic from an earlier, less-well-designed system. For over a year it hasn't been used for anything other than printing warning messages." An anonymous spokesman for the Linux kernel development community commented, "This is exactly the sort of thing we see happening all the time. As the kernel evolves, support for old techniques and old code can be jettisoned and replaced by newer, better approaches. Proprietary operating systems do not have the freedom or flexibility to change so quickly." Mr. Stern, a staff member at Harvard University's Rowland Institute who works on Linux only as a hobby, noted that the patch (labelled as548) did not update two files, keyspan.c and option.c, in the USB drivers' "serial" subdirectory. "Those files need more extensive changes," he remarked. "They examine the status field of several URBs at times when they're not supposed to. That will need to be fixed before the URB_ASYNC_UNLINK flag is removed." Greg Kroah-Hartman, the kernel maintainer responsible for overseeing all of Linux's USB drivers, did not respond to our inquiries or return our calls. His only comment was "Applied, thanks." Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 4 +--- drivers/usb/core/urb.c | 26 ++++---------------------- 2 files changed, 5 insertions(+), 25 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 88d1b376f67c..74197249c245 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); @@ -357,8 +356,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); -- cgit v1.2.1 From 8b28c7526a302bbfa618f7eab4ef961edd68c9a0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 10 Aug 2005 17:04:13 -0400 Subject: [PATCH] USB: Code motion in the hub driver This patch (as553) merely moves some code and deletes an unneeded test in the hub driver. This is in preparation for the patch that follows. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a220a5e7f4a5..4a4b41f2665a 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) { @@ -717,15 +734,12 @@ static void hub_disconnect(struct usb_interface *intf) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev; - 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; @@ -1430,23 +1444,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 -- cgit v1.2.1 From bf193d3cd2a3b73f2df74f57106114867946c09c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 10 Aug 2005 17:12:31 -0400 Subject: [PATCH] USB: Disconnect children when unbinding the hub driver This patch (as554) makes the hub driver disconnect any child USB devices when it is unbound from a hub. Normally this will never happen, but there are a few oddball ways to unbind the hub driver while leaving the children intact. For example, the new "unbind" sysfs attribute can be used for this purpose. Given that unbinding hubs with children is now safe, the patch also removes the code that prevented people from doing so using usbfs. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 10 ---------- drivers/usb/core/hub.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 12 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 56c082f34927..b4265aa7d45e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1238,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))) @@ -1270,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); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4a4b41f2665a..9f54e8330f78 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -729,10 +729,29 @@ 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; usb_set_intfdata (intf, NULL); hdev = hub->hdev; @@ -760,8 +779,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) -- cgit v1.2.1 From 3ea15966ed59f2bc20928c7b0496b4585f6de206 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 11 Aug 2005 10:15:39 -0400 Subject: [PATCH] USB: Add timeout to usb_lock_device_for_reset This patch (as555) modifies the already-awkward usb_lock_device_for_reset routine in usbcore by adding a timeout. The whole point of the routine is that the caller wants to acquire some semaphores in the wrong order; protecting against the possibility of deadlock by timing out seems only prudent. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index bc966dbc6021..109f7558167a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -912,7 +912,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 +922,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 +940,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; -- cgit v1.2.1 From 3b4d7f79164853e10342d707e32307e0c8054982 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 11 Aug 2005 15:50:32 -0400 Subject: [PATCH] USB: Support unbinding of the usb_generic driver This patch (as556) adds support for unbinding the usb_generic "driver". That driver only binds to USB devices, as opposed to interfaces, and it does nothing much besides marking which struct device's go with an overall USB device plus providing suspend/resume methods. Now that users can unbind drivers at will using the sysfs "unbind" attribute, we need a rational way of dealing with USB devices that are no longer under full control of the USB stack. The patch handles this by unconfiguring the device, thereby removing all the interfaces and their associated drivers and children. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 109f7558167a..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; } -- cgit v1.2.1 From f956e7cd9ac4618b98022020e638bbdc01d9d65a Mon Sep 17 00:00:00 2001 From: "david-b@pacbell.net" Date: Thu, 11 Aug 2005 19:37:01 -0700 Subject: [PATCH] USB: tweak highspeed timing calculations Use a more correct calculation for highspeed bit times. http://bugzilla.kernel.org/show_bug.cgi?id=3604 This sort if thing might start to make a difference now that the high speed periodic scheduler is more complete -- and even getting used. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/core') 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)) -- cgit v1.2.1 From 8f34c2883b894b9a97f07b23b5b86fd65ecd2f85 Mon Sep 17 00:00:00 2001 From: "david-b@pacbell.net" Date: Thu, 11 Aug 2005 19:36:36 -0700 Subject: [PATCH] USB: remove annoying message Avoid an annoying message that can appear if devices are disconnected in the middle of a USB scatterlist operation. Message noted in http://bugzilla.kernel.org/show_bug.cgi?id=4373 (but the real issue there seems to be a SCSI level hang). Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 74197249c245..c47c8052b486 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -265,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); -- cgit v1.2.1 From e09711aef4180002241c7f2eab37390ddf40d6a0 Mon Sep 17 00:00:00 2001 From: "david-b@pacbell.net" Date: Sat, 13 Aug 2005 18:41:04 -0700 Subject: [PATCH] ehci: add think_time This adds think_time to the usb_tt struct and sets it appropriately (measured in ns); this can help us implement better split transaction scheduling. Signed-off-by: Dan Streetman Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 32 +++++++++++++++++++++++--------- drivers/usb/core/hub.h | 7 +++++++ 2 files changed, 30 insertions(+), 9 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 9f54e8330f78..758c7f0ed159 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -627,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; } 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; -- cgit v1.2.1