>From bc53db5a7c84c2afad0f0dd938dec6c336e8ebe4 Mon Sep 17 00:00:00 2001 From: Andiry Xu <andiry.xu@xxxxxxx> Date: Mon, 23 Aug 2010 13:29:35 +0800 Subject: [PATCH 2/3] 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 or does not match the udev, 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 | 37 ++++++++++++++++++++++++++++++++++--- drivers/usb/host/xhci.h | 4 +++- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index be90180..e7947ea 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -779,6 +779,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->udev = udev; + 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 f7efe02..aefc349 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -152,7 +152,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 4201119..a0dca7c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1372,6 +1372,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)); @@ -1943,8 +1944,13 @@ 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 or does not match the udev, + * it means the device is lost, possibly due to the xHC restore error and + * re-initialization 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; @@ -1955,12 +1961,36 @@ int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev) int timeleft; int last_freed_endpoint; - ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); + ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); slot_id = udev->slot_id; virt_dev = xhci->devs[slot_id]; + if (!virt_dev) { + 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; + } + + if (virt_dev->udev != udev) { + /* If the virt_dev and the udev does not match, this virt_dev + * may belong to another udev. + * Re-allocate the device. + */ + xhci_dbg(xhci, "The device to be reset with slot ID %u does " + "not match the udev. 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); /* Allocate the command structure that holds the struct completion. @@ -2024,6 +2054,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)) @@ -2177,7 +2208,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 f03f140..6ab0a52 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -746,6 +746,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; @@ -1389,7 +1391,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