>From fb54a4791208d08d42c1bae8f58b2657301a1143 Mon Sep 17 00:00:00 2001 From: Andiry Xu <andiry.xu@xxxxxxx> Date: Mon, 16 Aug 2010 15:52:18 +0800 Subject: [PATCH 1/2] xHCI: change xhci_reset_device() to allocate new device Rename xhci_reset_device() to xhci_discover_or_reset_device(). If xhci_discover_or_reset_device() is called to reset a device which does not exist, it calls xhci_alloc_dev() to re-allocate the device. This would prevent the reset device failure, possibly due to the xHC restore error during S3/S4 resume. Note this change would affect xhci_address_device() behavior: udev->config exists, but the device is actually not configured yet. Add a flag in struct xhci_virt_device to indicate its actual state. Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx> --- drivers/usb/host/xhci-mem.c | 1 + drivers/usb/host/xhci-pci.c | 2 +- drivers/usb/host/xhci.c | 20 +++++++++++++++----- drivers/usb/host/xhci.h | 4 +++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 002c775..2b62062 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -896,6 +896,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, init_completion(&dev->cmd_completion); INIT_LIST_HEAD(&dev->cmd_list); + dev->configured = false; /* Point to output device context in dcbaa. */ xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 12bdc29..6f47dd8 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -170,7 +170,7 @@ static const struct hc_driver xhci_pci_hc_driver = { .reset_bandwidth = xhci_reset_bandwidth, .address_device = xhci_address_device, .update_hub_device = xhci_update_hub_device, - .reset_device = xhci_reset_device, + .reset_device = xhci_discover_or_reset_device, /* * scheduling support diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8be7633..c22d339 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1675,6 +1675,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) return ret; } + virt_dev->configured = true; xhci_dbg(xhci, "Output context after successful config ep cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); @@ -2251,8 +2252,12 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, * Wait for the Reset Device command to finish. Remove all structures * associated with the endpoints that were disabled. Clear the input device * structure? Cache the rings? Reset the control endpoint 0 max packet size? + * + * If the virt_dev to be reset does not exist, it means the device is lost, + * perhaps due to the xHC restore error and reset during S3/S4. In this case, + * call xhci_alloc_dev() to re-allocate the device. */ -int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev) +int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) { int ret, i; unsigned long flags; @@ -2270,9 +2275,13 @@ int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev) slot_id = udev->slot_id; virt_dev = xhci->devs[slot_id]; if (!virt_dev) { - xhci_dbg(xhci, "%s called with invalid slot ID %u\n", - __func__, slot_id); - return -EINVAL; + xhci_dbg(xhci, "The device to be reset with slot ID %u does " + "not exist. Re-allocate the device\n", slot_id); + ret = xhci_alloc_dev(hcd, udev); + if (ret == 1) + return 0; + else + return -EINVAL; } xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id); @@ -2337,6 +2346,7 @@ int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev) goto command_cleanup; case COMP_SUCCESS: xhci_dbg(xhci, "Successful reset device command.\n"); + virt_dev->configured = false; break; default: if (xhci_is_vendor_info_code(xhci, ret)) @@ -2490,7 +2500,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) virt_dev = xhci->devs[udev->slot_id]; /* If this is a Set Address to an unconfigured device, setup ep 0 */ - if (!udev->config) + if (!udev->config || !virt_dev->configured) xhci_setup_addressable_virt_dev(xhci, udev); else xhci_copy_ep0_dequeue_into_input_ctx(xhci, udev); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index a2d1eed..4415ce2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -753,6 +753,8 @@ struct xhci_virt_device { /* Rings saved to ensure old alt settings can be re-instated */ struct xhci_ring **ring_cache; int num_rings_cached; + /* Indicate whether the device is configured */ + bool configured; #define XHCI_MAX_RINGS_CACHED 31 struct xhci_virt_ep eps[31]; struct completion cmd_completion; @@ -1430,7 +1432,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep); int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep); void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep); -int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev); int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html