summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-mem.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-09-04 10:53:13 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-09-23 06:46:39 -0700
commit913a8a344ffcaf0b4a586d6662a2c66a7106557d (patch)
tree07a2a10118ab15bd4c597c1b1460c8028a3b1adc /drivers/usb/host/xhci-mem.c
parent5270b951b9cd5e50aea55cb52684a171fb10381c (diff)
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.c55
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);