git: 99977369433d - stable/13 - Factor out repeated code in the USB controller drivers to avoid bugs computing the same isochronous start frame number over and over again.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 10 Mar 2022 08:35:38 UTC
The branch stable/13 has been updated by hselasky: URL: https://cgit.FreeBSD.org/src/commit/?id=99977369433de3eaec6907e51bcabc1dbb088628 commit 99977369433de3eaec6907e51bcabc1dbb088628 Author: Hans Petter Selasky <hselasky@FreeBSD.org> AuthorDate: 2021-07-10 16:17:51 +0000 Commit: Hans Petter Selasky <hselasky@FreeBSD.org> CommitDate: 2022-03-10 08:29:19 +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 Sponsored by: NVIDIA Networking (cherry picked from commit 8fc2a3c41791b205a107dc2bec16ac7514a57958) (cherry picked from commit f52783fcf5cc60734121d061beef0d4ea47b224a) (cherry picked from commit cf48d1f77126d8de4c03c4dd7c8502be2b5f1954) --- 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 | 88 ++++++++++++--------------------- sys/dev/usb/usb_transfer.c | 69 ++++++++++++++++++++++++++ sys/dev/usb/usb_transfer.h | 1 + 12 files changed, 142 insertions(+), 452 deletions(-) diff --git a/sys/dev/usb/controller/atmegadci.c b/sys/dev/usb/controller/atmegadci.c index a52b8ef0c19c..84f331f557a3 100644 --- a/sys/dev/usb/controller/atmegadci.c +++ b/sys/dev/usb/controller/atmegadci.c @@ -1420,7 +1420,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", @@ -1432,41 +1431,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 d999f1982e9e..20f9a0e6bd4b 100644 --- a/sys/dev/usb/controller/avr32dci.c +++ b/sys/dev/usb/controller/avr32dci.c @@ -1354,7 +1354,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; @@ -1365,41 +1364,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 869c44c3bb1a..5a1f2d271251 100644 --- a/sys/dev/usb/controller/dwc_otg.c +++ b/sys/dev/usb/controller/dwc_otg.c @@ -4188,9 +4188,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); @@ -4213,52 +4211,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 9dd24a837316..aa24544f8893 100644 --- a/sys/dev/usb/controller/musb_otg.c +++ b/sys/dev/usb/controller/musb_otg.c @@ -3435,9 +3435,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); @@ -3446,45 +3444,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 86bfe0b1108a..4c72118d9900 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 4fc6d5dbfea2..7bbfdace3e72 100644 --- a/sys/dev/usb/controller/uss820dci.c +++ b/sys/dev/usb/controller/uss820dci.c @@ -1705,7 +1705,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", @@ -1715,39 +1714,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 f42428614bbb..9c0f53de4924 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; @@ -2078,64 +2078,58 @@ xhci_setup_generic_chain(struct usb_xfer *xfer) x = XREAD4(temp.sc, runt, XHCI_MFINDEX); - 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); + DPRINTF("MFINDEX=0x%08x IST=0x%x\n", x, temp.sc->sc_ist); 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); + y = 7; + } 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 +3066,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 +3075,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 0dd750c33c0d..60841f473334 100644 --- a/sys/dev/usb/usb_transfer.h +++ b/sys/dev/usb/usb_transfer.h @@ -147,5 +147,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_ */