git: 8fc2a3c41791 - main - Factor out repeated code in the USB controller drivers to avoid bugs computing the same isochronous start frame number over and over again.

Hans Petter Selasky hselasky at FreeBSD.org
Sat Jul 10 19:04:51 UTC 2021


The branch main has been updated by hselasky:

URL: https://cgit.FreeBSD.org/src/commit/?id=8fc2a3c41791b205a107dc2bec16ac7514a57958

commit 8fc2a3c41791b205a107dc2bec16ac7514a57958
Author:     Hans Petter Selasky <hselasky at FreeBSD.org>
AuthorDate: 2021-07-10 16:17:51 +0000
Commit:     Hans Petter Selasky <hselasky at FreeBSD.org>
CommitDate: 2021-07-10 18:59:00 +0000

    Factor out repeated code in the USB controller drivers to avoid bugs
    computing the same isochronous start frame number over and over again.
    
    PR:             257082
    MFC after:      1 week
    Sponsored by:   NVIDIA Networking
---
 sys/dev/usb/controller/atmegadci.c   | 37 +-------------
 sys/dev/usb/controller/avr32dci.c    | 37 +-------------
 sys/dev/usb/controller/dwc_otg.c     | 45 +----------------
 sys/dev/usb/controller/ehci.c        | 96 ++++++------------------------------
 sys/dev/usb/controller/musb_otg.c    | 42 +---------------
 sys/dev/usb/controller/ohci.c        | 33 +++----------
 sys/dev/usb/controller/saf1761_otg.c | 70 ++------------------------
 sys/dev/usb/controller/uhci.c        | 41 +++------------
 sys/dev/usb/controller/uss820dci.c   | 35 +------------
 sys/dev/usb/controller/xhci.c        | 85 +++++++++++--------------------
 sys/dev/usb/usb_transfer.c           | 69 ++++++++++++++++++++++++++
 sys/dev/usb/usb_transfer.h           |  1 +
 12 files changed, 140 insertions(+), 451 deletions(-)

diff --git a/sys/dev/usb/controller/atmegadci.c b/sys/dev/usb/controller/atmegadci.c
index 1f91948c4ccd..06bee13c3a59 100644
--- a/sys/dev/usb/controller/atmegadci.c
+++ b/sys/dev/usb/controller/atmegadci.c
@@ -1421,7 +1421,6 @@ static void
 atmegadci_device_isoc_fs_enter(struct usb_xfer *xfer)
 {
 	struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
-	uint32_t temp;
 	uint32_t nframes;
 
 	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
@@ -1433,41 +1432,9 @@ atmegadci_device_isoc_fs_enter(struct usb_xfer *xfer)
 	    (ATMEGA_READ_1(sc, ATMEGA_UDFNUMH) << 8) |
 	    (ATMEGA_READ_1(sc, ATMEGA_UDFNUML));
 
-	nframes &= ATMEGA_FRAME_MASK;
-
-	/*
-	 * check if the frame index is within the window where the frames
-	 * will be inserted
-	 */
-	temp = (nframes - xfer->endpoint->isoc_next) & ATMEGA_FRAME_MASK;
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & ATMEGA_FRAME_MASK;
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, ATMEGA_FRAME_MASK, NULL))
 		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) & ATMEGA_FRAME_MASK;
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    xfer->nframes;
-
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += xfer->nframes;
 
 	/* setup TDs */
 	atmegadci_setup_standard_chain(xfer);
diff --git a/sys/dev/usb/controller/avr32dci.c b/sys/dev/usb/controller/avr32dci.c
index 7cc5ca5d90c7..0ab9a8ff665d 100644
--- a/sys/dev/usb/controller/avr32dci.c
+++ b/sys/dev/usb/controller/avr32dci.c
@@ -1355,7 +1355,6 @@ static void
 avr32dci_device_isoc_fs_enter(struct usb_xfer *xfer)
 {
 	struct avr32dci_softc *sc = AVR32_BUS2SC(xfer->xroot->bus);
-	uint32_t temp;
 	uint32_t nframes;
 	uint8_t ep_no;
 
@@ -1366,41 +1365,9 @@ avr32dci_device_isoc_fs_enter(struct usb_xfer *xfer)
 	ep_no = xfer->endpointno & UE_ADDR;
 	nframes = (AVR32_READ_4(sc, AVR32_FNUM) / 8);
 
-	nframes &= AVR32_FRAME_MASK;
-
-	/*
-	 * check if the frame index is within the window where the frames
-	 * will be inserted
-	 */
-	temp = (nframes - xfer->endpoint->isoc_next) & AVR32_FRAME_MASK;
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & AVR32_FRAME_MASK;
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, AVR32_FRAME_MASK, NULL))
 		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) & AVR32_FRAME_MASK;
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    xfer->nframes;
-
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += xfer->nframes;
 
 	/* setup TDs */
 	avr32dci_setup_standard_chain(xfer);
diff --git a/sys/dev/usb/controller/dwc_otg.c b/sys/dev/usb/controller/dwc_otg.c
index 421b95593b3a..8192c7b011e9 100644
--- a/sys/dev/usb/controller/dwc_otg.c
+++ b/sys/dev/usb/controller/dwc_otg.c
@@ -4189,9 +4189,7 @@ dwc_otg_device_isoc_start(struct usb_xfer *xfer)
 {
 	struct dwc_otg_softc *sc = DWC_OTG_BUS2SC(xfer->xroot->bus);
 	uint32_t temp;
-	uint32_t msframes;
 	uint32_t framenum;
-	uint8_t shift = usbd_xfer_get_fps_shift(xfer);
 
 	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
 	    xfer, xfer->endpoint->isoc_next, xfer->nframes);
@@ -4214,52 +4212,13 @@ dwc_otg_device_isoc_start(struct usb_xfer *xfer)
 	if (sc->sc_flags.status_high_speed)
 		framenum /= 8;
 
-	framenum &= DWC_OTG_FRAME_MASK;
-
-	/*
-	 * Compute number of milliseconds worth of data traffic for
-	 * this USB transfer:
-	 */ 
-	if (xfer->xroot->udev->speed == USB_SPEED_HIGH)
-		msframes = ((xfer->nframes << shift) + 7) / 8;
-	else
-		msframes = xfer->nframes;
-
-	/*
-	 * check if the frame index is within the window where the frames
-	 * will be inserted
-	 */
-	temp = (framenum - xfer->endpoint->isoc_next) & DWC_OTG_FRAME_MASK;
-
-	if ((xfer->endpoint->is_synced == 0) || (temp < msframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (framenum + 3) & DWC_OTG_FRAME_MASK;
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, framenum, 0, 1, DWC_OTG_FRAME_MASK, NULL))
 		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - framenum) & DWC_OTG_FRAME_MASK;
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-		usb_isoc_time_expand(&sc->sc_bus, framenum) + temp + msframes;
 
 	/* setup TDs */
 	dwc_otg_setup_standard_chain(xfer);
 
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += msframes;
-
 	/* start TD chain */
 	dwc_otg_start_standard_chain(xfer);
 }
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
index 0b7f41b39234..dd4f7c568625 100644
--- a/sys/dev/usb/controller/ehci.c
+++ b/sys/dev/usb/controller/ehci.c
@@ -2440,6 +2440,7 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
 	uint32_t *plen;
 	uint32_t buf_offset;
 	uint32_t nframes;
+	uint32_t startframe;
 	uint32_t temp;
 	uint32_t sitd_mask;
 	uint16_t tlen;
@@ -2458,39 +2459,9 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
 
 	nframes = EOREAD4(sc, EHCI_FRINDEX) / 8;
 
-	/*
-	 * check if the frame index is within the window where the frames
-	 * will be inserted
-	 */
-	buf_offset = (nframes - xfer->endpoint->isoc_next) &
-	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (buf_offset < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is empty we
-		 * schedule the transfer a few frames ahead of the current
-		 * frame position. Else two isochronous transfers might
-		 * overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) &
-		    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-		xfer->endpoint->is_synced = 1;
-		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	buf_offset = (xfer->endpoint->isoc_next - nframes) &
-	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) +
-	    buf_offset + xfer->nframes;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, EHCI_VIRTUAL_FRAMELIST_COUNT - 1, &startframe))
+		DPRINTFN(3, "start next=%d\n", startframe);
 
 	/* get the real number of frames */
 
@@ -2507,11 +2478,11 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
 	td = xfer->td_start[xfer->flags_int.curr_dma_set];
 	xfer->td_transfer_first = td;
 
-	pp_last = &sc->sc_isoc_fs_p_last[xfer->endpoint->isoc_next];
+	pp_last = &sc->sc_isoc_fs_p_last[startframe];
 
 	/* store starting position */
 
-	xfer->qh_pos = xfer->endpoint->isoc_next;
+	xfer->qh_pos = startframe;
 
 	while (nframes--) {
 		if (td == NULL) {
@@ -2633,10 +2604,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
 
 	xfer->td_transfer_last = td_last;
 
-	/* update isoc_next */
-	xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) &
-	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-
 	/*
 	 * We don't allow cancelling of the SPLIT transaction USB FULL
 	 * speed transfer, because it disturbs the bandwidth
@@ -2743,11 +2710,11 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
 	uint32_t status;
 	uint32_t buf_offset;
 	uint32_t nframes;
+	uint32_t startframe;
 	uint32_t itd_offset[8 + 1];
 	uint8_t x;
 	uint8_t td_no;
 	uint8_t page_no;
-	uint8_t shift = usbd_xfer_get_fps_shift(xfer);
 
 #ifdef USB_DEBUG
 	uint8_t once = 1;
@@ -2755,47 +2722,16 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
 #endif
 
 	DPRINTFN(6, "xfer=%p next=%d nframes=%d shift=%d\n",
-	    xfer, xfer->endpoint->isoc_next, xfer->nframes, (int)shift);
+	    xfer, xfer->endpoint->isoc_next, xfer->nframes,
+	    usbd_xfer_get_fps_shift(xfer));
 
 	/* get the current frame index */
 
 	nframes = EOREAD4(sc, EHCI_FRINDEX) / 8;
 
-	/*
-	 * check if the frame index is within the window where the frames
-	 * will be inserted
-	 */
-	buf_offset = (nframes - xfer->endpoint->isoc_next) &
-	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (buf_offset < (((xfer->nframes << shift) + 7) / 8))) {
-		/*
-		 * If there is data underflow or the pipe queue is empty we
-		 * schedule the transfer a few frames ahead of the current
-		 * frame position. Else two isochronous transfers might
-		 * overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) &
-		    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-		xfer->endpoint->is_synced = 1;
-		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	buf_offset = (xfer->endpoint->isoc_next - nframes) &
-	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset +
-	    (((xfer->nframes << shift) + 7) / 8);
-
-	/* get the real number of frames */
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, EHCI_VIRTUAL_FRAMELIST_COUNT - 1, &startframe))
+		DPRINTFN(3, "start next=%d\n", startframe);
 
 	nframes = xfer->nframes;
 
@@ -2811,11 +2747,11 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
 	td = xfer->td_start[xfer->flags_int.curr_dma_set];
 	xfer->td_transfer_first = td;
 
-	pp_last = &sc->sc_isoc_hs_p_last[xfer->endpoint->isoc_next];
+	pp_last = &sc->sc_isoc_hs_p_last[startframe];
 
 	/* store starting position */
 
-	xfer->qh_pos = xfer->endpoint->isoc_next;
+	xfer->qh_pos = startframe;
 
 	while (nframes) {
 		if (td == NULL) {
@@ -2927,10 +2863,6 @@ ehci_device_isoc_hs_enter(struct usb_xfer *xfer)
 	}
 
 	xfer->td_transfer_last = td_last;
-
-	/* update isoc_next */
-	xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) &
-	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
 }
 
 static void
diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c
index 24eba2a9a552..fd2f7e72c43a 100644
--- a/sys/dev/usb/controller/musb_otg.c
+++ b/sys/dev/usb/controller/musb_otg.c
@@ -3436,9 +3436,7 @@ static void
 musbotg_device_isoc_enter(struct usb_xfer *xfer)
 {
 	struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
-	uint32_t temp;
 	uint32_t nframes;
-	uint32_t fs_frames;
 
 	DPRINTFN(5, "xfer=%p next=%d nframes=%d\n",
 	    xfer, xfer->endpoint->isoc_next, xfer->nframes);
@@ -3447,45 +3445,9 @@ musbotg_device_isoc_enter(struct usb_xfer *xfer)
 
 	nframes = MUSB2_READ_2(sc, MUSB2_REG_FRAME);
 
-	/*
-	 * check if the frame index is within the window where the frames
-	 * will be inserted
-	 */
-	temp = (nframes - xfer->endpoint->isoc_next) & MUSB2_MASK_FRAME;
-
-	if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
-		fs_frames = (xfer->nframes + 7) / 8;
-	} else {
-		fs_frames = xfer->nframes;
-	}
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < fs_frames)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & MUSB2_MASK_FRAME;
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, MUSB2_MASK_FRAME, NULL))
 		DPRINTFN(2, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) & MUSB2_MASK_FRAME;
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    fs_frames;
-
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += fs_frames;
 
 	/* setup TDs */
 	musbotg_setup_standard_chain(xfer);
diff --git a/sys/dev/usb/controller/ohci.c b/sys/dev/usb/controller/ohci.c
index 39f3bd54f507..7268af06a602 100644
--- a/sys/dev/usb/controller/ohci.c
+++ b/sys/dev/usb/controller/ohci.c
@@ -1824,6 +1824,7 @@ ohci_device_isoc_enter(struct usb_xfer *xfer)
 	struct ohci_hcca *hcca;
 	uint32_t buf_offset;
 	uint32_t nframes;
+	uint32_t startframe;
 	uint32_t ed_flags;
 	uint32_t *plen;
 	uint16_t itd_offset[OHCI_ITD_NOFFSET];
@@ -1840,31 +1841,9 @@ ohci_device_isoc_enter(struct usb_xfer *xfer)
 	DPRINTFN(6, "xfer=%p isoc_next=%u nframes=%u hcca_fn=%u\n",
 	    xfer, xfer->endpoint->isoc_next, xfer->nframes, nframes);
 
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (((nframes - xfer->endpoint->isoc_next) & 0xFFFF) < xfer->nframes) ||
-	    (((xfer->endpoint->isoc_next - nframes) & 0xFFFF) >= 128)) {
-		/*
-		 * If there is data underflow or the pipe queue is empty we
-		 * schedule the transfer a few frames ahead of the current
-		 * frame position. Else two isochronous transfers might
-		 * overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & 0xFFFF;
-		xfer->endpoint->is_synced = 1;
-		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	buf_offset = ((xfer->endpoint->isoc_next - nframes) & 0xFFFF);
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    (usb_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset +
-	    xfer->nframes);
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, 0xFFFF, &startframe))
+		DPRINTFN(3, "start next=%d\n", startframe);
 
 	/* get the real number of frames */
 
@@ -1905,12 +1884,12 @@ ohci_device_isoc_enter(struct usb_xfer *xfer)
 			/* fill current ITD */
 			td->itd_flags = htole32(
 			    OHCI_ITD_NOCC |
-			    OHCI_ITD_SET_SF(xfer->endpoint->isoc_next) |
+			    OHCI_ITD_SET_SF(startframe) |
 			    OHCI_ITD_NOINTR |
 			    OHCI_ITD_SET_FC(ncur));
 
 			td->frames = ncur;
-			xfer->endpoint->isoc_next += ncur;
+			startframe += ncur;
 
 			if (length == 0) {
 				/* all zero */
diff --git a/sys/dev/usb/controller/saf1761_otg.c b/sys/dev/usb/controller/saf1761_otg.c
index df0162e51749..f5725a3cb48a 100644
--- a/sys/dev/usb/controller/saf1761_otg.c
+++ b/sys/dev/usb/controller/saf1761_otg.c
@@ -2632,7 +2632,6 @@ static void
 saf1761_otg_device_isoc_enter(struct usb_xfer *xfer)
 {
 	struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(xfer->xroot->bus);
-	uint32_t temp;
 	uint32_t nframes;
 
 	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
@@ -2642,39 +2641,9 @@ saf1761_otg_device_isoc_enter(struct usb_xfer *xfer)
 
 	nframes = SAF1761_READ_LE_4(sc, SOTG_FRAME_NUM);
 
-	/*
-	 * check if the frame index is within the window where the
-	 * frames will be inserted
-	 */
-	temp = (nframes - xfer->endpoint->isoc_next) & SOTG_FRAME_NUM_SOFR_MASK;
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & SOTG_FRAME_NUM_SOFR_MASK;
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, SOTG_FRAME_NUM_SOFR_MASK, NULL))
 		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) & SOTG_FRAME_NUM_SOFR_MASK;
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    xfer->nframes;
-
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += xfer->nframes;
 
 	/* setup TDs */
 	saf1761_otg_setup_standard_chain(xfer);
