Re: [PATCH 3/4] xHCI: handle command after aborting the command ring

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I made a stupid mistake. After I used checkpatch.pl to check this patch,
I added some annotations and forgot to check it again. So the patch
has a trailing whitespace. Sorry. I will send it again.

Best Regards,
Elric

2012/6/27 Elric Fu <elricfu1@xxxxxxxxx>:
> According to xHCI spec section 4.6.1.1 and section 4.6.1.2,
> after aborting a command on the command ring, xHC will
> generate a command completion event with its completion
> code set to Command Ring Stopped at least. If a command is
> currently executing at the time of aborting a command, xHC
> also generate a command completion event with its completion
> code set to Command Abort. When the command ring is stopped,
> software may remove, add, or rearrage Command Descriptors.
>
> To cancel a command, software will initialize a command
> descriptor for the cancel command, and add it into a
> cancel_cmd_list of xhci. When the command ring is stopped,
> software will find the command trbs described by command
> descriptors in cancel_cmd_list and modify it to No Op
> command. If software can't find the matched trbs, we can
> think it had been finished.
>
> Signed-off-by: Elric Fu <elricfu1@xxxxxxxxx>
> ---
>  drivers/usb/host/xhci-ring.c |  171 ++++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 165 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index aa514cc..9aec89f 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -1151,6 +1151,20 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
>        }
>  }
>
> +/* Complete the command and detele it from the devcie's command queue.
> + */
> +static void xhci_complete_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
> +               struct xhci_command *command, u32 status)
> +{
> +       command->status = status;
> +       list_del(&command->cmd_list);
> +       if (command->completion)
> +               complete(command->completion);
> +       else
> +               xhci_free_command(xhci, command);
> +}
> +
> +
>  /* 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.
> @@ -1169,15 +1183,144 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
>        if (xhci->cmd_ring->dequeue != command->command_trb)
>                return 0;
>
> -       command->status = GET_COMP_CODE(le32_to_cpu(event->status));
> -       list_del(&command->cmd_list);
> -       if (command->completion)
> -               complete(command->completion);
> -       else
> -               xhci_free_command(xhci, command);
> +       xhci_complete_cmd_in_cmd_wait_list(xhci, command,
> +                       GET_COMP_CODE(le32_to_cpu(event->status)));
>        return 1;
>  }
>
> +/*
> + * Finding the command trb need to be cancelled and modifying it to
> + * NO OP command. And if the command is in device's command wait
> + * list, finishing and freeing it.
> + *
> + * If we can't find the command trb, we think it had already been
> + * executed.
> + */
> +static void xhci_cmd_to_noop(struct xhci_hcd *xhci, struct xhci_cd *cur_cd)
> +{
> +       struct xhci_segment *cur_seg;
> +       union xhci_trb *cmd_trb;
> +       u32 cycle_state;
> +
> +       if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
> +               return;
> +
> +       /* find the current segment of command ring */
> +       cur_seg = find_trb_seg(xhci->cmd_ring->first_seg,
> +                       xhci->cmd_ring->dequeue, &cycle_state);
> +
> +       /* find the command trb matched by cd from command ring */
> +       for (cmd_trb = xhci->cmd_ring->dequeue;
> +                       cmd_trb != xhci->cmd_ring->enqueue;
> +                       next_trb(xhci, xhci->cmd_ring, &cur_seg, &cmd_trb)) {
> +               /* If the trb is link trb, continue */
> +               if (TRB_TYPE_LINK_LE32(cmd_trb->generic.field[3]))
> +                       continue;
> +
> +               if (cur_cd->cmd_trb == cmd_trb) {
> +
> +                       /* If the command in device's command list, we should
> +                        * finish it and free the command structure.
> +                        */
> +                       if (cur_cd->command)
> +                               xhci_complete_cmd_in_cmd_wait_list(xhci,
> +                                       cur_cd->command, COMP_CMD_STOP);
> +
> +                       /* get cycle state from the origin command trb */
> +                       cycle_state = le32_to_cpu(cmd_trb->generic.field[3])
> +                               & TRB_CYCLE;
> +
> +                       /* modify the command trb to NO OP command */
> +                       cmd_trb->generic.field[0] = 0;
> +                       cmd_trb->generic.field[1] = 0;
> +                       cmd_trb->generic.field[2] = 0;
> +                       cmd_trb->generic.field[3] = cpu_to_le32(
> +                                       TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
> +                       break;
> +               }
> +       }
> +}
> +
> +static void xhci_cancel_cmd_in_cd_list(struct xhci_hcd *xhci)
> +{
> +       struct xhci_cd *cur_cd, *next_cd;
> +
> +       if (list_empty(&xhci->cancel_cmd_list))
> +               return;
> +
> +       list_for_each_entry_safe(cur_cd, next_cd,
> +                       &xhci->cancel_cmd_list, cancel_cmd_list) {
> +               xhci_cmd_to_noop(xhci, cur_cd);
> +               list_del(&cur_cd->cancel_cmd_list);
> +               kfree(cur_cd);
> +       }
> +}
> +
> +/*
> + * traversing the cancel_cmd_list. If the command descriptor according
> + * to cmd_trb is found, the function free it and return 1, otherwise
> + * return 0.
> + */
> +static int xhci_search_cmd_trb_in_cd_list(struct xhci_hcd *xhci,
> +               union xhci_trb *cmd_trb)
> +{
> +       struct xhci_cd *cur_cd, *next_cd;
> +
> +       if (list_empty(&xhci->cancel_cmd_list))
> +               return 0;
> +
> +       list_for_each_entry_safe(cur_cd, next_cd,
> +                       &xhci->cancel_cmd_list, cancel_cmd_list) {
> +               if (cur_cd->cmd_trb == cmd_trb) {
> +                       if (cur_cd->command)
> +                               xhci_complete_cmd_in_cmd_wait_list(xhci,
> +                                       cur_cd->command, COMP_CMD_STOP);
> +                       list_del(&cur_cd->cancel_cmd_list);
> +                       kfree(cur_cd);
> +                       return 1;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * If the cmd_trb_comp_code is COMP_CMD_ABORT, we just check whether the
> + * trb pointed by the command ring dequeue pointer is the trb we want to
> + * cancel or not. And if the cmd_trb_comp_code is COMP_CMD_STOP, we will
> + * traverse the cancel_cmd_list to trun the all of the commands according
> + * to command descriptor to NO-OP trb.
> + */
> +static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
> +               int cmd_trb_comp_code)
> +{
> +       int cur_trb_is_good = 0;
> +
> +       /* Searching the cmd trb pointed by the command ring dequeue
> +        * pointer in command descriptor list. If it is found, free it.
> +        */
> +       cur_trb_is_good = xhci_search_cmd_trb_in_cd_list(xhci,
> +                       xhci->cmd_ring->dequeue);
> +
> +       if (cmd_trb_comp_code == COMP_CMD_ABORT)
> +               xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
> +       else if (cmd_trb_comp_code == COMP_CMD_STOP) {
> +               /* traversing the cancel_cmd_list and canceling
> +                * the command according to command descriptor
> +                */
> +               xhci_cancel_cmd_in_cd_list(xhci);
> +
> +               xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
> +               /*
> +                * ring command ring doorbell again to restart the
> +                * command ring
> +                */
> +               if (xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue)
> +                       xhci_ring_cmd_db(xhci);
> +       }
> +       return cur_trb_is_good;
> +}
> +
>  static void handle_cmd_completion(struct xhci_hcd *xhci,
>                struct xhci_event_cmd *event)
>  {
> @@ -1203,6 +1346,22 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
>                xhci->error_bitmask |= 1 << 5;
>                return;
>        }
> +
> +       if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) ||
> +               (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) {
> +               /* If the return value is 0, we think the trb pointed by
> +                * command ring dequeue pointer is a good trb. The good
> +                * trb means we don't want to cancel the trb, but it have
> +                * been stopped by host. So we should handle it normally.
> +                * Otherwise, driver should invoke inc_deq() and return.
> +                */
> +               if (handle_stopped_cmd_ring(xhci,
> +                               GET_COMP_CODE(le32_to_cpu(event->status)))) {
> +                       inc_deq(xhci, xhci->cmd_ring);
> +                       return;
> +               }
> +       }
> +
>        switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
>                & TRB_TYPE_BITMASK) {
>        case TRB_TYPE(TRB_ENABLE_SLOT):
> --
> 1.7.9.1
>
--
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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux