summaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2012-09-05 13:44:31 +0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-10 12:38:26 -0700
commitfa2a9566257a3b62c328ea5d621ccf5952079dac (patch)
tree0c44a7a978a55f9645f266fc376ce296107a54fc /drivers/usb/core
parent9c2089045b87130e8464ca7e21725959446d7f0c (diff)
usb: make usb port a real device
This patch turns each USB port on a hub into a new struct device. This new device has the USB hub interface device as its parent. The port devices are stored in a new structure (usb_port), and an array of usb_ports are dynamically allocated once we know how many ports the USB hub has. Move the port_owner variable out of usb_hub and into this new structure. A new file will be created in the hub interface sysfs directory, so add documentation. Acked-by: Alan Stern <stern@rowland.harvard.edu> 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>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c91
1 files changed, 75 insertions, 16 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 128a804c42f..87df22eef49 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -39,6 +39,11 @@
#endif
#endif
+struct usb_port {
+ struct device dev;
+ struct dev_state *port_owner;
+};
+
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
@@ -83,7 +88,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
- struct dev_state **port_owners;
+ struct usb_port **ports;
};
static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -156,6 +161,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
+#define to_usb_port(_dev) \
+ container_of(_dev, struct usb_port, dev)
static int usb_reset_and_verify_device(struct usb_device *udev);
@@ -1222,6 +1229,52 @@ static int hub_post_reset(struct usb_interface *intf)
return 0;
}
+static void usb_port_device_release(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ kfree(port_dev);
+}
+
+static void usb_hub_remove_port_device(struct usb_hub *hub,
+ int port1)
+{
+ device_unregister(&hub->ports[port1 - 1]->dev);
+}
+
+struct device_type usb_port_device_type = {
+ .name = "usb_port",
+ .release = usb_port_device_release,
+};
+
+static int usb_hub_create_port_device(struct usb_hub *hub,
+ int port1)
+{
+ struct usb_port *port_dev = NULL;
+ int retval;
+
+ port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ if (!port_dev) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ hub->ports[port1 - 1] = port_dev;
+ port_dev->dev.parent = hub->intfdev;
+ port_dev->dev.type = &usb_port_device_type;
+ dev_set_name(&port_dev->dev, "port%d", port1);
+
+ retval = device_register(&port_dev->dev);
+ if (retval)
+ goto error_register;
+ return 0;
+
+error_register:
+ put_device(&port_dev->dev);
+exit:
+ return retval;
+}
+
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
@@ -1231,7 +1284,7 @@ static int hub_configure(struct usb_hub *hub,
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
- int maxp, ret;
+ int maxp, ret, i;
char *message = "out of memory";
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
@@ -1273,9 +1326,9 @@ static int hub_configure(struct usb_hub *hub,
hdev->children = kzalloc(hdev->maxchild *
sizeof(struct usb_device *), GFP_KERNEL);
- hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
- GFP_KERNEL);
- if (!hdev->children || !hub->port_owners) {
+ hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
+ GFP_KERNEL);
+ if (!hdev->children || !hub->ports) {
ret = -ENOMEM;
goto fail;
}
@@ -1484,6 +1537,11 @@ 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++)
+ if (usb_hub_create_port_device(hub, i + 1) < 0)
+ dev_err(hub->intfdev,
+ "couldn't create port%d device.\n", i + 1);
+
hub_activate(hub, HUB_INIT);
return 0;
@@ -1508,6 +1566,10 @@ static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = interface_to_usbdev(intf);
+ int i;
+
+ for (i = 0; i < hdev->maxchild; i++)
+ usb_hub_remove_port_device(hub, i + 1);
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
@@ -1530,7 +1592,7 @@ static void hub_disconnect(struct usb_interface *intf)
usb_free_urb(hub->urb);
kfree(hdev->children);
- kfree(hub->port_owners);
+ kfree(hub->ports);
kfree(hub->descriptor);
kfree(hub->status);
kfree(hub->buffer);
@@ -1662,7 +1724,7 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
/* This assumes that devices not managed by the hub driver
* will always have maxchild equal to 0.
*/
- *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ *ppowner = &(hdev_to_hub(hdev)->ports[port1 - 1]->port_owner);
return 0;
}
@@ -1699,16 +1761,14 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
{
+ struct usb_hub *hub = hdev_to_hub(hdev);
int n;
- struct dev_state **powner;
- n = find_port_owner(hdev, 1, &powner);
- if (n == 0) {
- for (; n < hdev->maxchild; (++n, ++powner)) {
- if (*powner == owner)
- *powner = NULL;
- }
+ for (n = 0; n < hdev->maxchild; n++) {
+ if (hub->ports[n]->port_owner == owner)
+ hub->ports[n]->port_owner = NULL;
}
+
}
/* The caller must hold udev's lock */
@@ -1719,10 +1779,9 @@ bool usb_device_is_owned(struct usb_device *udev)
if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
return false;
hub = hdev_to_hub(udev->parent);
- return !!hub->port_owners[udev->portnum - 1];
+ return !!hub->ports[udev->portnum - 1]->port_owner;
}
-
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
int i;