USB: utilize the bus notifiers
authorAlan Stern <stern@rowland.harvard.edu>
Fri, 5 Dec 2008 19:10:34 +0000 (14:10 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 7 Jan 2009 18:00:08 +0000 (10:00 -0800)
This patch (as1185) makes usbcore take advantage of the bus
notifications sent out by the driver core.  Now we can create all our
device and interface attribute files before the device or interface
uevent is broadcast.

A side effect is that we no longer create the endpoint "pseudo"
devices at the same time as a device or interface is registered -- it
seems like a bad idea to try registering an endpoint before the
registration of its parent is complete.  So the routines for creating
and removing endpoint devices have been split out and renamed, and
they are called explicitly when needed.  A new bitflag is used for
keeping track of whether or not the interface's endpoint devices have
been created, since (just as with the interface attributes) they vary
with the altsetting and hence can be changed at random times.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/endpoint.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/sysfs.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
include/linux/usb.h

index 946fae4..e1710f2 100644 (file)
@@ -276,7 +276,7 @@ static void ep_device_release(struct device *dev)
        kfree(ep_dev);
 }
 
-int usb_create_ep_files(struct device *parent,
+int usb_create_ep_devs(struct device *parent,
                        struct usb_host_endpoint *endpoint,
                        struct usb_device *udev)
 {
@@ -340,7 +340,7 @@ exit:
        return retval;
 }
 
-void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
+void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
 {
        struct ep_device *ep_dev = endpoint->ep_dev;
 
index 5abdc11..756b8d9 100644 (file)
@@ -1437,17 +1437,12 @@ void usb_disconnect(struct usb_device **pdev)
        usb_disable_device(udev, 0);
        usb_hcd_synchronize_unlinks(udev);
 
+       usb_remove_ep_devs(&udev->ep0);
        usb_unlock_device(udev);
 
-       /* Remove the device-specific files from sysfs.  This must be
-        * done with udev unlocked, because some of the attribute
-        * routines try to acquire the device lock.
-        */
-       usb_remove_sysfs_dev_files(udev);
-
        /* Unregister the device.  The device driver is responsible
-        * for removing the device files from usbfs and sysfs and for
-        * de-configuring the device.
+        * for de-configuring the device and invoking the remove-device
+        * notifier chain (used by usbfs and possibly others).
         */
        device_del(&udev->dev);
 
@@ -1654,8 +1649,8 @@ int usb_new_device(struct usb_device *udev)
        announce_device(udev);
 
        /* Register the device.  The device driver is responsible
-        * for adding the device files to sysfs and for configuring
-        * the device.
+        * for configuring the device and invoking the add-device
+        * notifier chain (used by usbfs and possibly others).
         */
        err = device_add(&udev->dev);
        if (err) {
@@ -1663,8 +1658,7 @@ int usb_new_device(struct usb_device *udev)
                goto fail;
        }
 
-       /* put device-specific files into sysfs */
-       usb_create_sysfs_dev_files(udev);
+       (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
        return err;
 
 fail:
index aadf29f..7943901 100644 (file)
@@ -1004,6 +1004,34 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
 }
 EXPORT_SYMBOL_GPL(usb_clear_halt);
 
+static int create_intf_ep_devs(struct usb_interface *intf)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct usb_host_interface *alt = intf->cur_altsetting;
+       int i;
+
+       if (intf->ep_devs_created || intf->unregistering)
+               return 0;
+
+       for (i = 0; i < alt->desc.bNumEndpoints; ++i)
+               (void) usb_create_ep_devs(&intf->dev, &alt->endpoint[i], udev);
+       intf->ep_devs_created = 1;
+       return 0;
+}
+
+static void remove_intf_ep_devs(struct usb_interface *intf)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+       int i;
+
+       if (!intf->ep_devs_created)
+               return;
+
+       for (i = 0; i < alt->desc.bNumEndpoints; ++i)
+               usb_remove_ep_devs(&alt->endpoint[i]);
+       intf->ep_devs_created = 0;
+}
+
 /**
  * usb_disable_endpoint -- Disable an endpoint by address
  * @dev: the device whose endpoint is being disabled
@@ -1092,7 +1120,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
                        dev_dbg(&dev->dev, "unregistering interface %s\n",
                                dev_name(&interface->dev));
                        interface->unregistering = 1;
-                       usb_remove_sysfs_intf_files(interface);
+                       remove_intf_ep_devs(interface);
                        device_del(&interface->dev);
                }
 
@@ -1235,8 +1263,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
         */
 
        /* prevent submissions using previous endpoint settings */
