summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-12-03 09:44:31 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 11:55:27 -0800
commit06df572909080786e128eabdb2e39a12bce239de (patch)
treea00ea73a802b3a7d6f70f4cdc81b264a357913d3
parent74f9fe21e0440066eb337b9f644238cb3050b91c (diff)
USB: xhci: Fix command completion after a drop endpoint.
The xHCI driver issues a Configure Endpoint command for two reasons: - a new configuration or alternate interface setting is selected - a quirky Fresco Logic prototype requires the command after a Reset Endpoint command. The xHCI driver only waits on the command in the first case. When a configure endpoint command completes, the driver needs to know why the command was generated. When the driver only supported selecting an initial configuration, the check was simple. Unfortunately that check doesn't work now that the driver supports alternate interfaces. If an endpoint must be dropped (because it's not in the new alternate setting) and no new endpoints are added, the math involving xhci_last_valid_endpoint() will assign -1 to an unsigned integer and cause an out-of-bounds array access. Move the check for the quirky hardware sooner and avoid the bad array access. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/xhci-ring.c36
1 files changed, 20 insertions, 16 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 2e346334a363..ee7bc7ecbc59 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -903,28 +903,32 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
virt_dev->in_ctx);
/* Input ctx add_flags are the endpoint index plus one */
ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1;
- ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
- if (!ep_ring) {
- /* This must have been an initial configure endpoint */
- xhci->devs[slot_id]->cmd_status =
- GET_COMP_CODE(event->status);
- complete(&xhci->devs[slot_id]->cmd_completion);
- break;
- }
- ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
- xhci_dbg(xhci, "Completed config ep cmd - last ep index = %d, "
- "state = %d\n", ep_index, ep_state);
+ /* A usb_set_interface() call directly after clearing a halted
+ * condition may race on this quirky hardware.
+ * Not worth worrying about, since this is prototype hardware.
+ */
if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
- ep_state & EP_HALTED) {
+ ep_index != (unsigned int) -1 &&
+ ctrl_ctx->add_flags - SLOT_FLAG ==
+ ctrl_ctx->drop_flags) {
+ ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
+ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
+ if (!(ep_state & EP_HALTED))
+ goto bandwidth_change;
+ xhci_dbg(xhci, "Completed config ep cmd - "
+ "last ep index = %d, state = %d\n",
+ ep_index, ep_state);
/* Clear our internal halted state and restart ring */
xhci->devs[slot_id]->eps[ep_index].ep_state &=
~EP_HALTED;
ring_ep_doorbell(xhci, slot_id, ep_index);
- } else {
- xhci->devs[slot_id]->cmd_status =
- GET_COMP_CODE(event->status);
- complete(&xhci->devs[slot_id]->cmd_completion);
+ break;
}
+bandwidth_change:
+ xhci_dbg(xhci, "Completed config ep cmd\n");
+ xhci->devs[slot_id]->cmd_status =
+ GET_COMP_CODE(event->status);
+ complete(&xhci->devs[slot_id]->cmd_completion);
break;
case TRB_TYPE(TRB_EVAL_CONTEXT):
virt_dev = xhci->devs[slot_id];