Skip to content

Commit

Permalink
USB: xhci: Control transfer support.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sarah Sharp authored and Greg Kroah-Hartman committed Jun 16, 2009
1 parent 6d65b78 commit d0e96f5
Show file tree
Hide file tree
Showing 5 changed files with 506 additions and 3 deletions.
93 changes: 93 additions & 0 deletions drivers/usb/host/xhci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,99 @@ void xhci_shutdown(struct usb_hcd *hcd)

/*-------------------------------------------------------------------------*/

/**
* xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and
* HCDs. Find the index for an endpoint given its descriptor. Use the return
* value to right shift 1 for the bitmask.
*
* Index = (epnum * 2) + direction - 1,
* where direction = 0 for OUT, 1 for IN.
* For control endpoints, the IN index is used (OUT index is unused), so
* index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2)
*/
unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc)
{
unsigned int index;
if (usb_endpoint_xfer_control(desc))
index = (unsigned int) (usb_endpoint_num(desc)*2);
else
index = (unsigned int) (usb_endpoint_num(desc)*2) +
(usb_endpoint_dir_in(desc) ? 1 : 0) - 1;
return index;
}

/* Returns 1 if the arguments are OK;
* returns 0 this is a root hub; returns -EINVAL for NULL pointers.
*/
int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep, int check_ep, const char *func) {
if (!hcd || (check_ep && !ep) || !udev) {
printk(KERN_DEBUG "xHCI %s called with invalid args\n",
func);
return -EINVAL;
}
if (!udev->parent) {
printk(KERN_DEBUG "xHCI %s called for root hub\n",
func);
return 0;
}
if (!udev->slot_id) {
printk(KERN_DEBUG "xHCI %s called with unaddressed device\n",
func);
return -EINVAL;
}
return 1;
}

/*
* non-error returns are a promise to giveback() the urb later
* we drop ownership so next owner (or urb unlink) can get it
*/
int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned long flags;
int ret = 0;
unsigned int slot_id, ep_index;

if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
return -EINVAL;

slot_id = urb->dev->slot_id;
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
/* Only support ep 0 control transfers for now */
if (ep_index != 0) {
xhci_dbg(xhci, "WARN: urb submitted to unsupported ep %x\n",
urb->ep->desc.bEndpointAddress);
return -ENOSYS;
}

spin_lock_irqsave(&xhci->lock, flags);
if (!xhci->devs || !xhci->devs[slot_id]) {
if (!in_interrupt())
dev_warn(&urb->dev->dev, "WARN: urb submitted for dev with no Slot ID\n");
return -EINVAL;
}
if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
if (!in_interrupt())
xhci_dbg(xhci, "urb submitted during PCI suspend\n");
ret = -ESHUTDOWN;
goto exit;
}
ret = queue_ctrl_tx(xhci, mem_flags, urb, slot_id, ep_index);
exit:
spin_unlock_irqrestore(&xhci->lock, flags);
return ret;
}

/* Remove from hardware lists
* completions normally happen asynchronously
*/
int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
return -ENOSYS;
}

/*
* At this point, the struct usb_device is about to go away, the device has
* disconnected, and all traffic has been stopped and the endpoints have been
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
if (!ring)
return 0;

INIT_LIST_HEAD(&ring->td_list);
if (num_segs == 0)
return ring;

Expand Down Expand Up @@ -188,6 +189,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
return 0;
}

/* All the xhci_tds in the ring's TD list should be freed at this point */
void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *dev;
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/xhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ static const struct hc_driver xhci_pci_hc_driver = {
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = xhci_urb_enqueue,
.urb_dequeue = xhci_urb_dequeue,
.alloc_dev = xhci_alloc_dev,
.free_dev = xhci_free_dev,
.address_device = xhci_address_device,
Expand Down
Loading

0 comments on commit d0e96f5

Please sign in to comment.