From d99f6b41308779244662109a9c2bad09a82e8ac6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 18:08:17 -0700 Subject: usb: rename usb_port device objects The current port name "portX" is ambiguous. Before adding more port messages rename ports to "-portX" This is an ABI change, but the suspicion is that it will go unnoticed as the port power control implementation has been broken since its introduction. If however, someone was relying on the old name we can add sysfs links from the old name to the new name. Additionally, it unifies/simplifies port dev_printk messages and modifies instances of: dev_XXX(hub->intfdev, ..."port %d"... dev_XXX(&hdev->dev, ..."port%d"... into: dev_XXX(&port_dev->dev, ... Now that the names are unique usb_port devices it would be nice if they could be included in /sys/bus/usb. However, it turns out that this breaks 'lsusb -t'. For now, create a dummy port driver so that print messages are prefixed "usb 1-1-port3" rather than the subsystem-ambiguous " 1-1-port3". Finally, it corrects an odd usage of sscanf("port%d") in usb-acpi.c. Suggested-by: Alan Stern Acked-by: Alan Stern Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 75bf649da82..69bfc253a7b 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -175,10 +175,6 @@ extern void usb_notify_add_device(struct usb_device *udev); extern void usb_notify_remove_device(struct usb_device *udev); extern void usb_notify_add_bus(struct usb_bus *ubus); extern void usb_notify_remove_bus(struct usb_bus *ubus); -extern enum usb_port_connect_type - usb_get_hub_port_connect_type(struct usb_device *hdev, int port1); -extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, - enum usb_port_connect_type type); extern void usb_hub_adjust_deviceremovable(struct usb_device *hdev, struct usb_hub_descriptor *desc); -- cgit v1.2.3-70-g09d2 From d8521afe35862f4fbe3ccd6ca37897c0a304edf3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 18:08:28 -0700 Subject: usb: assign default peer ports for root hubs 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. A new lock 'usb_port_peer_mutex' is introduced to synchronize port device add/remove with peer lookups. It protects peering against changes to hcd->shared_hcd, hcd->self.root_hub, hdev->maxchild, and port_dev->child pointers. [1]: usb 3.1 section 10.3.3 [2]: xhci 1.1 appendix D Cc: Alan Stern [alan: usb_port_peer_mutex locking scheme] Acked-by: Alan Stern Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 43 +++++++++++++++++++++++------ drivers/usb/core/hub.c | 42 ++++++++++++++++++---------- drivers/usb/core/hub.h | 2 ++ drivers/usb/core/port.c | 73 +++++++++++++++++++++++++++++++++++++++++++++---- drivers/usb/core/usb.h | 1 + 5 files changed, 134 insertions(+), 27 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 174eb857a6b..b81407518fd 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2458,11 +2458,13 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, mutex_init(hcd->bandwidth_mutex); dev_set_drvdata(dev, hcd); } else { + mutex_lock(&usb_port_peer_mutex); hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; hcd->primary_hcd = primary_hcd; primary_hcd->primary_hcd = primary_hcd; hcd->shared_hcd = primary_hcd; primary_hcd->shared_hcd = hcd; + mutex_unlock(&usb_port_peer_mutex); } kref_init(&hcd->kref); @@ -2514,18 +2516,25 @@ EXPORT_SYMBOL_GPL(usb_create_hcd); * deallocated. * * Make sure to only deallocate the bandwidth_mutex when the primary HCD is - * freed. When hcd_release() is called for the non-primary HCD, set the - * primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be - * freed shortly). + * freed. When hcd_release() is called for either hcd in a peer set + * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to + * block new peering attempts */ -static void hcd_release (struct kref *kref) +static void hcd_release(struct kref *kref) { struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); + mutex_lock(&usb_port_peer_mutex); if (usb_hcd_is_primary_hcd(hcd)) kfree(hcd->bandwidth_mutex); - else - hcd->shared_hcd->shared_hcd = NULL; + if (hcd->shared_hcd) { + struct usb_hcd *peer = hcd->shared_hcd; + + peer->shared_hcd = NULL; + if (peer->primary_hcd == hcd) + peer->primary_hcd = NULL; + } + mutex_unlock(&usb_port_peer_mutex); kfree(hcd); } @@ -2593,6 +2602,21 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd, return 0; } +/* + * Before we free this root hub, flush in-flight peering attempts + * and disable peer lookups + */ +static void usb_put_invalidate_rhdev(struct usb_hcd *hcd) +{ + struct usb_device *rhdev; + + mutex_lock(&usb_port_peer_mutex); + rhdev = hcd->self.root_hub; + hcd->self.root_hub = NULL; + mutex_unlock(&usb_port_peer_mutex); + usb_put_dev(rhdev); +} + /** * usb_add_hcd - finish generic HCD structure initialization and register * @hcd: the usb_hcd structure to initialize @@ -2653,7 +2677,9 @@ int usb_add_hcd(struct usb_hcd *hcd, retval = -ENOMEM; goto err_allocate_root_hub; } + mutex_lock(&usb_port_peer_mutex); hcd->self.root_hub = rhdev; + mutex_unlock(&usb_port_peer_mutex); switch (hcd->speed) { case HCD_USB11: @@ -2762,7 +2788,7 @@ err_hcd_driver_start: err_request_irq: err_hcd_driver_setup: err_set_rh_speed: - usb_put_dev(hcd->self.root_hub); + usb_put_invalidate_rhdev(hcd); err_allocate_root_hub: usb_deregister_bus(&hcd->self); err_register_bus: @@ -2842,7 +2868,6 @@ void usb_remove_hcd(struct usb_hcd *hcd) free_irq(hcd->irq, hcd); } - usb_put_dev(hcd->self.root_hub); usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); if (hcd->remove_phy && hcd->phy) { @@ -2850,6 +2875,8 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_put_phy(hcd->phy); hcd->phy = NULL; } + + usb_put_invalidate_rhdev(hcd); } EXPORT_SYMBOL_GPL(usb_remove_hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 29129250877..5a909ba6fb6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -55,6 +55,9 @@ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); static struct task_struct *khubd_task; +/* synchronize hub-port add/remove and peering operations */ +DEFINE_MUTEX(usb_port_peer_mutex); + /* cycle leds on hubs that aren't blinking for attention */ static bool blinkenlights = 0; module_param (blinkenlights, bool, S_IRUGO); @@ -1323,6 +1326,7 @@ static int hub_configure(struct usb_hub *hub, char *message = "out of memory"; unsigned unit_load; unsigned full_load; + unsigned maxchild; hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); if (!hub->buffer) { @@ -1361,12 +1365,11 @@ static int hub_configure(struct usb_hub *hub, goto fail; } - hdev->maxchild = hub->descriptor->bNbrPorts; - dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, - (hdev->maxchild == 1) ? "" : "s"); + maxchild = hub->descriptor->bNbrPorts; + dev_info(hub_dev, "%d port%s detected\n", maxchild, + (maxchild == 1) ? "" : "s"); - hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *), - GFP_KERNEL); + hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL); if (!hub->ports) { ret = -ENOMEM; goto fail; @@ -1387,11 +1390,11 @@ static int hub_configure(struct usb_hub *hub, int i; char portstr[USB_MAXCHILDREN + 1]; - for (i = 0; i < hdev->maxchild; i++) + for (i = 0; i < maxchild; i++) portstr[i] = hub->descriptor->u.hs.DeviceRemovable [((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; - portstr[hdev->maxchild] = 0; + portstr[maxchild] = 0; dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr); } else dev_dbg(hub_dev, "standalone hub\n"); @@ -1503,7 +1506,7 @@ static int hub_configure(struct usb_hub *hub, if (hcd->power_budget > 0) hdev->bus_mA = hcd->power_budget; else - hdev->bus_mA = full_load * hdev->maxchild; + hdev->bus_mA = full_load * maxchild; if (hdev->bus_mA >= full_load) hub->mA_per_port = full_load; else { @@ -1518,7 +1521,7 @@ static int hub_configure(struct usb_hub *hub, hub->descriptor->bHubContrCurrent); hub->limited_power = 1; - if (remaining < hdev->maxchild * unit_load) + if (remaining < maxchild * unit_load) dev_warn(hub_dev, "insufficient power available " "to use all downstream ports\n"); @@ -1586,15 +1589,19 @@ static int hub_configure(struct usb_hub *hub, if (hub->has_indicators && blinkenlights) hub->indicator[0] = INDICATOR_CYCLE; - for (i = 0; i < hdev->maxchild; i++) { + mutex_lock(&usb_port_peer_mutex); + for (i = 0; i < maxchild; i++) { ret = usb_hub_create_port_device(hub, i + 1); if (ret < 0) { dev_err(hub->intfdev, "couldn't create port%d device.\n", i + 1); - hdev->maxchild = i; - goto fail_keep_maxchild; + break; } } + hdev->maxchild = i; + mutex_unlock(&usb_port_peer_mutex); + if (ret < 0) + goto fail; usb_hub_adjust_deviceremovable(hdev, hub->descriptor); @@ -1602,8 +1609,6 @@ static int hub_configure(struct usb_hub *hub, return 0; fail: - hdev->maxchild = 0; -fail_keep_maxchild: dev_err (hub_dev, "config failed, %s (err %d)\n", message, ret); /* hub_disconnect() frees urb and descriptor */ @@ -1639,6 +1644,8 @@ static void hub_disconnect(struct usb_interface *intf) hub->error = 0; hub_quiesce(hub, HUB_DISCONNECT); + mutex_lock(&usb_port_peer_mutex); + /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); port1 = hdev->maxchild; @@ -1649,6 +1656,8 @@ static void hub_disconnect(struct usb_interface *intf) for (; port1 > 0; --port1) usb_hub_remove_port_device(hub, port1); + mutex_unlock(&usb_port_peer_mutex); + if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; @@ -4608,6 +4617,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, */ status = 0; + mutex_lock(&usb_port_peer_mutex); + /* We mustn't add new devices if the parent hub has * been disconnected; we would race with the * recursively_mark_NOTATTACHED() routine. @@ -4618,14 +4629,17 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, else port_dev->child = udev; spin_unlock_irq(&device_state_lock); + mutex_unlock(&usb_port_peer_mutex); /* Run it through the hoops (find a driver, etc) */ if (!status) { status = usb_new_device(udev); if (status) { + mutex_lock(&usb_port_peer_mutex); spin_lock_irq(&device_state_lock); port_dev->child = NULL; spin_unlock_irq(&device_state_lock); + mutex_unlock(&usb_port_peer_mutex); } } diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 4bd72dd303d..fcad5f9d12f 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -82,6 +82,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 @@ -91,6 +92,7 @@ struct usb_port { struct usb_device *child; struct device dev; struct usb_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 6a8999728cb..5ecdbf31dfc 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -157,9 +157,66 @@ static struct device_driver usb_port_driver = { .owner = THIS_MODULE, }; +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; +} + +/* set the default peer port for root 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; + + 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; + + if (!peer_hcd) + return; + + peer_hdev = peer_hcd->self.root_hub; + 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) { - struct usb_port *port_dev = NULL; + struct usb_port *port_dev; int retval; port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); @@ -181,6 +238,8 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) if (retval) goto error_register; + find_and_link_peer(hub, port1); + pm_runtime_set_active(&port_dev->dev); /* @@ -203,9 +262,13 @@ 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; + peer = port_dev->peer; + if (peer) + unlink_peers(port_dev, peer); + device_unregister(&port_dev->dev); +} diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 69bfc253a7b..6afa738b5ba 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -119,6 +119,7 @@ static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) #endif extern struct bus_type usb_bus_type; +extern struct mutex usb_port_peer_mutex; extern struct device_type usb_device_type; extern struct device_type usb_if_device_type; extern struct device_type usb_ep_device_type; -- cgit v1.2.3-70-g09d2 From 3bfd659baec822f54e4acb0734669e671d853a35 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 18:08:40 -0700 Subject: usb: find internal hub tier mismatch via acpi ACPI identifies peer ports by setting their 'group_token' and 'group_position' _PLD data to the same value. If a platform has tier mismatch [1] , ACPI can override the default (USB3 defined) peer port association for internal hubs. External hubs follow the default peer association scheme. Location data is cached as an opaque cookie in usb_port_location data. Note that we only consider the group_token and group_position attributes from the _PLD data as ACPI specifies that group_token is a unique identifier. When we find port location data for a port then we assume that the firmware will also describe its peer port. This allows the implementation to only ever set the peer once. This leads to a question about what happens when a pm runtime event occurs while the peer associations are still resolving. Since we only ever set the peer information once, a USB3 port needs to be prevented from suspending while its ->peer pointer is NULL (implemented in a subsequent patch). There is always the possibility that firmware mis-identifies the ports, but there is not much the kernel can do in that case. [1]: xhci 1.1 appendix D figure 131 [2]: acpi 5 section 6.1.8 [alan]: don't do default peering when acpi data present Suggested-by: Alan Stern Acked-by: Alan Stern Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.h | 2 ++ drivers/usb/core/port.c | 56 +++++++++++++++++++++++++++++++++++++++++---- drivers/usb/core/usb-acpi.c | 41 ++++++++++++++++++--------------- drivers/usb/core/usb.h | 6 +++++ 4 files changed, 83 insertions(+), 22 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index fcad5f9d12f..048c797f394 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -84,6 +84,7 @@ struct usb_hub { * @port_owner: port's owner * @peer: related usb2 and usb3 ports (share the same connector) * @connect_type: port's connect type + * @location: opaque representation of platform connector location * @portnum: port index num based one * @power_is_on: port's power state * @did_runtime_put: port has done pm_runtime_put(). @@ -94,6 +95,7 @@ struct usb_port { struct usb_dev_state *port_owner; struct usb_port *peer; enum usb_port_connect_type connect_type; + usb_port_location_t location; u8 portnum; unsigned power_is_on:1; unsigned did_runtime_put:1; diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 9b7496b52f2..aea54e8dfe4 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -188,8 +188,42 @@ static void unlink_peers(struct usb_port *left, struct usb_port *right) } /* - * Set the default peer port for root hubs, or via the upstream peer - * relationship for all other hubs + * For each usb hub device in the system check to see if it is in the + * peer domain of the given port_dev, and if it is check to see if it + * has a port that matches the given port by location + */ +static int match_location(struct usb_device *peer_hdev, void *p) +{ + int port1; + struct usb_hcd *hcd, *peer_hcd; + struct usb_port *port_dev = p, *peer; + struct usb_hub *peer_hub = usb_hub_to_struct_hub(peer_hdev); + struct usb_device *hdev = to_usb_device(port_dev->dev.parent->parent); + + if (!peer_hub) + return 0; + + hcd = bus_to_hcd(hdev->bus); + peer_hcd = bus_to_hcd(peer_hdev->bus); + /* peer_hcd is provisional until we verify it against the known peer */ + if (peer_hcd != hcd->shared_hcd) + return 0; + + for (port1 = 1; port1 <= peer_hdev->maxchild; port1++) { + peer = peer_hub->ports[port1 - 1]; + if (peer && peer->location == port_dev->location) { + link_peers(port_dev, peer); + return 1; /* done */ + } + } + + return 0; +} + +/* + * Find the peer port either via explicit platform firmware "location" + * data, the peer hcd for root hubs, or the upstream peer relationship + * for all other hubs. */ static void find_and_link_peer(struct usb_hub *hub, int port1) { @@ -198,7 +232,17 @@ static void find_and_link_peer(struct usb_hub *hub, int port1) struct usb_device *peer_hdev; struct usb_hub *peer_hub; - if (!hdev->parent) { + /* + * If location data is available then we can only peer this port + * by a location match, not the default peer (lest we create a + * situation where we need to go back and undo a default peering + * when the port is later peered by location data) + */ + if (port_dev->location) { + /* we link the peer in match_location() if found */ + usb_for_each_dev(port_dev, match_location); + return; + } else if (!hdev->parent) { struct usb_hcd *hcd = bus_to_hcd(hdev->bus); struct usb_hcd *peer_hcd = hcd->shared_hcd; @@ -225,8 +269,12 @@ static void find_and_link_peer(struct usb_hub *hub, int port1) if (!peer_hub || port1 > peer_hdev->maxchild) return; + /* + * we found a valid default peer, last check is to make sure it + * does not have location data + */ peer = peer_hub->ports[port1 - 1]; - if (peer) + if (peer && peer->location == 0) link_peers(port_dev, peer); } diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index d3e7e1b4125..2776cfe64c0 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -85,19 +85,13 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) } EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); -static int usb_acpi_check_port_connect_type(struct usb_device *hdev, - acpi_handle handle, int port1) +static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle, + struct acpi_pld_info *pld) { enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct usb_hub *hub = usb_hub_to_struct_hub(hdev); - struct acpi_pld_info *pld; union acpi_object *upc; acpi_status status; - int ret = 0; - - if (!hub) - return 0; /* * According to ACPI Spec 9.13. PLD indicates whether usb port is @@ -107,15 +101,10 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev, * a usb device is directly hard-wired to the port. If no visible and * no connectable, the port would be not used. */ - status = acpi_get_physical_device_location(handle, &pld); - if (ACPI_FAILURE(status)) - return -ENODEV; - status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); upc = buffer.pointer; if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4) { - ret = -EINVAL; goto out; } @@ -126,14 +115,18 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev, connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; else if (!pld->user_visible) connect_type = USB_PORT_NOT_USED; - hub->ports[port1 - 1]->connect_type = connect_type; - out: - ACPI_FREE(pld); kfree(upc); - return ret; + return connect_type; } + +/* + * Private to usb-acpi, all the core needs to know is that + * port_dev->location is non-zero when it has been set by the firmware. + */ +#define USB_ACPI_LOCATION_VALID (1 << 31) + static struct acpi_device *usb_acpi_find_companion(struct device *dev) { struct usb_device *udev; @@ -164,6 +157,9 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) } else if (is_usb_port(dev)) { struct usb_port *port_dev = to_usb_port(dev); int port1 = port_dev->portnum; + struct acpi_pld_info *pld; + acpi_handle *handle; + acpi_status status; /* Get the struct usb_device point of port's hub */ udev = to_usb_device(dev->parent->parent); @@ -194,7 +190,16 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) if (!adev) return NULL; } - usb_acpi_check_port_connect_type(udev, adev->handle, port1); + handle = adev->handle; + status = acpi_get_physical_device_location(handle, &pld); + if (ACPI_FAILURE(status) || !pld) + return adev; + + port_dev->location = USB_ACPI_LOCATION_VALID + | pld->group_token << 8 | pld->group_position; + port_dev->connect_type = usb_acpi_get_connect_type(handle, pld); + ACPI_FREE(pld); + return adev; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 6afa738b5ba..98dc08e1344 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -171,6 +171,12 @@ extern void usbfs_conn_disc_event(void); extern int usb_devio_init(void); extern void usb_devio_cleanup(void); +/* + * Firmware specific cookie identifying a port's location. '0' == no location + * data available + */ +typedef u32 usb_port_location_t; + /* internal notify stuff */ extern void usb_notify_add_device(struct usb_device *udev); extern void usb_notify_remove_device(struct usb_device *udev); -- cgit v1.2.3-70-g09d2 From 7e73be227b1510a2ba1391185be7cc357e2226ef Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 18:09:31 -0700 Subject: usb: hub_handle_remote_wakeup() depends on CONFIG_PM_RUNTIME=y Per Alan: "You mean from within hub_handle_remote_wakeup()? That routine will never get called if CONFIG_PM_RUNTIME isn't enabled, because khubd never sees wakeup requests if they arise during system suspend. In fact, that routine ought to go inside the "#ifdef CONFIG_PM_RUNTIME" portion of hub.c, along with the other suspend/resume code." Suggested-by: Alan Stern Acked-by: Alan Stern Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 90 +++++++++++++++++++++++++++----------------------- drivers/usb/core/usb.h | 5 --- 2 files changed, 49 insertions(+), 46 deletions(-) (limited to 'drivers/usb/core/usb.h') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d43054e8e25..28f5bbae35e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3348,6 +3348,55 @@ int usb_remote_wakeup(struct usb_device *udev) return status; } +/* Returns 1 if there was a remote wakeup and a connect status change. */ +static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, + u16 portstatus, u16 portchange) + __must_hold(&port_dev->status_lock) +{ + struct usb_port *port_dev = hub->ports[port - 1]; + struct usb_device *hdev; + struct usb_device *udev; + int connect_change = 0; + int ret; + + hdev = hub->hdev; + udev = port_dev->child; + if (!hub_is_superspeed(hdev)) { + if (!(portchange & USB_PORT_STAT_C_SUSPEND)) + return 0; + usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); + } else { + if (!udev || udev->state != USB_STATE_SUSPENDED || + (portstatus & USB_PORT_STAT_LINK_STATE) != + USB_SS_PORT_LS_U0) + return 0; + } + + if (udev) { + /* TRSMRCY = 10 msec */ + msleep(10); + + usb_unlock_port(port_dev); + ret = usb_remote_wakeup(udev); + usb_lock_port(port_dev); + if (ret < 0) + connect_change = 1; + } else { + ret = -ENODEV; + hub_port_disable(hub, port, 1); + } + dev_dbg(&port_dev->dev, "resume, status %d\n", ret); + return connect_change; +} + +#else + +static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, + u16 portstatus, u16 portchange) +{ + return 0; +} + #endif static int check_ports_changed(struct usb_hub *hub) @@ -4697,47 +4746,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, usb_lock_port(port_dev); } -/* Returns 1 if there was a remote wakeup and a connect status change. */ -static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, - u16 portstatus, u16 portchange) - __must_hold(&port_dev->status_lock) -{ - struct usb_port *port_dev = hub->ports[port - 1]; - struct usb_device *hdev; - struct usb_device *udev; - int connect_change = 0; - int ret; - - hdev = hub->hdev; - udev = port_dev->child; - if (!hub_is_superspeed(hdev)) { - if (!(portchange & USB_PORT_STAT_C_SUSPEND)) - return 0; - usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND); - } else { - if (!udev || udev->state != USB_STATE_SUSPENDED || - (portstatus & USB_PORT_STAT_LINK_STATE) != - USB_SS_PORT_LS_U0) - return 0; - } - - if (udev) { - /* TRSMRCY = 10 msec */ - msleep(10); - - usb_unlock_port(port_dev); - ret = usb_remote_wakeup(udev); - usb_lock_port(port_dev); - if (ret < 0) - connect_change = 1; - } else { - ret = -ENODEV; - hub_port_disable(hub, port, 1); - } - dev_dbg(&port_dev->dev, "resume, status %d\n", ret); - return connect_change; -} - static void port_event(struct usb_hub *hub, int port1) __must_hold(&port_dev->status_lock) { diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 98dc08e1344..d9d08720c38 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -107,11 +107,6 @@ static inline int usb_autoresume_device(struct usb_device *udev) return 0; } -static inline int usb_remote_wakeup(struct usb_device *udev) -{ - return 0; -} - static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) { return 0; -- cgit v1.2.3-70-g09d2