[prev in list] [next in list] [prev in thread] [next in thread]
List: linux-usb
Subject: [PATCH v6 part1 4/8] usb: assign default peer ports for root hubs
From: Dan Williams <dan.j.williams () intel ! com>
Date: 2014-02-28 23:18:21
Message-ID: 20140228231821.15862.43596.stgit () viggo ! jf ! intel ! com
[Download RAW message or body]
Assume that the peer of a superspeed port is the port with the same id
on the shared_hcd root hub. This identification scheme is required of
external hubs by the USB3 spec [1]. However, for root hubs, tier mismatch
may be in effect [2]. Tier mismatch can only be enumerated via platform
firmware. For now, simply perform the nominal association.
Once the root hub is marked as unregistered we block attempts to walk
the ->shared_hcd pointer to find a root-peer port.
[1]: usb 3.1 section 10.3.3
[2]: xhci 1.1 appendix D
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
drivers/usb/core/hcd.c | 8 ++++
drivers/usb/core/hub.h | 2 +
drivers/usb/core/port.c | 90 ++++++++++++++++++++++++++++++++++++++++++++---
drivers/usb/core/usb.h | 3 ++
4 files changed, 97 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 2518c3250750..e380b8a80830 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2778,9 +2778,17 @@ void usb_remove_hcd(struct usb_hcd *hcd)
hcd->state = HC_STATE_QUIESCING;
dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
+
+ /*
+ * Flush + disable peering operations, and atomically update the
+ * hcd state relative to other root hub state
+ * changes/evaluations
+ */
+ mutex_lock(&usb_port_peer_mutex);
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
+ mutex_unlock(&usb_port_peer_mutex);
#ifdef CONFIG_PM_RUNTIME
cancel_work_sync(&hcd->wakeup_work);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index baf5b48b79f7..d51feb68165b 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -81,6 +81,7 @@ struct usb_hub {
* @child: usb device attached to the port
* @dev: generic device interface
* @port_owner: port's owner
+ * @peer: related usb2 and usb3 ports (share the same connector)
* @connect_type: port's connect type
* @portnum: port index num based one
* @power_is_on: port's power state
@@ -90,6 +91,7 @@ struct usb_port {
struct usb_device *child;
struct device dev;
struct dev_state *port_owner;
+ struct usb_port *peer;
enum usb_port_connect_type connect_type;
u8 portnum;
unsigned power_is_on:1;
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index afe0d894c907..d8bea408a2f2 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -21,6 +21,9 @@
#include "hub.h"
+/* synchronize hub-port add/remove and peering operations */
+DEFINE_MUTEX(usb_port_peer_mutex);
+
static const struct attribute_group *port_dev_group[];
static ssize_t connect_type_show(struct device *dev,
@@ -151,9 +154,71 @@ static struct device_driver usb_port_driver = {
.owner = THIS_MODULE,
};
+/*
+ * Set the default peer port for root hubs. Assumes the primary_hcd is
+ * registered first
+ */
+static struct usb_port *find_default_peer(struct usb_hub *hub, int port1)
+{
+ struct usb_device *hdev = hub->hdev;
+ struct usb_port *peer = NULL;
+
+ if (!hub || hub->disconnected)
+ return NULL;
+
+ 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;
+
+ if (!peer_hcd || !peer_hcd->rh_registered
+ || !hcd->rh_registered
+ || hcd == peer_hcd)
+ return NULL;
+
+ peer_hdev = peer_hcd->self.root_hub;
+ peer_hub = usb_hub_to_struct_hub(peer_hdev);
+ if (peer_hub && port1 <= peer_hdev->maxchild)
+ peer = peer_hub->ports[port1 - 1];
+ }
+
+ return peer;
+}
+
+static void link_peers(struct usb_port *left, struct usb_port *right)
+{
+ if (left->peer == right && right->peer == left)
+ return;
+
+ if (left->peer || right->peer) {
+ struct usb_port *lpeer = left->peer;
+ struct usb_port *rpeer = right->peer;
+
+ WARN(1, "failed to peer %s and %s (%s -> %p) (%s -> %p)\n",
+ dev_name(&left->dev), dev_name(&right->dev),
+ dev_name(&left->dev), lpeer,
+ dev_name(&right->dev), rpeer);
+ return;
+ }
+
+ left->peer = right;
+ right->peer = left;
+}
+
+static void unlink_peers(struct usb_port *left, struct usb_port *right)
+{
+ WARN(right->peer != left || left->peer != right,
+ "%s and %s are not peers?\n",
+ dev_name(&left->dev), dev_name(&right->dev));
+
+ right->peer = NULL;
+ left->peer = NULL;
+}
+
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{
- struct usb_port *port_dev = NULL;
+ struct usb_port *port_dev, *peer = NULL;
int retval;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
@@ -162,7 +227,6 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
goto exit;
}
- hub->ports[port1 - 1] = port_dev;
port_dev->portnum = port1;
port_dev->power_is_on = true;
port_dev->dev.parent = hub->intfdev;
@@ -171,7 +235,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port_dev->dev.driver = &usb_port_driver;
dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
port1);
+
+ mutex_lock(&usb_port_peer_mutex);
+ hub->ports[port1 - 1] = port_dev;
retval = device_register(&port_dev->dev);
+ if (retval == 0)
+ peer = find_default_peer(hub, port1);
+ if (peer)
+ link_peers(port_dev, peer);
+ mutex_unlock(&usb_port_peer_mutex);
if (retval)
goto error_register;
@@ -196,9 +268,15 @@ exit:
return retval;
}
-void usb_hub_remove_port_device(struct usb_hub *hub,
- int port1)
+void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
{
- device_unregister(&hub->ports[port1 - 1]->dev);
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_port *peer;
+
+ mutex_lock(&usb_port_peer_mutex);
+ peer = port_dev->peer;
+ if (peer)
+ unlink_peers(port_dev, peer);
+ device_unregister(&port_dev->dev);
+ mutex_unlock(&usb_port_peer_mutex);
}
-
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 28f272884c0d..8a1acf59d498 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -157,6 +157,9 @@ static inline int is_usb_device_driver(struct device_driver *drv)
for_devices;
}
+/* port peering lock shared between port, hub, and hcd */
+extern struct mutex usb_port_peer_mutex;
+
/* for labeling diagnostics */
extern const char *usbcore_name;
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic