mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 13:11:40 +00:00
usb/acpi: Store info on device removability.
In the upcoming USB port power off patches, we need to know whether a USB port can ever see a disconnect event. Often USB ports are internal to a system, and users can't disconnect USB devices from that port. Sometimes those ports will remain empty, because the OEM chose not to connect an internal USB device to that port. According to ACPI Spec 9.13, PLD indicates whether USB port is user visible and _UPC indicates whether a USB device can be connected to the USB port (we'll call this "connectible"). Here's a matrix of the possible combinations: Visible Connectible Name Example ------------------------------------------------------------------------- Yes No Unknown (Invalid state.) Yes Yes Hot-plug USB ports on the outside of a laptop. A user could freely connect and disconnect USB devices. No Yes Hard-wired A USB modem hard-wired to a port on the inside of a laptop. No No Not used The port is internal to the system and will remain empty. Represent each of these four states with an enum usb_port_connect_type. The four states are USB_PORT_CONNECT_TYPE_UNKNOWN, USB_PORT_CONNECT_TYPE_HOT_PLUG, USB_PORT_CONNECT_TYPE_HARD_WIRED, and USB_PORT_NOT_USED. When we get the USB port's acpi_handle, store the state in connect_type in struct usb_port. Signed-off-by: Lan Tianyu <tianyu.lan@intel.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
d557542421
commit
05f916894a
@ -43,6 +43,7 @@ struct usb_port {
|
||||
struct usb_device *child;
|
||||
struct device dev;
|
||||
struct dev_state *port_owner;
|
||||
enum usb_port_connect_type connect_type;
|
||||
};
|
||||
|
||||
struct usb_hub {
|
||||
@ -5078,6 +5079,36 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hub_find_child);
|
||||
|
||||
/**
|
||||
* usb_set_hub_port_connect_type - set hub port connect type.
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @port1: port num of the port
|
||||
* @type: connect type of the port
|
||||
*/
|
||||
void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
|
||||
enum usb_port_connect_type type)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
hub->ports[port1 - 1]->connect_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_get_hub_port_connect_type - Get the port's connect type
|
||||
* @hdev: USB device belonging to the usb hub
|
||||
* @port1: port num of the port
|
||||
*
|
||||
* Return connect type of the port and if input params are
|
||||
* invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN.
|
||||
*/
|
||||
enum usb_port_connect_type
|
||||
usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
|
||||
{
|
||||
struct usb_hub *hub = hdev_to_hub(hdev);
|
||||
|
||||
return hub->ports[port1 - 1]->connect_type;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/**
|
||||
* usb_get_hub_port_acpi_handle - Get the usb port's acpi handle
|
||||
|
@ -19,20 +19,29 @@
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
|
||||
static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
|
||||
acpi_handle handle, int port1)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *upc;
|
||||
struct acpi_pld pld;
|
||||
int ret = 0;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
|
||||
|
||||
/*
|
||||
* Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
|
||||
* user visible and _UPC indicates whether it is connectable. If
|
||||
* the port was visible and connectable, it could be freely connected
|
||||
* and disconnected with USB devices. If no visible and connectable,
|
||||
* 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;
|
||||
@ -40,33 +49,20 @@ static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
|
||||
}
|
||||
|
||||
if (upc->package.elements[0].integer.value)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
if (pld.user_visible)
|
||||
usb_set_hub_port_connect_type(hdev, port1,
|
||||
USB_PORT_CONNECT_TYPE_HOT_PLUG);
|
||||
else
|
||||
usb_set_hub_port_connect_type(hdev, port1,
|
||||
USB_PORT_CONNECT_TYPE_HARD_WIRED);
|
||||
else if (!pld.user_visible)
|
||||
usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
|
||||
|
||||
out:
|
||||
kfree(upc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_pld pld;
|
||||
|
||||
status = acpi_get_physical_device_location(handle, &pld);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
if (pld.user_visible)
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
@ -88,8 +84,30 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
*/
|
||||
if (is_usb_device(dev)) {
|
||||
udev = to_usb_device(dev);
|
||||
if (udev->parent)
|
||||
if (udev->parent) {
|
||||
enum usb_port_connect_type type;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* root hub's parent is the usb hcd. */
|
||||
parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
|
||||
*handle = acpi_get_child(parent_handle, udev->portnum);
|
||||
@ -122,18 +140,10 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
}
|
||||
usb_acpi_check_port_connect_type(udev, *handle, port_num);
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* PLD will tell us whether a port is removable to the user or
|
||||
* not. If we don't get an answer from PLD (it's not present
|
||||
* or it's malformed) then try to infer it from UPC. If a
|
||||
* device isn't connectable then it's probably not removable.
|
||||
*/
|
||||
if (usb_acpi_check_pld(udev, *handle) != 0)
|
||||
usb_acpi_check_upc(udev, *handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -169,6 +169,10 @@ 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);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern int usb_acpi_register(void);
|
||||
|
@ -384,6 +384,13 @@ enum usb_device_removable {
|
||||
USB_DEVICE_FIXED,
|
||||
};
|
||||
|
||||
enum usb_port_connect_type {
|
||||
USB_PORT_CONNECT_TYPE_UNKNOWN = 0,
|
||||
USB_PORT_CONNECT_TYPE_HOT_PLUG,
|
||||
USB_PORT_CONNECT_TYPE_HARD_WIRED,
|
||||
USB_PORT_NOT_USED,
|
||||
};
|
||||
|
||||
/*
|
||||
* USB 3.0 Link Power Management (LPM) parameters.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user