diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
| -rw-r--r-- | drivers/usb/core/hub.c | 214 | 
1 files changed, 174 insertions, 40 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 128a804c42f..673ee469626 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -39,6 +39,13 @@  #endif  #endif +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 {  	struct device		*intfdev;	/* the "interface" device */  	struct usb_device	*hdev; @@ -83,7 +90,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 +163,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); @@ -174,7 +183,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)  /* Note that hdev or one of its children must be locked! */  static struct usb_hub *hdev_to_hub(struct usb_device *hdev)  { -	if (!hdev || !hdev->actconfig) +	if (!hdev || !hdev->actconfig || !hdev->maxchild)  		return NULL;  	return usb_get_intfdata(hdev->actconfig->interface[0]);  } @@ -869,8 +878,8 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)  	struct usb_device *hdev = hub->hdev;  	int ret = 0; -	if (hdev->children[port1-1] && set_state) -		usb_set_device_state(hdev->children[port1-1], +	if (hub->ports[port1 - 1]->child && set_state) +		usb_set_device_state(hub->ports[port1 - 1]->child,  				USB_STATE_NOTATTACHED);  	if (!hub->error && !hub_is_superspeed(hub->hdev))  		ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); @@ -1026,7 +1035,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  	 * which ports need attention.  	 */  	for (port1 = 1; port1 <= hdev->maxchild; ++port1) { -		struct usb_device *udev = hdev->children[port1-1]; +		struct usb_device *udev = hub->ports[port1 - 1]->child;  		u16 portstatus, portchange;  		portstatus = portchange = 0; @@ -1191,8 +1200,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)  	if (type != HUB_SUSPEND) {  		/* Disconnect all the children */  		for (i = 0; i < hdev->maxchild; ++i) { -			if (hdev->children[i]) -				usb_disconnect(&hdev->children[i]); +			if (hub->ports[i]->child) +				usb_disconnect(&hub->ports[i]->child);  		}  	} @@ -1222,6 +1231,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 +1286,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); @@ -1271,11 +1326,9 @@ static int hub_configure(struct usb_hub *hub,  	dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,  		(hdev->maxchild == 1) ? "" : "s"); -	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 (!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,7 @@ 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;  	/* Take the hub off the event list and don't let it be added again */  	spin_lock_irq(&hub_event_lock); @@ -1523,14 +1582,16 @@ static void hub_disconnect(struct usb_interface *intf)  	hub_quiesce(hub, HUB_DISCONNECT);  	usb_set_intfdata (intf, NULL); + +	for (i = 0; i < hdev->maxchild; i++) +		usb_hub_remove_port_device(hub, i + 1);  	hub->hdev->maxchild = 0;  	if (hub->hdev->speed == USB_SPEED_HIGH)  		highspeed_hubs--;  	usb_free_urb(hub->urb); -	kfree(hdev->children); -	kfree(hub->port_owners); +	kfree(hub->ports);  	kfree(hub->descriptor);  	kfree(hub->status);  	kfree(hub->buffer); @@ -1617,6 +1678,7 @@ static int  hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)  {  	struct usb_device *hdev = interface_to_usbdev (intf); +	struct usb_hub *hub = hdev_to_hub(hdev);  	/* assert ifno == 0 (part of hub spec) */  	switch (code) { @@ -1630,11 +1692,11 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)  		else {  			info->nports = hdev->maxchild;  			for (i = 0; i < info->nports; i++) { -				if (hdev->children[i] == NULL) +				if (hub->ports[i]->child == NULL)  					info->port[i] = 0;  				else  					info->port[i] = -						hdev->children[i]->devnum; +						hub->ports[i]->child->devnum;  			}  		}  		spin_unlock_irq(&device_state_lock); @@ -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,17 +1779,17 @@ 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)  { +	struct usb_hub *hub = hdev_to_hub(udev);  	int i;  	for (i = 0; i < udev->maxchild; ++i) { -		if (udev->children[i]) -			recursively_mark_NOTATTACHED(udev->children[i]); +		if (hub->ports[i]->child) +			recursively_mark_NOTATTACHED(hub->ports[i]->child);  	}  	if (udev->state == USB_STATE_SUSPENDED)  		udev->active_duration -= jiffies; @@ -1893,6 +1953,7 @@ static void hub_free_dev(struct usb_device *udev)  void usb_disconnect(struct usb_device **pdev)  {  	struct usb_device	*udev = *pdev; +	struct usb_hub		*hub = hdev_to_hub(udev);  	int			i;  	/* mark the device as inactive, so any further urb submissions for @@ -1907,8 +1968,8 @@ void usb_disconnect(struct usb_device **pdev)  	/* Free up all the children before we remove this device */  	for (i = 0; i < udev->maxchild; i++) { -		if (udev->children[i]) -			usb_disconnect(&udev->children[i]); +		if (hub->ports[i]->child) +			usb_disconnect(&hub->ports[i]->child);  	}  	/* deallocate hcd/hardware state ... nuking all pending urbs and @@ -2113,7 +2174,8 @@ static void set_usb_port_removable(struct usb_device *udev)  		return;  	if (hub_is_superspeed(hdev)) { -		if (hub->descriptor->u.ss.DeviceRemovable & (1 << port)) +		if (le16_to_cpu(hub->descriptor->u.ss.DeviceRemovable) +				& (1 << port))  			removable = false;  	} else {  		if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8))) @@ -3072,7 +3134,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)  	for (port1 = 1; port1 <= hdev->maxchild; port1++) {  		struct usb_device	*udev; -		udev = hdev->children [port1-1]; +		udev = hub->ports[port1 - 1]->child;  		if (udev && udev->can_submit) {  			dev_warn(&intf->dev, "port %d nyet suspended\n", port1);  			if (PMSG_IS_AUTO(msg)) @@ -3999,7 +4061,7 @@ hub_power_remaining (struct usb_hub *hub)  	remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;  	for (port1 = 1; port1 <= hdev->maxchild; ++port1) { -		struct usb_device	*udev = hdev->children[port1 - 1]; +		struct usb_device	*udev = hub->ports[port1 - 1]->child;  		int			delta;  		if (!udev) @@ -4063,7 +4125,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  #endif  	/* Try to resuscitate an existing device */ -	udev = hdev->children[port1-1]; +	udev = hub->ports[port1 - 1]->child;  	if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&  			udev->state != USB_STATE_NOTATTACHED) {  		usb_lock_device(udev); @@ -4092,7 +4154,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  	/* Disconnect any existing devices under this port */  	if (udev) -		usb_disconnect(&hdev->children[port1-1]); +		usb_disconnect(&hub->ports[port1 - 1]->child);  	clear_bit(port1, hub->change_bits);  	/* We can forget about a "removed" device when there's a physical @@ -4228,7 +4290,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  		if (hdev->state == USB_STATE_NOTATTACHED)  			status = -ENOTCONN;  		else -			hdev->children[port1-1] = udev; +			hub->ports[port1 - 1]->child = udev;  		spin_unlock_irq(&device_state_lock);  		/* Run it through the hoops (find a driver, etc) */ @@ -4236,7 +4298,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  			status = usb_new_device(udev);  			if (status) {  				spin_lock_irq(&device_state_lock); -				hdev->children[port1-1] = NULL; +				hub->ports[port1 - 1]->child = NULL;  				spin_unlock_irq(&device_state_lock);  			}  		} @@ -4282,7 +4344,7 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,  	int ret;  	hdev = hub->hdev; -	udev = hdev->children[port-1]; +	udev = hub->ports[port - 1]->child;  	if (!hub_is_superspeed(hdev)) {  		if (!(portchange & USB_PORT_STAT_C_SUSPEND))  			return 0; @@ -4436,7 +4498,7 @@ static void hub_events(void)  				 */  				if (!(portstatus & USB_PORT_STAT_ENABLE)  				    && !connect_change -				    && hdev->children[i-1]) { +				    && hub->ports[i - 1]->child) {  					dev_err (hub_dev,  					    "port %i "  					    "disabled by hub (EMI?), " @@ -4993,3 +5055,75 @@ void usb_queue_reset_device(struct usb_interface *iface)  	schedule_work(&iface->reset_ws);  }  EXPORT_SYMBOL_GPL(usb_queue_reset_device); + +/** + * usb_hub_find_child - Get the pointer of child device + * attached to the port which is specified by @port1. + * @hdev: USB device belonging to the usb hub + * @port1: port num to indicate which port the child device + *	is attached to. + * + * USB drivers call this function to get hub's child device + * pointer. + * + * Return NULL if input param is invalid and + * child's usb_device pointer if non-NULL. + */ +struct usb_device *usb_hub_find_child(struct usb_device *hdev, +		int port1) +{ +	struct usb_hub *hub = hdev_to_hub(hdev); + +	if (port1 < 1 || port1 > hdev->maxchild) +		return NULL; +	return hub->ports[port1 - 1]->child; +} +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 + * @hdev: USB device belonging to the usb hub + * @port1: port num of the port + * + * Return port's acpi handle if successful, NULL if params are + * invaild. + */ +acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev, +	int port1) +{ +	struct usb_hub *hub = hdev_to_hub(hdev); + +	return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev); +} +#endif  |