diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-09-04 21:53:13 +0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 17:46:39 +0400 |
commit | 913a8a344ffcaf0b4a586d6662a2c66a7106557d (patch) | |
tree | 07a2a10118ab15bd4c597c1b1460c8028a3b1adc /drivers/usb/host/xhci-mem.c | |
parent | 5270b951b9cd5e50aea55cb52684a171fb10381c (diff) | |
download | linux-913a8a344ffcaf0b4a586d6662a2c66a7106557d.tar.xz |
USB: xhci: Change how xHCI commands are handled.
Some commands to the xHCI hardware cannot be allowed to fail due to out of
memory issues or the command ring being full.
Add a way to reserve a TRB on the command ring, and make all command
queueing functions indicate whether they are using a reserved TRB.
Add a way to pre-allocate all the memory a command might need. A command
needs an input context, a variable to store the status, and (optionally) a
completion for the caller to wait on. Change all code that assumes the
input device context, status, and completion for a command is stored in
the xhci virtual USB device structure (xhci_virt_device).
Store pending completions in a FIFO in xhci_virt_device. Make the event
handler for a configure endpoint command check to see whether a pending
command in the list has completed. We need to use separate input device
contexts for some configure endpoint commands, since multiple drivers can
submit requests at the same time that require a configure endpoint
command.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci-mem.c')
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 55 |
1 files changed, 49 insertions, 6 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 75458ecc8eab..6e6797a38780 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -319,6 +319,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, goto fail; init_completion(&dev->cmd_completion); + INIT_LIST_HEAD(&dev->cmd_list); /* Point to output device context in dcbaa. */ xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma; @@ -624,13 +625,15 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci, * issue a configure endpoint command. */ void xhci_endpoint_copy(struct xhci_hcd *xhci, - struct xhci_virt_device *vdev, unsigned int ep_index) + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx, + unsigned int ep_index) { struct xhci_ep_ctx *out_ep_ctx; struct xhci_ep_ctx *in_ep_ctx; - out_ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index); - in_ep_ctx = xhci_get_ep_ctx(xhci, vdev->in_ctx, ep_index); + out_ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); + in_ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index); in_ep_ctx->ep_info = out_ep_ctx->ep_info; in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2; @@ -643,13 +646,15 @@ void xhci_endpoint_copy(struct xhci_hcd *xhci, * issue a configure endpoint command. Only the context entries field matters, * but we'll copy the whole thing anyway. */ -void xhci_slot_copy(struct xhci_hcd *xhci, struct xhci_virt_device *vdev) +void xhci_slot_copy(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_container_ctx *out_ctx) { struct xhci_slot_ctx *in_slot_ctx; struct xhci_slot_ctx *out_slot_ctx; - in_slot_ctx = xhci_get_slot_ctx(xhci, vdev->in_ctx); - out_slot_ctx = xhci_get_slot_ctx(xhci, vdev->out_ctx); + in_slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); + out_slot_ctx = xhci_get_slot_ctx(xhci, out_ctx); in_slot_ctx->dev_info = out_slot_ctx->dev_info; in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2; @@ -754,6 +759,44 @@ static void scratchpad_free(struct xhci_hcd *xhci) xhci->scratchpad = NULL; } +struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags) +{ + struct xhci_command *command; + + command = kzalloc(sizeof(*command), mem_flags); + if (!command) + return NULL; + + command->in_ctx = + xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, mem_flags); + if (!command->in_ctx) + return NULL; + + if (allocate_completion) { + command->completion = + kzalloc(sizeof(struct completion), mem_flags); + if (!command->completion) { + xhci_free_container_ctx(xhci, command->in_ctx); + return NULL; + } + init_completion(command->completion); + } + + command->status = 0; + INIT_LIST_HEAD(&command->cmd_list); + return command; +} + +void xhci_free_command(struct xhci_hcd *xhci, + struct xhci_command *command) +{ + xhci_free_container_ctx(xhci, + command->in_ctx); + kfree(command->completion); + kfree(command); +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); |