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);

Reply via email to