On Tue, 22 Apr 2014, Russel Hughes wrote: > > More importantly, the routine sets urb->start_frame to the current > > value of the frame counter. This is completely wrong; urb->start_frame > > is supposed to be the (micro-)frame number for when the transfer > > begins, not when the transfer was submitted. > > > > As far as I can tell, the only way to do this correctly is to set the > > Frame ID field (with SIA = 0) in the first TD of an isochronous stream, > > and then set SIA = 1 in all the following TDs (see 4.11.2.5). That > > way, xhci-hcd will know exactly when the stream begins, so it can keep > > track of the frame in which each URB starts. Dealing with underruns is > > left as an exercise for the implementer... > > > > Let me know if you want any changes tested using my DAC that reliably > shows the problem. Russel, here's a patch you can test. It's only a partial fix for the problem, because it doesn't handle over/underruns. Still, it would be nice to see if the patch makes any difference in normal operation. Even if it doesn't fix the problem, please post a short stretch (a few hundred lines) from a usbmon trace with the patch installed. Alan Stern Index: usb-3.15/drivers/usb/host/xhci-ring.c =================================================================== --- usb-3.15.orig/drivers/usb/host/xhci-ring.c +++ usb-3.15/drivers/usb/host/xhci-ring.c @@ -3153,7 +3153,7 @@ static void check_trb_math(struct urb *u static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, unsigned int ep_index, unsigned int stream_id, int start_cycle, - struct xhci_generic_trb *start_trb) + struct xhci_generic_trb *start_trb, bool ring_doorbell) { /* * Pass all the TRBs to the hardware at once and make sure this write @@ -3164,7 +3164,8 @@ static void giveback_first_trb(struct xh start_trb->field[3] |= cpu_to_le32(start_cycle); else start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); - xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); + if (ring_doorbell) + xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); } /* @@ -3406,7 +3407,7 @@ static int queue_bulk_sg_tx(struct xhci_ check_trb_math(urb, num_trbs, running_total); giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, - start_cycle, start_trb); + start_cycle, start_trb, true); return 0; } @@ -3545,7 +3546,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd * check_trb_math(urb, num_trbs, running_total); giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, - start_cycle, start_trb); + start_cycle, start_trb, true); return 0; } @@ -3662,7 +3663,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state); giveback_first_trb(xhci, slot_id, ep_index, 0, - start_cycle, start_trb); + start_cycle, start_trb, true); return 0; } @@ -3742,8 +3743,10 @@ static unsigned int xhci_get_last_burst_ /* This is for isoc transfer */ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, - struct urb *urb, int slot_id, unsigned int ep_index) + struct urb *urb, int slot_id, unsigned int ep_index, + unsigned esit) { + struct xhci_virt_ep *ep; struct xhci_ring *ep_ring; struct urb_priv *urb_priv; struct xhci_td *td; @@ -3756,8 +3759,11 @@ static int xhci_queue_isoc_tx(struct xhc u64 start_addr, addr; int i, j; bool more_trbs_coming; + bool new_isoc_stream; + unsigned start_uframe; - ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; + ep = &xhci->devs[slot_id]->eps[ep_index]; + ep_ring = ep->ring; num_tds = urb->number_of_packets; if (num_tds < 1) { @@ -3765,6 +3771,8 @@ static int xhci_queue_isoc_tx(struct xhc return -EINVAL; } + new_isoc_stream = list_empty(&urb->ep->urb_list); + start_addr = (u64) urb->transfer_dma; start_trb = &ep_ring->enqueue->generic; start_cycle = ep_ring->cycle_state; @@ -3826,10 +3834,6 @@ static int xhci_queue_isoc_tx(struct xhc field |= ep_ring->cycle_state; } - /* Only set interrupt on short packet for IN EPs */ - if (usb_urb_dir_in(urb)) - field |= TRB_ISP; - /* Chain all the TRBs together; clear the chain bit in * the last TRB to indicate it's the last TRB in the * chain. @@ -3895,8 +3899,52 @@ static int xhci_queue_isoc_tx(struct xhc } xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++; + /* + * For new isochronous streams, schedule the transfer to start + * in the first slot beyond the Isochronous Scheduling Threshold + * (4.14.2.1.4). Otherwise, leave the SIA (Schedule Isochronous ASAP) + * bit set in the first TRB. Over/underruns are handled when the + * events are received. + */ + if (new_isoc_stream) { + unsigned ist = HCS_IST(xhci->hcs_params2); + unsigned boundary; + + start_uframe = readl(&xhci->run_regs->microframe_index); + + /* IST is given in frames if bit 3 is set, else in uframes */ + start_uframe += (ist & 0x8) ? (ist & 0x7) << 3 : ist & 0x7; + + /* Round up to the following frame or ESIT boundary */ + boundary = max(esit, 8u); + start_uframe = (start_uframe + boundary) & (- boundary); + start_uframe &= MFINDEX_MASK; + + /* Store the starting frame number in the first TRB */ + start_trb->field[3] &= cpu_to_le32(~TRB_SIA); + start_trb->field[3] |= cpu_to_le32( + TRB_FRAME_ID(start_uframe >> 3)); + } else { + start_uframe = ep->next_uframe; + } + ep->next_uframe = (start_uframe + esit * num_tds) & MFINDEX_MASK; + + urb->start_frame = start_uframe; + if (urb->dev->speed == USB_SPEED_FULL) + urb->start_frame >>= 3; + + /* + * Ring the endpoint doorbell only for the first TRB of a new + * isochronous stream. Following TRBs should be queued sufficiently + * far in advance (beyond the IST) that no doorbell ring is needed. + * + * Indeed, if we did ring the doorbell again and an over/underrun + * occurred at the same time, we wouldn't know whether or not the + * endpoint had been removed from the periodic schedule, because + * ringing the doorbell reactivates endpoints (4.14.2.1). + */ giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, - start_cycle, start_trb); + start_cycle, start_trb, new_isoc_stream); return 0; cleanup: /* Clean up a partially enqueued isoc transfer. */ @@ -3935,7 +3983,6 @@ int xhci_queue_isoc_tx_prepare(struct xh struct xhci_virt_device *xdev; struct xhci_ring *ep_ring; struct xhci_ep_ctx *ep_ctx; - int start_frame; int xhci_interval; int ep_interval; int num_tds, num_trbs, i; @@ -3958,19 +4005,10 @@ int xhci_queue_isoc_tx_prepare(struct xh if (ret) return ret; - start_frame = readl(&xhci->run_regs->microframe_index); - start_frame &= 0x3fff; - - urb->start_frame = start_frame; - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->speed == USB_SPEED_FULL) - urb->start_frame >>= 3; - xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info)); ep_interval = urb->interval; /* Convert to microframes */ - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->speed == USB_SPEED_FULL) + if (urb->dev->speed == USB_SPEED_FULL) ep_interval *= 8; /* FIXME change this to a warning and a suggestion to use the new API * to set the polling interval (once the API is added). @@ -3982,13 +4020,13 @@ int xhci_queue_isoc_tx_prepare(struct xh xhci_interval, xhci_interval == 1 ? "" : "s"); urb->interval = xhci_interval; /* Convert back to frames for LS/FS devices */ - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->speed == USB_SPEED_FULL) + if (urb->dev->speed == USB_SPEED_FULL) urb->interval /= 8; } ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free; - return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); + return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index, + xhci_interval); } /**** Command Ring Operations ****/ Index: usb-3.15/drivers/usb/host/xhci.h =================================================================== --- usb-3.15.orig/drivers/usb/host/xhci.h +++ usb-3.15/drivers/usb/host/xhci.h @@ -481,6 +481,8 @@ struct xhci_run_regs { struct xhci_intr_reg ir_set[128]; }; +#define MFINDEX_MASK 0x3fff + /** * struct doorbell_array * @@ -887,6 +889,8 @@ struct xhci_virt_ep { * process the missed tds on the endpoint ring. */ bool skip; + unsigned next_uframe; /* isochronous scheduling */ + /* Bandwidth checking storage */ struct xhci_bw_info bw_info; struct list_head bw_endpoint_list; @@ -1167,6 +1171,7 @@ enum xhci_setup_dev { /* Isochronous TRB specific fields */ #define TRB_SIA (1<<31) +#define TRB_FRAME_ID(p) ((p) << 20) struct xhci_generic_trb { __le32 field[4]; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html