]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
usb: assign usb3 external hub port peers
authorDan Williams <dan.j.williams@intel.com>
Wed, 21 May 2014 01:08:33 +0000 (18:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 May 2014 23:38:52 +0000 (16:38 -0700)
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 labeling scheme required by the
specification [1].

[1]: usb3 3.1 section 10.3.3

Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/port.c

index 5ecdbf31dfcbfafc626a74202103442cdffc9c8e..9b7496b52f2ab4e9e69bb28806afeec3abbdbc76 100644 (file)
@@ -187,15 +187,18 @@ static void unlink_peers(struct usb_port *left, struct usb_port *right)
        left->peer = NULL;
 }
 
-/* set the default peer port for root hubs */
+/*
+ * Set the default peer port for root hubs, or via the upstream peer
+ * relationship for all other hubs
+ */
 static void find_and_link_peer(struct usb_hub *hub, int port1)
 {
        struct usb_port *port_dev = hub->ports[port1 - 1], *peer;
        struct usb_device *hdev = hub->hdev;
+       struct usb_device *peer_hdev;
+       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->shared_hcd;
 
@@ -203,15 +206,28 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
                        return;
 
                peer_hdev = peer_hcd->self.root_hub;
-               peer_hub = usb_hub_to_struct_hub(peer_hdev);
-               if (!peer_hub || port1 > peer_hdev->maxchild)
+       } 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;
 
-               peer = peer_hub->ports[port1 - 1];
+               upstream = parent_hub->ports[hdev->portnum - 1];
+               if (!upstream || !upstream->peer)
+                       return;
 
-               if (peer)
-                       link_peers(port_dev, peer);
+               peer_hdev = upstream->peer->child;
        }
+
+       peer_hub = usb_hub_to_struct_hub(peer_hdev);
+       if (!peer_hub || port1 > peer_hdev->maxchild)
+               return;
+
+       peer = peer_hub->ports[port1 - 1];
+       if (peer)
+               link_peers(port_dev, peer);
 }
 
 int usb_hub_create_port_device(struct usb_hub *hub, int port1)