USB: xhci: Fix command wait list handling.
authorSarah Sharp <sarah.a.sharp@linux.intel.com>
Fri, 4 Sep 2009 17:53:15 +0000 (10:53 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Sep 2009 13:46:40 +0000 (06:46 -0700)
In the xHCI driver, configure endpoint commands that are submitted to the
hardware may involve one of two data structures.  If the configure
endpoint command is setting up a new configuration or modifying max packet
sizes, the data structures and completions are statically allocated in the
xhci_virt_device structure.  If the command is being used to set up
streams or add hub information, then the data structures are dynamically
allocated, and placed on a device command waiting list.

Break out the code to check whether a completed command is in the device
command waiting list.  Fix a subtle bug in the old code: continue
processing the command if the command isn't in the wait list.  In the old
code, if there was a command in the wait list, but it didn't match the
completed command, the completed command event would be dropped.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/xhci-ring.c

index a9379b3..2274604 100644 (file)
@@ -685,6 +685,34 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
        }
 }
 
+/* Check to see if a command in the device's command queue matches this one.
+ * Signal the completion or free the command, and return 1.  Return 0 if the
+ * completed command isn't at the head of the command list.
+ */
+static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
+               struct xhci_virt_device *virt_dev,
+               struct xhci_event_cmd *event)
+{
+       struct xhci_command *command;
+
+       if (list_empty(&virt_dev->cmd_list))
+               return 0;
+
+       command = list_entry(virt_dev->cmd_list.next,
+                       struct xhci_command, cmd_list);
+       if (xhci->cmd_ring->dequeue != command->command_trb)
+               return 0;
+
+       command->status =
+               GET_COMP_CODE(event->status);
+       list_del(&command->cmd_list);
+       if (command->completion)
+               complete(command->completion);
+       else
+               xhci_free_command(xhci, command);
+       return 1;
+}
+
 static void handle_cmd_completion(struct xhci_hcd *xhci,
                struct xhci_event_cmd *event)
 {
@@ -724,24 +752,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                break;
        case TRB_TYPE(TRB_CONFIG_EP):
                virt_dev = xhci->devs[slot_id];
-               /* Check to see if a command in the device's command queue
-                * matches this one.  Signal the completion or free the command.
-                */
-               if (!list_empty(&virt_dev->cmd_list)) {
-                       struct xhci_command *command;
-                       command = list_entry(virt_dev->cmd_list.next,
-                                       struct xhci_command, cmd_list);
-                       if (xhci->cmd_ring->dequeue == command->command_trb) {
-                               command->status =
-                                       GET_COMP_CODE(event->status);
-                               list_del(&command->cmd_list);
-                               if (command->completion)
-                                       complete(command->completion);
-                               else
-                                       xhci_free_command(xhci, command);
-                       }
+               if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
                        break;
-               }
                /*
                 * Configure endpoint commands can come from the USB core
                 * configuration or alt setting changes, or because the HW