|
@@ -979,6 +979,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
|
|
xhci->devs[slot_id] = NULL;
|
|
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,
|
|
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
|
|
struct usb_device *udev, gfp_t flags)
|
|
struct usb_device *udev, gfp_t flags)
|
|
{
|
|
{
|
|
@@ -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);
|
|
dma_pool_destroy(xhci->segment_pool);
|
|
xhci->segment_pool = NULL;
|
|
xhci->segment_pool = NULL;
|