@@ -2714,7 +2683,6 @@ static void
 saf1761_otg_host_isoc_enter(struct usb_xfer *xfer)
 {
 	struct saf1761_otg_softc *sc = SAF1761_OTG_BUS2SC(xfer->xroot->bus);
-	uint32_t temp;
 	uint32_t nframes;
 
 	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
@@ -2724,39 +2692,9 @@ saf1761_otg_host_isoc_enter(struct usb_xfer *xfer)
 
 	nframes = (SAF1761_READ_LE_4(sc, SOTG_FRINDEX) & SOTG_FRINDEX_MASK) >> 3;
 
-	/*
-	 * check if the frame index is within the window where the
-	 * frames will be inserted
-	 */
-	temp = (nframes - xfer->endpoint->isoc_next) & (SOTG_FRINDEX_MASK >> 3);
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & (SOTG_FRINDEX_MASK >> 3);
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, SOTG_FRINDEX_MASK >> 3, NULL))
 		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) & (SOTG_FRINDEX_MASK >> 3);
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    xfer->nframes;
-
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += xfer->nframes;
 
 	/* setup TDs */
 	saf1761_otg_setup_standard_chain(xfer);
diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c
index 59453359deb0..41caa1610989 100644
--- a/sys/dev/usb/controller/uhci.c
+++ b/sys/dev/usb/controller/uhci.c
@@ -2111,7 +2111,7 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
 	struct uhci_mem_layout ml;
 	uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
 	uint32_t nframes;
-	uint32_t temp;
+	uint32_t startframe;
 	uint32_t *plen;
 
 #ifdef USB_DEBUG
@@ -2127,34 +2127,9 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
 
 	nframes = UREAD2(sc, UHCI_FRNUM);
 
-	temp = (nframes - xfer->endpoint->isoc_next) &
-	    (UHCI_VFRAMELIST_COUNT - 1);
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is empty we
-		 * schedule the transfer a few frames ahead of the current
-		 * frame position. Else two isochronous transfers might
-		 * overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & (UHCI_VFRAMELIST_COUNT - 1);
-		xfer->endpoint->is_synced = 1;
-		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) &
-	    (UHCI_VFRAMELIST_COUNT - 1);
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    xfer->nframes;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, UHCI_VFRAMELIST_COUNT - 1, &startframe))
+		DPRINTFN(3, "start next=%d\n", startframe);
 
 	/* get the real number of frames */
 
@@ -2171,11 +2146,11 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
 	td = xfer->td_start[xfer->flags_int.curr_dma_set];
 	xfer->td_transfer_first = td;
 
