diff options
Diffstat (limited to 'drivers/usb/host/xhci-mem.c')
| -rw-r--r-- | drivers/usb/host/xhci-mem.c | 46 | 
1 files changed, 40 insertions, 6 deletions
| diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 321de2e0161b..8414ed2a02de 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -979,6 +979,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)  	xhci->devs[slot_id] = NULL;  } +/* + * Free a virt_device structure. + * If the virt_device added a tt_info (a hub) and has children pointing to + * that tt_info, then free the child first. Recursive. + * We can't rely on udev at this point to find child-parent relationships. + */ +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) +{ +	struct xhci_virt_device *vdev; +	struct list_head *tt_list_head; +	struct xhci_tt_bw_info *tt_info, *next; +	int i; + +	vdev = xhci->devs[slot_id]; +	if (!vdev) +		return; + +	tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts); +	list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { +		/* is this a hub device that added a tt_info to the tts list */ +		if (tt_info->slot_id == slot_id) { +			/* are any devices using this tt_info? */ +			for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { +				vdev = xhci->devs[i]; +				if (vdev && (vdev->tt_info == tt_info)) +					xhci_free_virt_devices_depth_first( +						xhci, i); +			} +		} +	} +	/* we are now at a leaf device */ +	xhci_free_virt_device(xhci, slot_id); +} +  int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,  		struct usb_device *udev, gfp_t flags)  { @@ -1795,7 +1829,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)  	int size;  	int i, j, num_ports; -	del_timer_sync(&xhci->cmd_timer); +	cancel_delayed_work_sync(&xhci->cmd_timer);  	/* Free the Event Ring Segment Table and the actual Event Ring */  	size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); @@ -1828,8 +1862,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)  		}  	} -	for (i = 1; i < MAX_HC_SLOTS; ++i) -		xhci_free_virt_device(xhci, i); +	for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) +		xhci_free_virt_devices_depth_first(xhci, i);  	dma_pool_destroy(xhci->segment_pool);  	xhci->segment_pool = NULL; @@ -2342,9 +2376,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)  	INIT_LIST_HEAD(&xhci->cmd_list); -	/* init command timeout timer */ -	setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, -		    (unsigned long)xhci); +	/* init command timeout work */ +	INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); +	init_completion(&xhci->cmd_ring_stop_completion);  	page_size = readl(&xhci->op_regs->page_size);  	xhci_dbg_trace(xhci, trace_xhci_dbg_init, |