-       if (iface->cur_altsetting != alt)
+       if (iface->cur_altsetting != alt) {
+               remove_intf_ep_devs(iface);
                usb_remove_sysfs_intf_files(iface);
+       }
        usb_disable_interface(dev, iface);
 
        iface->cur_altsetting = alt;
@@ -1272,9 +1302,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
         * (Likewise, EP0 never "halts" on well designed devices.)
         */
        usb_enable_interface(dev, iface);
-       if (device_is_registered(&iface->dev))
+       if (device_is_registered(&iface->dev)) {
                usb_create_sysfs_intf_files(iface);
-
+               create_intf_ep_devs(iface);
+       }
        return 0;
 }
 EXPORT_SYMBOL_GPL(usb_set_interface);
@@ -1334,7 +1365,6 @@ int usb_reset_configuration(struct usb_device *dev)
                struct usb_interface *intf = config->interface[i];
                struct usb_host_interface *alt;
 
-               usb_remove_sysfs_intf_files(intf);
                alt = usb_altnum_to_altsetting(intf, 0);
 
                /* No altsetting 0?  We'll assume the first altsetting.
@@ -1345,10 +1375,16 @@ int usb_reset_configuration(struct usb_device *dev)
                if (!alt)
                        alt = &intf->altsetting[0];
 
+               if (alt != intf->cur_altsetting) {
+                       remove_intf_ep_devs(intf);
+                       usb_remove_sysfs_intf_files(intf);
+               }
                intf->cur_altsetting = alt;
                usb_enable_interface(dev, intf);
-               if (device_is_registered(&intf->dev))
+               if (device_is_registered(&intf->dev)) {
                        usb_create_sysfs_intf_files(intf);
+                       create_intf_ep_devs(intf);
+               }
        }
        return 0;
 }
@@ -1682,7 +1718,7 @@ free_interfaces:
                                dev_name(&intf->dev), ret);
                        continue;
                }
-               usb_create_sysfs_intf_files(intf);
+               create_intf_ep_devs(intf);
        }
 
        usb_autosuspend_device(dev);
index 0f0ccf6..4cc2456 100644 (file)
@@ -629,9 +629,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
        struct device *dev = &udev->dev;
        int retval;
 
-       /* Unforunately these attributes cannot be created before
-        * the uevent is broadcast.
-        */
        retval = device_create_bin_file(dev, &dev_bin_attr_descriptors);
        if (retval)
                goto error;
@@ -643,11 +640,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
        retval = add_power_attributes(dev);
        if (retval)
                goto error;
-
-       retval = usb_create_ep_files(dev, &udev->ep0, udev);
-       if (retval)
-               goto error;
-       return 0;
+       return retval;
 error:
        usb_remove_sysfs_dev_files(udev);
        return retval;
@@ -657,7 +650,6 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
 {
        struct device *dev = &udev->dev;
 
-       usb_remove_ep_files(&udev->ep0);
        remove_power_attributes(dev);
        remove_persist_attributes(dev);
        device_remove_bin_file(dev, &dev_bin_attr_descriptors);
@@ -816,36 +808,24 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf)
 {
        struct usb_device *udev = interface_to_usbdev(intf);
        struct usb_host_interface *alt = intf->cur_altsetting;
-       int i;
        int retval;
 
        if (intf->sysfs_files_created || intf->unregistering)
                return 0;
 
-       /* The interface string may be present in some altsettings
-        * and missing in others.  Hence its attribute cannot be created
-        * before the uevent is broadcast.
-        */
        if (alt->string == NULL)
                alt->string = usb_cache_string(udev, alt->desc.iInterface);
        if (alt->string)
                retval = device_create_file(&intf->dev, &dev_attr_interface);
-       for (i = 0; i < alt->desc.bNumEndpoints; ++i)
-               usb_create_ep_files(&intf->dev, &alt->endpoint[i], udev);
        intf->sysfs_files_created = 1;
        return 0;
 }
 
 void usb_remove_sysfs_intf_files(struct usb_interface *intf)
 {
-       struct usb_host_interface *alt = intf->cur_altsetting;
-       int i;
-
        if (!intf->sysfs_files_created)
                return;
 
-       for (i = 0; i < alt->desc.bNumEndpoints; ++i)
-               usb_remove_ep_files(&alt->endpoint[i]);
        device_remove_file(&intf->dev, &dev_attr_interface);
        intf->sysfs_files_created = 0;
 }
index 4c98f39..c082156 100644 (file)
@@ -971,6 +971,37 @@ int usb_disabled(void)
 EXPORT_SYMBOL_GPL(usb_disabled);
 
 /*
+ * Notifications of device and interface registration
+ */
+static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
+               void *data)
+{
+       struct device *dev = data;
+
+       switch (action) {
+       case BUS_NOTIFY_ADD_DEVICE:
+               if (dev->type == &usb_device_type)
+                       (void) usb_create_sysfs_dev_files(to_usb_device(dev));
+               else if (dev->type == &usb_if_device_type)
+                       (void) usb_create_sysfs_intf_files(
+                                       to_usb_interface(dev));
+               break;
+
+       case BUS_NOTIFY_DEL_DEVICE:
+               if (dev->type == &usb_device_type)
+                       usb_remove_sysfs_dev_files(to_usb_device(dev));
+               else if (dev->type == &usb_if_device_type)
+                       usb_remove_sysfs_intf_files(to_usb_interface(dev));
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block usb_bus_nb = {
+       .notifier_call = usb_bus_notify,
+};
+
+/*
  * Init
  */
 static int __init usb_init(void)
@@ -987,6 +1018,9 @@ static int __init usb_init(void)
        retval = bus_register(&usb_bus_type);
        if (retval)
                goto bus_register_failed;
+       retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
+       if (retval)
+               goto bus_notifier_failed;
        retval = usb_host_init();
        if (retval)
                goto host_init_failed;
@@ -1021,6 +1055,8 @@ driver_register_failed:
 major_init_failed:
        usb_host_cleanup();
 host_init_failed:
+       bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
+bus_notifier_failed:
        bus_unregister(&usb_bus_type);
 bus_register_failed:
        ksuspend_usb_cleanup();
@@ -1044,6 +1080,7 @@ static void __exit usb_exit(void)
        usb_devio_cleanup();
        usb_hub_cleanup();
        usb_host_cleanup();
+       bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
        bus_unregister(&usb_bus_type);
        ksuspend_usb_cleanup();
 }
index 9fb1956..381eae9 100644 (file)
@@ -6,10 +6,10 @@ extern int usb_create_sysfs_dev_files(struct usb_device *dev);
 extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
 extern int usb_create_sysfs_intf_files(struct usb_interface *intf);
 extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
-extern int usb_create_ep_files(struct device *parent,
+extern int usb_create_ep_devs(struct device *parent,
                                struct usb_host_endpoint *endpoint,
                                struct usb_device *udev);
-extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
+extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint);
 
 extern void usb_enable_endpoint(struct usb_device *dev,
                struct usb_host_endpoint *ep);
index 74d0b99..e9d6356 100644 (file)
@@ -108,6 +108,7 @@ enum usb_interface_condition {
  *     (in probe()), bound to a driver, or unbinding (in disconnect())
  * @is_active: flag set when the interface is bound and not suspended.
  * @sysfs_files_created: sysfs attributes exist
+ * @ep_devs_created: endpoint child pseudo-devices exist
  * @unregistering: flag set when the interface is being unregistered
  * @needs_remote_wakeup: flag set when the driver requires remote-wakeup
  *     capability during autosuspend.
@@ -169,6 +170,7 @@ struct usb_interface {
        enum usb_interface_condition condition;         /* state of binding */
        unsigned is_active:1;           /* the interface is not suspended */
        unsigned sysfs_files_created:1; /* the sysfs attributes exist */
+       unsigned ep_devs_created:1;     /* endpoint "devices" exist */
        unsigned unregistering:1;       /* unregistration is in progress */
        unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
        unsigned needs_altsetting0:1;   /* switch to altsetting 0 is pending */