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 946fae43d622720521ac17a6876709f2417a5c27..e1710f260b4fee1ad57a3cc41429f232506cd7d5 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 5abdc11be1e5e4a3483d3c3e53a267a338b27c08..756b8d9993fcd58fa539dba8e586aa06c1266d58 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 aadf29f09c45307c0d21ab5917df665e3dadabb1..7943901c641c7653a709dc9b64f2189d807f466f 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 0f0ccf64011449defede47c8000a41f961038065..4cc2456ef3be17fa88b16ac91bb29f1f9f30422e 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 4c98f3975afe5c26700674872878883c00843e50..c0821564a3feaad132423766c95786e2d9904cf2 100644 (file)
@@ -970,6 +970,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
  */
@@ -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 9fb195665fa848a9d8248c423b8c8ab9e34f9905..381eae90c3b74b4ff896c3bba0df08f108a95d33 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 74d0b9990c7325f5ac9d847f81ef1c963744c788..e9d63562325a64aff966f343e59d875dd536ada3 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 */