Skip to content

Commit

Permalink
USB: xhci: Correct assumptions about number of rings per endpoint.
Browse files Browse the repository at this point in the history
Much of the xHCI driver code assumes that endpoints only have one ring.
Now an endpoint can have one ring per enabled stream ID, so correct that
assumption.  Use functions that translate the stream_id field in the URB
or the DMA address of a TRB into the correct stream ring.

Correct the polling loop to print out all enabled stream rings.  Make the
URB cancellation routine find the correct stream ring if the URB has
stream_id set.  Make sure the URB enqueueing routine does the same.  Also
correct the code that handles stalled/halted endpoints.

Check that commands and registers that can take stream IDs handle them
properly.  That includes ringing an endpoint doorbell, resetting a
stalled/halted endpoint, and setting a transfer ring dequeue pointer
(since that command can set the dequeue pointer in a stream context or an
endpoint context).

Correct the transfer event handler to translate a TRB DMA address into the
stream ring it was enqueued to.  Make the code to allocate and prepare TD
structures adds the TD to the right td_list for the stream ring.  Make
sure the code to give the first TRB in a TD to the hardware manipulates
the correct stream ring.

When an endpoint stalls, store the stream ID of the stream ring that
stalled in the xhci_virt_ep structure.  Use that instead of the stream ID
in the URB, since an URB may be re-used after it is given back after a
non-control endpoint stall.

Signed-off-by: Sarah Sharp <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
Sarah Sharp authored and gregkh committed May 20, 2010
1 parent 8df75f4 commit e9df17e
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 55 deletions.
24 changes: 24 additions & 0 deletions drivers/usb/host/xhci-dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,30 @@ void xhci_debug_ring(struct xhci_hcd *xhci, struct xhci_ring *ring)
xhci_debug_segment(xhci, seg);
}

void xhci_dbg_ep_rings(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_virt_ep *ep)
{
int i;
struct xhci_ring *ring;

if (ep->ep_state & EP_HAS_STREAMS) {
for (i = 1; i < ep->stream_info->num_streams; i++) {
ring = ep->stream_info->stream_rings[i];
xhci_dbg(xhci, "Dev %d endpoint %d stream ID %d:\n",
slot_id, ep_index, i);
xhci_debug_segment(xhci, ring->deq_seg);
}
} else {
ring = ep->ring;
if (!ring)
return;
xhci_dbg(xhci, "Dev %d endpoint ring %d:\n",
slot_id, ep_index);
xhci_debug_segment(xhci, ring->deq_seg);
}
}

void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
{
u32 addr = (u32) erst->erst_dma_addr;
Expand Down
74 changes: 73 additions & 1 deletion drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,19 @@ struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
mem_flags, dma);
}

struct xhci_ring *xhci_dma_to_transfer_ring(
struct xhci_virt_ep *ep,
u64 address)
{
if (ep->ep_state & EP_HAS_STREAMS)
return radix_tree_lookup(&ep->stream_info->trb_address_map,
address >> SEGMENT_SHIFT);
return ep->ring;
}

/* Only use this when you know stream_info is valid */
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
struct xhci_ring *dma_to_stream_ring(
static struct xhci_ring *dma_to_stream_ring(
struct xhci_stream_info *stream_info,
u64 address)
{
Expand All @@ -363,6 +374,66 @@ struct xhci_ring *dma_to_stream_ring(
}
#endif /* CONFIG_USB_XHCI_HCD_DEBUGGING */

struct xhci_ring *xhci_stream_id_to_ring(
struct xhci_virt_device *dev,
unsigned int ep_index,
unsigned int stream_id)
{
struct xhci_virt_ep *ep = &dev->eps[ep_index];

if (stream_id == 0)
return ep->ring;
if (!ep->stream_info)
return NULL;

if (stream_id > ep->stream_info->num_streams)
return NULL;
return ep->stream_info->stream_rings[stream_id];
}

struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id)
{
struct xhci_virt_ep *ep;

ep = &xhci->devs[slot_id]->eps[ep_index];
/* Common case: no streams */
if (!(ep->ep_state & EP_HAS_STREAMS))
return ep->ring;

if (stream_id == 0) {
xhci_warn(xhci,
"WARN: Slot ID %u, ep index %u has streams, "
"but URB has no stream ID.\n",
slot_id, ep_index);
return NULL;
}

if (stream_id < ep->stream_info->num_streams)
return ep->stream_info->stream_rings[stream_id];

xhci_warn(xhci,
"WARN: Slot ID %u, ep index %u has "
"stream IDs 1 to %u allocated, "
"but stream ID %u is requested.\n",
slot_id, ep_index,
ep->stream_info->num_streams - 1,
stream_id);
return NULL;
}

/* Get the right ring for the given URB.
* If the endpoint supports streams, boundary check the URB's stream ID.
* If the endpoint doesn't support streams, return the singular endpoint ring.
*/
struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
struct urb *urb)
{
return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
}

#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
static int xhci_test_radix_tree(struct xhci_hcd *xhci,
unsigned int num_streams,
Expand Down Expand Up @@ -515,6 +586,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
cur_ring = stream_info->stream_rings[cur_stream];
if (!cur_ring)
goto cleanup_rings;
cur_ring->stream_id = cur_stream;
/* Set deq ptr, cycle bit, and stream context type */
addr = cur_ring->first_seg->dma |
SCT_FOR_CTX(SCT_PRI_TR) |
Expand Down
Loading

0 comments on commit e9df17e

Please sign in to comment.