-	pp_last = &sc->sc_isoc_p_last[xfer->endpoint->isoc_next];
+	pp_last = &sc->sc_isoc_p_last[startframe];
 
 	/* store starting position */
 
-	xfer->qh_pos = xfer->endpoint->isoc_next;
+	xfer->qh_pos = startframe;
 
 	while (nframes--) {
 		if (td == NULL) {
@@ -2252,10 +2227,6 @@ uhci_device_isoc_enter(struct usb_xfer *xfer)
 	}
 
 	xfer->td_transfer_last = td_last;
-
-	/* update isoc_next */
-	xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_p_last[0]) &
-	    (UHCI_VFRAMELIST_COUNT - 1);
 }
 
 static void
diff --git a/sys/dev/usb/controller/uss820dci.c b/sys/dev/usb/controller/uss820dci.c
index b2e112b07f6e..0c37a932b2a9 100644
--- a/sys/dev/usb/controller/uss820dci.c
+++ b/sys/dev/usb/controller/uss820dci.c
@@ -1706,7 +1706,6 @@ static void
 uss820dci_device_isoc_fs_enter(struct usb_xfer *xfer)
 {
 	struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
-	uint32_t temp;
 	uint32_t nframes;
 
 	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
@@ -1716,39 +1715,9 @@ uss820dci_device_isoc_fs_enter(struct usb_xfer *xfer)
 
 	nframes = USS820_READ_1(sc, USS820_SOFL);
 
-	/*
-	 * check if the frame index is within the window where the
-	 * frames will be inserted
-	 */
-	temp = (nframes - xfer->endpoint->isoc_next) & USS820_SOFL_MASK;
-
-	if ((xfer->endpoint->is_synced == 0) ||
-	    (temp < xfer->nframes)) {
-		/*
-		 * If there is data underflow or the pipe queue is
-		 * empty we schedule the transfer a few frames ahead
-		 * of the current frame position. Else two isochronous
-		 * transfers might overlap.
-		 */
-		xfer->endpoint->isoc_next = (nframes + 3) & USS820_SOFL_MASK;
-		xfer->endpoint->is_synced = 1;
+	if (usbd_xfer_get_isochronous_start_frame(
+	    xfer, nframes, 0, 1, USS820_SOFL_MASK, NULL))
 		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
-	}
-	/*
-	 * compute how many milliseconds the insertion is ahead of the
-	 * current frame position:
-	 */
-	temp = (xfer->endpoint->isoc_next - nframes) & USS820_SOFL_MASK;
-
-	/*
-	 * pre-compute when the isochronous transfer will be finished:
-	 */
-	xfer->isoc_time_complete =
-	    usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
-	    xfer->nframes;
-
-	/* compute frame number for next insertion */
-	xfer->endpoint->isoc_next += xfer->nframes;
 
 	/* setup TDs */
 	uss820dci_setup_standard_chain(xfer);
diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c
index dd64c2d45141..93468c5d9333 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -133,8 +133,8 @@ struct xhci_std_temp {
 	uint32_t		offset;
 	uint32_t		max_packet_size;
 	uint32_t		average;
+	uint32_t		isoc_frame;
 	uint16_t		isoc_delta;
-	uint16_t		isoc_frame;
 	uint8_t			shortpkt;
 	uint8_t			multishort;
 	uint8_t			last_frame;
@@ -2080,62 +2080,55 @@ xhci_setup_generic_chain(struct usb_xfer *xfer)
 
 		DPRINTF("MFINDEX=0x%08x IST=0x%x\n", x, sc->sc_ist);
 
-		/* add isochronous scheduling threshold */
-		if (temp.sc->sc_ist & 8)
-			x += (temp.sc->sc_ist & 7) << 3;
-		else
-			x += (temp.sc->sc_ist & 7);
-
 		switch (usbd_get_speed(xfer->xroot->udev)) {
 		case USB_SPEED_FULL:
 			shift = 3;
 			temp.isoc_delta = 8;	/* 1ms */
-			x += temp.isoc_delta - 1;
-			x &= ~(temp.isoc_delta - 1);
 			break;
 		default:
 			shift = usbd_xfer_get_fps_shift(xfer);
 			temp.isoc_delta = 1U << shift;
-			x += temp.isoc_delta - 1;
-			x &= ~(temp.isoc_delta - 1);
-			/* simple frame load balancing */
-			x += xfer->endpoint->usb_uframe;
 			break;
 		}
 
-		y = XHCI_MFINDEX_GET(x - xfer->endpoint->isoc_next);
+		/* Compute isochronous scheduling threshold. */
+		if (temp.sc->sc_ist & 8)
+			y = (temp.sc->sc_ist & 7) << 3;
+		else
+			y = (temp.sc->sc_ist & 7);
 
-		if ((xfer->endpoint->is_synced == 0) ||
-		    (y < (xfer->nframes << shift)) ||
-		    (XHCI_MFINDEX_GET(-y) >= (128 * 8))) {
+		/* Range check the IST. */
+		if (y < 8) {
+			y = 0;
+		} else if (y > 15) {
+			DPRINTFN(3, "IST(%d) is too big!\n", temp.sc->sc_ist);
 			/*
-			 * If there is data underflow or the pipe
-			 * queue is empty we schedule the transfer a
-			 * few frames ahead of the current frame
-			 * position. Else two isochronous transfers
-			 * might overlap.
+			 * The USB stack minimum isochronous transfer
+			 * size is typically 2x2 ms of payload. If the
+			 * IST makes is above 15 microframes, we have
+			 * an effective scheduling delay of more than
+			 * or equal to 2 milliseconds, which is too
+			 * much.
 			 */
-			xfer->endpoint->isoc_next = XHCI_MFINDEX_GET(x + (3 * 8));
-			xfer->endpoint->is_synced = 1;
-			temp.do_isoc_sync = 1;
-
-			DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
+		} else {
+			/*
+			 * Subtract one millisecond, because the
+			 * generic code adds that to the latency.
+			 */
+			y -= 8;
 		}
 
-		/* compute isochronous completion time */
-
-		y = XHCI_MFINDEX_GET(xfer->endpoint->isoc_next - (x & ~7));
+		if (usbd_xfer_get_isochronous_start_frame(
+		    xfer, x, y, 8, XHCI_MFINDEX_GET(-1), &temp.isoc_frame)) {
+			/* Start isochronous transfer at specified time. */
+			temp.do_isoc_sync = 1;
 
-		xfer->isoc_time_complete =
-		    usb_isoc_time_expand(&temp.sc->sc_bus, x / 8) +
-		    (y / 8) + (((xfer->nframes << shift) + 7) / 8);
+			DPRINTFN(3, "start next=%d\n", temp.isoc_frame);
+		}
 
 		x = 0;
-		temp.isoc_frame = xfer->endpoint->isoc_next;
 		temp.trb_type = XHCI_TRB_TYPE_ISOCH;
 
-		xfer->endpoint->isoc_next += xfer->nframes << shift;
-
 	} else if (xfer->flags_int.control_xfr) {
 		/* check if we should prepend a setup message */
 
@@ -3072,15 +3065,7 @@ xhci_device_done(struct usb_xfer *xfer, usb_error_t error)
 static void
 xhci_device_generic_open(struct usb_xfer *xfer)
 {
-	if (xfer->flags_int.isochronous_xfr) {
-		switch (xfer->xroot->udev->speed) {
-		case USB_SPEED_FULL:
-			break;
-		default:
-			usb_hs_bandwidth_alloc(xfer);
-			break;
-		}
-	}
+	DPRINTF("\n");
 }
 
 static void
@@ -3089,16 +3074,6 @@ xhci_device_generic_close(struct usb_xfer *xfer)
 	DPRINTF("\n");
 
 	xhci_device_done(xfer, USB_ERR_CANCELLED);
-
-	if (xfer->flags_int.isochronous_xfr) {
-		switch (xfer->xroot->udev->speed) {
-		case USB_SPEED_FULL:
-			break;
-		default:
-			usb_hs_bandwidth_free(xfer);
-			break;
-		}
-	}
 }
 
 static void
diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
index 871f8d729658..436e08db14a1 100644
--- a/sys/dev/usb/usb_transfer.c
+++ b/sys/dev/usb/usb_transfer.c
@@ -3627,3 +3627,72 @@ usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer)
 {
 	return (xfer->flags_int.maxp_was_clamped);
 }
+
+/*
+ * The following function computes the next isochronous frame number
+ * where the first isochronous packet should be queued.
+ *
+ * The function returns non-zero if there was a discontinuity.
+ * Else zero is returned for normal operation.
+ */
+uint8_t
+usbd_xfer_get_isochronous_start_frame(struct usb_xfer *xfer, uint32_t frame_curr,
+    uint32_t frame_min, uint32_t frame_ms, uint32_t frame_mask, uint32_t *p_frame_start)
+{
+	uint32_t duration;
+	uint32_t delta;
+	uint8_t retval;
+	uint8_t shift;
+
+	/* Compute time ahead of current schedule. */
+	delta = (xfer->endpoint->isoc_next - frame_curr) & frame_mask;
+
+	/*
+	 * Check if it is the first transfer or if the future frame
+	 * delta is less than one millisecond or if the frame delta is
+	 * negative:
+	 */
+	if (xfer->endpoint->is_synced == 0 ||
+	    delta < (frame_ms + frame_min) ||
+	    delta > (frame_mask / 2)) {
+		/* Schedule transfer 2 milliseconds into the future. */
+		xfer->endpoint->isoc_next = (frame_curr + 2 * frame_ms + frame_min) & frame_mask;
+		xfer->endpoint->is_synced = 1;
+
+		retval = 1;
+	} else {
+		retval = 0;
+	}
+
+	/* Store start time, if any. */
+	if (p_frame_start != NULL)
+		*p_frame_start = xfer->endpoint->isoc_next & frame_mask;
+
+	/* Get relative completion time, in milliseconds. */
+	delta = xfer->endpoint->isoc_next - frame_curr + (frame_curr % frame_ms);
+	delta &= frame_mask;
+	delta /= frame_ms;
+
+	switch (usbd_get_speed(xfer->xroot->udev)) {
+	case USB_SPEED_FULL:
+		shift = 3;
+		break;
+	default:
+		shift = usbd_xfer_get_fps_shift(xfer);
+		break;
+	}
+
+	/* Get duration in milliseconds, rounded up. */
+	duration = ((xfer->nframes << shift) + 7) / 8;
+
+	/* Compute full 32-bit completion time, in milliseconds. */
+	xfer->isoc_time_complete =
+	    usb_isoc_time_expand(xfer->xroot->bus, frame_curr / frame_ms) +
+	    delta + duration;
+
+	/* Compute next isochronous frame. */
+	xfer->endpoint->isoc_next += duration * frame_ms;
+	xfer->endpoint->isoc_next &= frame_mask;
+
+	return (retval);
+}
diff --git a/sys/dev/usb/usb_transfer.h b/sys/dev/usb/usb_transfer.h
index c6b97e1ab3c5..91f4f6790b31 100644
--- a/sys/dev/usb/usb_transfer.h
+++ b/sys/dev/usb/usb_transfer.h
@@ -250,5 +250,6 @@ void	usbd_transfer_timeout_ms(struct usb_xfer *xfer,
 	    void (*cb) (void *arg), usb_timeout_t ms);
 usb_timeout_t usbd_get_dma_delay(struct usb_device *udev);
 void	usbd_transfer_power_ref(struct usb_xfer *xfer, int val);
+uint8_t	usbd_xfer_get_isochronous_start_frame(struct usb_xfer *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t *);
 
 #endif					/* _USB_TRANSFER_H_ */


More information about the dev-commits-src-all mailing list