xhci(4) and ehci(4) are scheduling continuous isoc transfers. For that we have implemented a kind of workaround, which checks whether usbd_start_next() is calling in between and try to start an transfer which is still in progress.
NetBSD has introduced a variable called 'serialise' for the pipe, with which one can control whether to skip the usbd_start_next() call at all, and therefore schedule continuous transfers without the need for explicit checks whether the transfer is still in progress. By default we set 'serialise' to 1 in pipe initialization, so that the default behavior remains to call usbd_start_next() after a completed transfer. If we want to schedule continuous transfers, we overwrite 'serialise' with 0 in the HC driver. I like the approach and I think it's more straight forward than what we do today. It will also allow to easily implement continuous transfers in other HC drivers, i.e. in dwctwo(4). Comments? OKs? Index: sys/dev/usb/ehci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ehci.c,v retrieving revision 1.219 diff -u -p -u -p -r1.219 ehci.c --- sys/dev/usb/ehci.c 12 Apr 2022 19:41:11 -0000 1.219 +++ sys/dev/usb/ehci.c 29 Aug 2022 15:40:54 -0000 @@ -1505,6 +1505,7 @@ ehci_open(struct usbd_pipe *pipe) case EHCI_QH_SPEED_HIGH: case EHCI_QH_SPEED_FULL: pipe->methods = &ehci_device_isoc_methods; + pipe->serialise = 0; break; case EHCI_QH_SPEED_LOW: default: @@ -3247,7 +3248,7 @@ ehci_device_isoc_transfer(struct usbd_xf usbd_status err; err = usb_insert_transfer(xfer); - if (err && err != USBD_IN_PROGRESS) + if (err) return (err); return (ehci_device_isoc_start(xfer)); @@ -3267,15 +3268,6 @@ ehci_device_isoc_start(struct usbd_xfer KASSERT(!(xfer->rqflags & URQ_REQUEST)); KASSERT(ival > 0 && ival <= 16); - - /* - * To allow continuous transfers, above we start all transfers - * immediately. However, we're still going to get usbd_start_next call - * this when another xfer completes. So, check if this is already - * in progress or not - */ - if (ex->itdstart != NULL) - return (USBD_IN_PROGRESS); if (sc->sc_bus.dying) return (USBD_IOERROR); Index: sys/dev/usb/usb_subr.c =================================================================== RCS file: /cvs/src/sys/dev/usb/usb_subr.c,v retrieving revision 1.158 diff -u -p -u -p -r1.158 usb_subr.c --- sys/dev/usb/usb_subr.c 16 Feb 2022 06:23:42 -0000 1.158 +++ sys/dev/usb/usb_subr.c 29 Aug 2022 15:40:54 -0000 @@ -793,6 +793,7 @@ usbd_setup_pipe(struct usbd_device *dev, p->endpoint = ep; ep->refcnt++; p->interval = ival; + p->serialise = 1; SIMPLEQ_INIT(&p->queue); err = dev->bus->methods->open_pipe(p); if (err) { Index: sys/dev/usb/usbdi.c =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdi.c,v retrieving revision 1.110 diff -u -p -u -p -r1.110 usbdi.c --- sys/dev/usb/usbdi.c 3 Feb 2021 11:34:24 -0000 1.110 +++ sys/dev/usb/usbdi.c 29 Aug 2022 15:40:54 -0000 @@ -822,7 +822,7 @@ usb_transfer_complete(struct usbd_xfer * status == USBD_TIMEOUT) && pipe->iface != NULL) /* not control pipe */ pipe->running = 0; - else + else if (pipe->serialise) usbd_start_next(pipe); } } @@ -845,7 +845,7 @@ usb_insert_transfer(struct usbd_xfer *xf #endif s = splusb(); SIMPLEQ_INSERT_TAIL(&pipe->queue, xfer, next); - if (pipe->running) + if (pipe->running && pipe->serialise) err = USBD_IN_PROGRESS; else { pipe->running = 1; Index: sys/dev/usb/usbdivar.h =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.82 diff -u -p -u -p -r1.82 usbdivar.h --- sys/dev/usb/usbdivar.h 12 Apr 2022 19:41:11 -0000 1.82 +++ sys/dev/usb/usbdivar.h 29 Aug 2022 15:40:55 -0000 @@ -186,6 +186,7 @@ struct usbd_pipe { size_t pipe_size; char running; char aborting; + char serialise; SIMPLEQ_HEAD(, usbd_xfer) queue; LIST_ENTRY(usbd_pipe) next; Index: sys/dev/usb/xhci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/xhci.c,v retrieving revision 1.126 diff -u -p -u -p -r1.126 xhci.c --- sys/dev/usb/xhci.c 15 Jul 2022 07:52:06 -0000 1.126 +++ sys/dev/usb/xhci.c 29 Aug 2022 15:40:55 -0000 @@ -1224,6 +1224,7 @@ xhci_pipe_open(struct usbd_pipe *pipe) break; case UE_ISOCHRONOUS: pipe->methods = &xhci_device_isoc_methods; + pipe->serialise = 0; break; case UE_BULK: pipe->methods = &xhci_device_bulk_methods; @@ -3075,7 +3076,7 @@ xhci_device_isoc_transfer(struct usbd_xf usbd_status err; err = usb_insert_transfer(xfer); - if (err && err != USBD_IN_PROGRESS) + if (err) return (err); return (xhci_device_isoc_start(xfer)); @@ -3086,7 +3087,6 @@ xhci_device_isoc_start(struct usbd_xfer { struct xhci_softc *sc = (struct xhci_softc *)xfer->device->bus; struct xhci_pipe *xp = (struct xhci_pipe *)xfer->pipe; - struct xhci_xfer *xx = (struct xhci_xfer *)xfer; struct xhci_trb *trb0, *trb; uint32_t len, remain, flags; uint64_t paddr; @@ -3095,15 +3095,6 @@ xhci_device_isoc_start(struct usbd_xfer uint8_t toggle; KASSERT(!(xfer->rqflags & URQ_REQUEST)); - - /* - * To allow continuous transfers, above we start all transfers - * immediately. However, we're still going to get usbd_start_next call - * this when another xfer completes. So, check if this is already - * in progress or not - */ - if (xx->ntrb > 0) - return (USBD_IN_PROGRESS); if (sc->sc_bus.dying || xp->halted) return (USBD_IOERROR);