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-acpi.c | 60 +++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 26 deletions(-) (limited to 'drivers/usb/core/usb-acpi.c') diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 5ca4070b1f3..f91ef022006 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -17,7 +17,7 @@ #include #include -#include "usb.h" +#include "hub.h" /** * usb_acpi_power_manageable - check whether usb port has @@ -55,13 +55,18 @@ EXPORT_SYMBOL_GPL(usb_acpi_power_manageable); */ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) { + struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + struct usb_port *port_dev; acpi_handle port_handle; unsigned char state; int port1 = index + 1; int error = -EINVAL; - port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev, - port1); + if (!hub) + return -ENODEV; + port_dev = hub->ports[port1 - 1]; + + port_handle = (acpi_handle) usb_get_hub_port_acpi_handle(hdev, port1); if (!port_handle) return error; @@ -72,10 +77,9 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) error = acpi_bus_set_power(port_handle, state); if (!error) - dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n", - port1, enable); + dev_dbg(&port_dev->dev, "acpi: power was set to %d\n", enable); else - dev_dbg(&hdev->dev, "The power of hub port failed to be set\n"); + dev_dbg(&port_dev->dev, "acpi: power failed to be set\n"); return error; } @@ -84,12 +88,17 @@ 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) { - acpi_status status; + enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *upc; + 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 * user visible and _UPC indicates whether it is connectable. If @@ -112,13 +121,12 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev, if (upc->package.elements[0].integer.value) if (pld->user_visible) - usb_set_hub_port_connect_type(hdev, port1, - USB_PORT_CONNECT_TYPE_HOT_PLUG); + connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG; else - usb_set_hub_port_connect_type(hdev, port1, - USB_PORT_CONNECT_TYPE_HARD_WIRED); + connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; else if (!pld->user_visible) - usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED); + connect_type = USB_PORT_NOT_USED; + hub->ports[port1 - 1]->connect_type = connect_type; out: ACPI_FREE(pld); @@ -128,9 +136,9 @@ out: static struct acpi_device *usb_acpi_find_companion(struct device *dev) { + int port1; struct usb_device *udev; acpi_handle *parent_handle; - int port_num; /* * In the ACPI DSDT table, only usb root hub and usb ports are @@ -147,16 +155,16 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) */ if (is_usb_device(dev)) { udev = to_usb_device(dev); + port1 = udev->portnum; if (udev->parent) { - enum usb_port_connect_type type; + struct usb_hub *hub; + hub = usb_hub_to_struct_hub(udev->parent); /* * According usb port's connect type to set usb device's * removability. */ - type = usb_get_hub_port_connect_type(udev->parent, - udev->portnum); - switch (type) { + switch (hub->ports[port1 - 1]->connect_type) { case USB_PORT_CONNECT_TYPE_HOT_PLUG: udev->removable = USB_DEVICE_REMOVABLE; break; @@ -173,13 +181,14 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) /* root hub's parent is the usb hcd. */ return acpi_find_child_device(ACPI_COMPANION(dev->parent), - udev->portnum, false); + port1, false); } else if (is_usb_port(dev)) { + struct usb_port *port_dev = to_usb_port(dev); struct acpi_device *adev = NULL; - sscanf(dev_name(dev), "port%d", &port_num); /* Get the struct usb_device point of port's hub */ udev = to_usb_device(dev->parent->parent); + port1 = port_dev->portnum; /* * The root hub ports' parent is the root hub. The non-root-hub @@ -188,12 +197,11 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) */ if (!udev->parent) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); - int raw_port_num; + int raw; - raw_port_num = usb_hcd_find_raw_port_number(hcd, - port_num); + raw = usb_hcd_find_raw_port_number(hcd, port1); adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev), - raw_port_num, false); + raw, false); if (!adev) return NULL; } else { @@ -204,11 +212,11 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) return NULL; acpi_bus_get_device(parent_handle, &adev); - adev = acpi_find_child_device(adev, port_num, false); + adev = acpi_find_child_device(adev, port1, false); if (!adev) return NULL; } - usb_acpi_check_port_connect_type(udev, adev->handle, port_num); + usb_acpi_check_port_connect_type(udev, adev->handle, port1); return adev; } -- cgit v1.2.3-70-g09d2 From a4204ff0bd576fc114357eed70e7c4e776ddf396 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 May 2014 18:08:22 -0700 Subject: usb: cleanup setting udev->removable from port_dev->connect_type Once usb-acpi has set the port's connect type the usb_device's ->removable attribute can be set in the standard location set_usb_port_removable(). This also changes behavior in the case where the firmware says that the port connect type is unknown. In that case just use the default setting determined from the hub descriptor. Note, we no longer pass udev->portnum to acpi_find_child_device() in the root hub case since: 1/ the usb-core sets this to zero 2/ acpi always expects zero ...just pass zero. Suggested-by: Alan Stern Acked-by: Alan Stern Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 22 +++++++++++++++++----- drivers/usb/core/usb-acpi.c | 34 ++++++---------------------------- 2 files changed, 23 insertions(+), 33 deletions(-) (limited to 'drivers/usb/core/usb-acpi.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 653f80c5248..29129250877 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2305,6 +2305,22 @@ static void set_usb_port_removable(struct usb_device *udev) udev->removable = USB_DEVICE_REMOVABLE; else udev->removable = USB_DEVICE_FIXED; + + /* + * Platform firmware may have populated an alternative value for + * removable. If the parent port has a known connect_type use + * that instead. + */ + switch (hub->ports[udev->portnum - 1]->connect_type) { + case USB_PORT_CONNECT_TYPE_HOT_PLUG: + udev->removable = USB_DEVICE_REMOVABLE; + break; + case USB_PORT_CONNECT_TYPE_HARD_WIRED: + udev->removable = USB_DEVICE_FIXED; + break; + default: /* use what was set above */ + break; + } } /** @@ -2374,11 +2390,7 @@ int usb_new_device(struct usb_device *udev) device_enable_async_suspend(&udev->dev); - /* - * check whether the hub marks this port as non-removable. Do it - * now so that platform-specific data can override it in - * device_add() - */ + /* check whether the hub or firmware marks this port as non-removable */ if (udev->parent) set_usb_port_removable(udev); diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index f91ef022006..d3e7e1b4125 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -136,8 +136,8 @@ out: static struct acpi_device *usb_acpi_find_companion(struct device *dev) { - int port1; struct usb_device *udev; + struct acpi_device *adev; acpi_handle *parent_handle; /* @@ -155,40 +155,18 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev) */ if (is_usb_device(dev)) { udev = to_usb_device(dev); - port1 = udev->portnum; - if (udev->parent) { - struct usb_hub *hub; - - hub = usb_hub_to_struct_hub(udev->parent); - /* - * According usb port's connect type to set usb device's - * removability. - */ - switch (hub->ports[port1 - 1]->connect_type) { - case USB_PORT_CONNECT_TYPE_HOT_PLUG: - udev->removable = USB_DEVICE_REMOVABLE; - break; - case USB_PORT_CONNECT_TYPE_HARD_WIRED: - udev->removable = USB_DEVICE_FIXED; - break; - default: - udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN; - break; - } - + if (udev->parent) return NULL; - } - /* root hub's parent is the usb hcd. */ - return acpi_find_child_device(ACPI_COMPANION(dev->parent), - port1, false); + /* root hub is only child (_ADR=0) under its parent, the HC */ + adev = ACPI_COMPANION(dev->parent); + return acpi_find_child_device(adev, 0, false); } else if (is_usb_port(dev)) { struct usb_port *port_dev = to_usb_port(dev); - struct acpi_device *adev = NULL; + int port1 = port_dev->portnum; /* Get the struct usb_device point of port's hub */ udev = to_usb_device(dev->parent->parent); - port1 = port_dev->portnum; /* * The root hub ports' parent is the root hub. The non-root-hub -- 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-acpi.c') 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