return TRB_TYPE_LINK_LE32(trb->link.control);
}
-static bool last_trb_on_seg(struct xhci_segment *seg, union xhci_trb *trb)
+static bool last_trb_on_seg(struct xhci_segment *seg,
+ unsigned int trbs_per_seg, union xhci_trb *trb)
{
- return trb == &seg->trbs[TRBS_PER_SEGMENT - 1];
+ return trb == &seg->trbs[trbs_per_seg - 1];
}
static bool last_trb_on_ring(struct xhci_ring *ring,
struct xhci_segment *seg, union xhci_trb *trb)
{
- return last_trb_on_seg(seg, trb) && (seg->next == ring->first_seg);
+ return last_trb_on_seg(seg, ring->trbs_per_seg, trb) && (seg->next == ring->first_seg);
}
static bool link_trb_toggles_cycle(union xhci_trb *trb)
{
/* event ring doesn't have link trbs, check for last trb */
if (ring->type == TYPE_EVENT) {
- if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
+ if (!last_trb_on_seg(ring->deq_seg, ring->trbs_per_seg,
+ ring->dequeue)) {
ring->dequeue++;
goto out;
}
return 0;
if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) {
+ /*
+ * If the ring has a single segment the dequeue segment
+ * never changes, so don't use it as measure of free space.
+ */
+ if (ring->num_segs == 1)
+ return ring->num_trbs_free >= num_trbs;
num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs;
if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg)
return 0;
} while (!cycle_found || !td_last_trb_found);
+ /*
+ * Quirk: the xHC does not correctly parse link TRBs if the HW Dequeue
+ * pointer is set to one. Advance to the next TRB (and next segment).
+ */
+ if (xhci->quirks & XHCI_AVOID_DQ_ON_LINK && trb_is_link(new_deq)) {
+ if (link_trb_toggles_cycle(new_deq))
+ state->new_cycle_state ^= 0x1;
+ next_trb(xhci, ep_ring, &new_seg, &new_deq);
+ }
+
state->new_deq_seg = new_seg;
state->new_deq_ptr = new_deq;
* that clears the EHB.
*/
while (xhci_handle_event(xhci) > 0) {
- if (event_loop++ < TRBS_PER_SEGMENT / 2)
+ if (event_loop++ < xhci->event_ring->trbs_per_seg / 2)
continue;
xhci_update_erst_dequeue(xhci, event_ring_deq);
event_loop = 0;
}
ep = &xhci->devs[slot_id]->eps[ep_index];
if ((ep->ep_state & SET_DEQ_PENDING)) {
- xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n");
- xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n");
- return;
+ xhci_warn(xhci, "WARN A Set TR Deq Ptr command is pending for slot %u ep %u\n",
+ slot_id, ep_index);
+ ep->ep_state &= ~SET_DEQ_PENDING;
}
/* This function gets called from contexts where it cannot sleep */