Given that root hub port peers are already established, external hub peer
ports can be determined by traversing the device topology:

1/ ascend to the parent hub and find the upstream port_dev

2/ walk ->peer to find the peer port

3/ descend to the peer hub via ->child

4/ find the port with the matching port id

Note that this assumes the port labelling scheme required by the
specification [1].

[1]: usb3 3.1 section 10.3.3

Signed-off-by: Dan Williams <dan.j.willi...@intel.com>
---
 drivers/usb/core/port.c |   22 ++++++++++++++++++++--
 1 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index d57fb01bbc1c..068d495007e1 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -155,11 +155,11 @@ struct device_type usb_port_device_type = {
 static struct usb_port *find_default_peer(struct usb_hub *hub, int port1)
 {
        struct usb_device *hdev = hub->hdev;
+       struct usb_device *peer_hdev;
        struct usb_port *peer = NULL;
+       struct usb_hub *peer_hub;
 
        if (!hdev->parent) {
-               struct usb_hub *peer_hub;
-               struct usb_device *peer_hdev;
                struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
                struct usb_hcd *peer_hcd = hcd->primary_hcd;
 
@@ -170,6 +170,24 @@ static struct usb_port *find_default_peer(struct usb_hub 
*hub, int port1)
                peer_hub = usb_hub_to_struct_hub(peer_hdev);
                if (peer_hub && port1 <= peer_hdev->maxchild)
                        peer = peer_hub->ports[port1 - 1];
+       } else {
+               struct usb_port *upstream;
+               struct usb_device *parent = hdev->parent;
+               struct usb_hub *parent_hub = usb_hub_to_struct_hub(parent);
+
+               if (!parent_hub)
+                       return NULL;
+
+               upstream = parent_hub->ports[hdev->portnum - 1];
+               if (!upstream->peer)
+                       return NULL;
+
+               peer_hdev = upstream->peer->child;
+               peer_hub = usb_hub_to_struct_hub(peer_hdev);
+               if (!peer_hub || port1 > peer_hdev->maxchild)
+                       return NULL;
+
+               peer = peer_hub->ports[port1 - 1];
        }
 
        return peer;

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to