diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-04-27 22:58:01 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:49 -0400 |
commit | d0e96f5a71a032ced0c35f521c1cbd67e816922a (patch) | |
tree | 402e4d1ce20682fd2efd3ffd2ad23ffd097b1436 /drivers/usb/host/xhci-hcd.c | |
parent | 6d65b78a093552fb42448480d4c66bf093a6d4cf (diff) |
USB: xhci: Control transfer support.
Allow device drivers to enqueue URBs to control endpoints on devices under
an xHCI host controller. Each control transfer is represented by a
series of Transfer Descriptors (TDs) written to an endpoint ring. There
is one TD for the Setup phase, (optionally) one TD for the Data phase, and
one TD for the Status phase.
Enqueue these TDs onto the endpoint ring that represents the control
endpoint. The host controller hardware will return an event on the event
ring that points to the (DMA) address of one of the TDs on the endpoint
ring. If the transfer was successful, the transfer event TRB will have a
completion code of success, and it will point to the Status phase TD.
Anything else is considered an error.
This should work for control endpoints besides the default endpoint, but
that hasn't been tested.
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-hcd.c')
-rw-r--r-- | drivers/usb/host/xhci-hcd.c | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index a01d2ee7435a..5d94b4ffac92 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c | |||
@@ -509,6 +509,99 @@ void xhci_shutdown(struct usb_hcd *hcd) | |||
509 | 509 | ||
510 | /*-------------------------------------------------------------------------*/ | 510 | /*-------------------------------------------------------------------------*/ |
511 | 511 | ||
512 | /** | ||
513 | * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and | ||
514 | * HCDs. Find the index for an endpoint given its descriptor. Use the return | ||
515 | * value to right shift 1 for the bitmask. | ||
516 | * | ||
517 | * Index = (epnum * 2) + direction - 1, | ||
518 | * where direction = 0 for OUT, 1 for IN. | ||
519 | * For control endpoints, the IN index is used (OUT index is unused), so | ||
520 | * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2) | ||
521 | */ | ||
522 | unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc) | ||
523 | { | ||
524 | unsigned int index; | ||
525 | if (usb_endpoint_xfer_control(desc)) | ||
526 | index = (unsigned int) (usb_endpoint_num(desc)*2); | ||
527 | else | ||
528 | index = (unsigned int) (usb_endpoint_num(desc)*2) + | ||
529 | (usb_endpoint_dir_in(desc) ? 1 : 0) - 1; | ||
530 | return index; | ||
531 | } | ||
532 | |||
533 | /* Returns 1 if the arguments are OK; | ||
534 | * returns 0 this is a root hub; returns -EINVAL for NULL pointers. | ||
535 | */ | ||
536 | int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, | ||
537 | struct usb_host_endpoint *ep, int check_ep, const char *func) { | ||
538 | if (!hcd || (check_ep && !ep) || !udev) { | ||
539 | printk(KERN_DEBUG "xHCI %s called with invalid args\n", | ||
540 | func); | ||
541 | return -EINVAL; | ||
542 | } | ||
543 | if (!udev->parent) { | ||
544 | printk(KERN_DEBUG "xHCI %s called for root hub\n", | ||
545 | func); | ||
546 | return 0; | ||
547 | } | ||
548 | if (!udev->slot_id) { | ||
549 | printk(KERN_DEBUG "xHCI %s called with unaddressed device\n", | ||
550 | func); | ||
551 | return -EINVAL; | ||
552 | } | ||
553 | return 1; | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | * non-error returns are a promise to giveback() the urb later | ||
558 | * we drop ownership so next owner (or urb unlink) can get it | ||
559 | */ | ||
560 | int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) | ||
561 | { | ||
562 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | ||
563 | unsigned long flags; | ||
564 | int ret = 0; | ||
565 | unsigned int slot_id, ep_index; | ||
566 | |||
567 | if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0) | ||
568 | return -EINVAL; | ||
569 | |||
570 | slot_id = urb->dev->slot_id; | ||
571 | ep_index = xhci_get_endpoint_index(&urb->ep->desc); | ||
572 | /* Only support ep 0 control transfers for now */ | ||
573 | if (ep_index != 0) { | ||
574 | xhci_dbg(xhci, "WARN: urb submitted to unsupported ep %x\n", | ||
575 | urb->ep->desc.bEndpointAddress); | ||
576 | return -ENOSYS; | ||
577 | } | ||
578 | |||
579 | spin_lock_irqsave(&xhci->lock, flags); | ||
580 | if (!xhci->devs || !xhci->devs[slot_id]) { | ||
581 | if (!in_interrupt()) | ||
582 | dev_warn(&urb->dev->dev, "WARN: urb submitted for dev with no Slot ID\n"); | ||
583 | return -EINVAL; | ||
584 | } | ||
585 | if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { | ||
586 | if (!in_interrupt()) | ||
587 | xhci_dbg(xhci, "urb submitted during PCI suspend\n"); | ||
588 | ret = -ESHUTDOWN; | ||
589 | goto exit; | ||
590 | } | ||
591 | ret = queue_ctrl_tx(xhci, mem_flags, urb, slot_id, ep_index); | ||
592 | exit: | ||
593 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
594 | return ret; | ||
595 | } | ||
596 | |||
597 | /* Remove from hardware lists | ||
598 | * completions normally happen asynchronously | ||
599 | */ | ||
600 | int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | ||
601 | { | ||
602 | return -ENOSYS; | ||
603 | } | ||
604 | |||
512 | /* | 605 | /* |
513 | * At this point, the struct usb_device is about to go away, the device has | 606 | * At this point, the struct usb_device is about to go away, the device has |
514 | * disconnected, and all traffic has been stopped and the endpoints have been | 607 | * disconnected, and all traffic has been stopped and the endpoints have been |