summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-05-20 18:08:33 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 16:38:52 -0700
commit8b1ba80c59fb3e77f9e1761480617d5ea9ee159c (patch)
tree120b54fcb3cf3d97d6798bd41d19b2b725554bab /drivers/usb
parentd8521afe35862f4fbe3ccd6ca37897c0a304edf3 (diff)
usb: assign usb3 external hub port peers
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/port.c32
1 files changed, 24 insertions, 8 deletions
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 5ecdbf31dfc..9b7496b52f2 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -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)