usb/acpi: Bind ACPI node to USB port, not usb_device.
authorLan Tianyu <tianyu.lan@intel.com>
Wed, 5 Sep 2012 05:44:33 +0000 (13:44 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 10 Sep 2012 20:02:02 +0000 (13:02 -0700)
In the ACPI DSDT table, only usb root hub and usb ports are ACPI device
nodes.  Originally, we bound the usb port's ACPI node to the usb device
attached to the port.  However, we want to access those ACPI port
methods when the port is empty, and there's no usb_device associated
with that port.

Now that the usb port is a real device, we can bind the port's ACPI node
to struct usb_port instead.

Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hub.c
drivers/usb/core/usb-acpi.c
drivers/usb/core/usb.h

index cdbade14899575e40957300863129df240bc7466..3341b55f495c6930fdf8a5c0331525411ed2d00d 100644 (file)
@@ -5077,3 +5077,21 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev,
        return hub->ports[port1 - 1]->child;
 }
 EXPORT_SYMBOL_GPL(usb_hub_find_child);
+
+#ifdef CONFIG_ACPI
+/**
+ * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ *
+ * Return port's acpi handle if successful, NULL if params are
+ * invaild.
+ */
+acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+       int port1)
+{
+       struct usb_hub *hub = hdev_to_hub(hdev);
+
+       return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+}
+#endif
index 8947b203d5a4dd1cbe2dcc190a9d3b19363bf75b..47197bf0b28db2b84101aea2bda5d19d9cc13e79 100644 (file)
@@ -70,22 +70,59 @@ static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
 static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
 {
        struct usb_device *udev;
-       struct device *parent;
        acpi_handle *parent_handle;
+       int port_num;
 
-       if (!is_usb_device(dev))
-               return -ENODEV;
-
-       udev = to_usb_device(dev);
-       parent = dev->parent;
-       parent_handle = DEVICE_ACPI_HANDLE(parent);
-
-       if (!parent_handle)
-               return -ENODEV;
-
-       *handle = acpi_get_child(parent_handle, udev->portnum);
-
-       if (!*handle)
+       /*
+        * In the ACPI DSDT table, only usb root hub and usb ports are
+        * acpi device nodes. The hierarchy like following.
+        * Device (EHC1)
+        *      Device (HUBN)
+        *              Device (PR01)
+        *                      Device (PR11)
+        *                      Device (PR12)
+        *                      Device (PR13)
+        *                      ...
+        * So all binding process is divided into two parts. binding
+        * root hub and usb ports.
+        */
+       if (is_usb_device(dev)) {
+               udev = to_usb_device(dev);
+               if (udev->parent)
+                       return -ENODEV;
+               /* root hub's parent is the usb hcd. */
+               parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+               *handle = acpi_get_child(parent_handle, udev->portnum);
+               if (!*handle)
+                       return -ENODEV;
+               return 0;
+       } else if (is_usb_port(dev)) {
+               sscanf(dev_name(dev), "port%d", &port_num);
+               /* Get the struct usb_device point of port's hub */
+               udev = to_usb_device(dev->parent->parent);
+
+               /*
+                * The root hub ports' parent is the root hub. The non-root-hub
+                * ports' parent is the parent hub port which the hub is
+                * connected to.
+                */
+               if (!udev->parent) {
+                       *handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
+                               port_num);
+                       if (!*handle)
+                               return -ENODEV;
+               } else {
+                       parent_handle =
+                               usb_get_hub_port_acpi_handle(udev->parent,
+                               udev->portnum);
+                       if (!parent_handle)
+                               return -ENODEV;
+
+                       *handle = acpi_get_child(parent_handle, port_num);
+                       if (!*handle)
+                               return -ENODEV;
+               }
+       } else
                return -ENODEV;
 
        /*
@@ -102,7 +139,7 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
 
 static struct acpi_bus_type usb_acpi_bus = {
        .bus = &usb_bus_type,
-       .find_bridge = NULL,
+       .find_bridge = usb_acpi_find_device,
        .find_device = usb_acpi_find_device,
 };
 
index acb103c5c391190e83ee66e3d837f80ad14f72e8..1633f6e99e35e6bebcf3dfc83d63d53a3781afe2 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/pm.h>
+#include <linux/acpi.h>
 
 struct dev_state;
 
@@ -115,6 +116,7 @@ extern struct bus_type usb_bus_type;
 extern struct device_type usb_device_type;
 extern struct device_type usb_if_device_type;
 extern struct device_type usb_ep_device_type;
+extern struct device_type usb_port_device_type;
 extern struct usb_device_driver usb_generic_driver;
 
 static inline int is_usb_device(const struct device *dev)
@@ -132,6 +134,11 @@ static inline int is_usb_endpoint(const struct device *dev)
        return dev->type == &usb_ep_device_type;
 }
 
+static inline int is_usb_port(const struct device *dev)
+{
+       return dev->type == &usb_port_device_type;
+}
+
 /* Do the same for device drivers and interface drivers. */
 
 static inline int is_usb_device_driver(struct device_driver *drv)
@@ -166,6 +173,8 @@ extern void usb_notify_remove_bus(struct usb_bus *ubus);
 #ifdef CONFIG_ACPI
 extern int usb_acpi_register(void);
 extern void usb_acpi_unregister(void);
+extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+       int port1);
 #else
 static inline int usb_acpi_register(void) { return 0; };
 static inline void usb_acpi_unregister(void) { };