On Thu, Jun 13, 2019 at 06:03:03AM +0300, Artturi Alm wrote: > On Wed, Jun 12, 2019 at 03:55:59PM +0200, Alexandre Ratchov wrote: > > Currenty USB device driver code has no way to obtain how many frames > > can be scheduled on the HC. If it attempts to schedule too many > > frames, usbd_transfer() fails or silently misbehaves. > > > > For audio this is a big problem because the max frames count > > determines the block size which must be reported to upper layers > > before any transfer starts. This makes impossible to implement uaudio > > properly. Currently there's a temporary hack to workaround this, which > > limits the block size. Now that uaudio is in and works, it's time to > > start removing such hacks. > > > > Similarly, driver code needs to know the minimum number of frames per > > transfer to get an interrupt (ie the completion callback). Indeed, if > > the transfer frame count is not rounded to it, we silently miss the > > interrupt, the completion call-back is not called and playback > > stutters. > > > > The diff below adds a new usbd_bus_info() function which reports the > > maximum frames that can be scheduled and the minimum frames per > > transfer to get an interrupt. > > > > [snip] > > Index: usbdi.c > > =================================================================== > > RCS file: /cvs/src/sys/dev/usb/usbdi.c,v > > retrieving revision 1.100 > > diff -u -p -u -p -r1.100 usbdi.c > > --- usbdi.c 18 Nov 2018 16:33:26 -0000 1.100 > > +++ usbdi.c 12 Jun 2019 13:33:41 -0000 > > @@ -275,6 +275,12 @@ usbd_close_pipe(struct usbd_pipe *pipe) > > return (USBD_NORMAL_COMPLETION); > > } > > > > +void > > +usbd_bus_info(struct usbd_device *dev, struct usbd_bus_info *info) > > +{ > > + dev->bus->methods->info(dev, info); > > +} > > + > > usbd_status > > usbd_transfer(struct usbd_xfer *xfer) > > { > > [snip] > > i think above is not good enough with only ehci, ohci, uhci and xhci > being taken care of in [snip]'ed parts of the diff unfortunately:] > just thinking about dwc2. >
Thanks. I grep'ed for usbd_bus_methods, and dwc2 seems to be the only one I missed. Below is a new diff with dwc2 bits and few fixes. I've no arm/octeon machines to test, anyone has one for a quick test? Interestingly, dwc2 supports at most DWC2_MAXISOCPACKETS frames, which is defined to 16 only. Without this diff, the uaudio driver would attempt to schedule ~48 frames which would trigger a KASSERT() in dwc2_device_start() immediately. BTW, 16 is too small for usb2.0 devices to work at all. I'd recommend 512, which is two blocks of 20ms each. Index: ehci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ehci.c,v retrieving revision 1.204 diff -u -p -r1.204 ehci.c --- ehci.c 31 Mar 2019 06:16:38 -0000 1.204 +++ ehci.c 13 Jun 2019 07:25:31 -0000 @@ -113,6 +113,7 @@ struct ehci_pipe { u_int8_t ehci_reverse_bits(u_int8_t, int); usbd_status ehci_open(struct usbd_pipe *); +void ehci_info(struct usbd_device *, struct usbd_bus_info *); int ehci_setaddr(struct usbd_device *, int); void ehci_poll(struct usbd_bus *); void ehci_softintr(void *); @@ -225,6 +226,7 @@ struct usbd_bus_methods ehci_bus_methods .do_poll = ehci_poll, .allocx = ehci_allocx, .freex = ehci_freex, + .info = ehci_info, }; struct usbd_pipe_methods ehci_root_ctrl_methods = { @@ -491,6 +493,21 @@ ehci_init(struct ehci_softc *sc) sc->sc_flsize * sizeof(struct ehci_soft_itd *)); usb_freemem(&sc->sc_bus, &sc->sc_fldma); return (err); +} + +void +ehci_info(struct usbd_device *dev, struct usbd_bus_info *info) +{ + /* + * XXX: even if most hosts have 1024 frame lists, only 256 + * frame transfers work reliably. As soon as 256 is exceeded, + * we start getting zeroed frames if multiple device are + * running simultaneously. Set this to sc->sc_fl_size, once + * ehci is fixed. Interrups occur every 1m, despite the + * EHCI_CMD_ITC_2 setting. + */ + info->nframes_max = 256; + info->intr_thres = (dev->speed >= USB_SPEED_HIGH) ? 8 : 1; } int Index: ohci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ohci.c,v retrieving revision 1.156 diff -u -p -r1.156 ohci.c --- ohci.c 11 Mar 2019 17:50:08 -0000 1.156 +++ ohci.c 13 Jun 2019 07:25:32 -0000 @@ -87,6 +87,7 @@ usbd_status ohci_alloc_std_chain(struct struct ohci_soft_td **); usbd_status ohci_open(struct usbd_pipe *); +void ohci_info(struct usbd_device *, struct usbd_bus_info *); int ohci_setaddr(struct usbd_device *, int); void ohci_poll(struct usbd_bus *); void ohci_softintr(void *); @@ -236,6 +237,7 @@ struct usbd_bus_methods ohci_bus_methods .do_poll = ohci_poll, .allocx = ohci_allocx, .freex = ohci_freex, + .info = ohci_info, }; struct usbd_pipe_methods ohci_root_ctrl_methods = { @@ -931,6 +933,13 @@ ohci_init(struct ohci_softc *sc) bad1: usb_freemem(&sc->sc_bus, &sc->sc_hccadma); return (err); +} + +void +ohci_info(struct usbd_device *dev, struct usbd_bus_info *info) +{ + info->nframes_max = OHCI_SITD_CHUNK; + info->intr_thres = 1; } struct usbd_xfer * Index: uaudio.c =================================================================== RCS file: /cvs/src/sys/dev/usb/uaudio.c,v retrieving revision 1.144 diff -u -p -r1.144 uaudio.c --- uaudio.c 9 May 2019 07:09:04 -0000 1.144 +++ uaudio.c 13 Jun 2019 07:25:33 -0000 @@ -382,6 +382,7 @@ struct uaudio_softc { unsigned int ufps; /* USB frames per second */ unsigned int sync_pktsz; /* size of sync packet */ unsigned int host_nframes; /* max frames we can schedule */ + unsigned int host_intr_thres; /* min frames to get an interrupt */ int diff_nsamp; /* samples play is ahead of rec */ int diff_nframes; /* frames play is ahead of rec */ @@ -2820,12 +2821,15 @@ uaudio_stream_open(struct uaudio_softc * s->data_pipe = NULL; s->sync_pipe = NULL; - s->nframes_mask = 0; - i = a->fps; - while (i > 1000) { - s->nframes_mask = (s->nframes_mask << 1) | 1; - i >>= 1; - } + /* + * Find the smallest transfer size (power of two), larger than + * the poll interval and the interrupt threshold. + */ + i = 0; + while (a->fps < (sc->ufps >> i) || sc->host_intr_thres > (1 << i)) + i++; + + s->nframes_mask = (1 << i) - 1; /* bytes per audio frame */ bpa = a->bps * a->nch; @@ -2900,12 +2904,10 @@ uaudio_stream_open(struct uaudio_softc * } /* - * Require at least 2ms block size to ensure no - * transfer exceeds two blocks. - * - * XXX: use s->nframes_mask instead of 1000 + * Require at least twice the smallest transfer size allowed + * as block size to ensure no transfer exceeds two blocks. */ - if (1000 * blksz < 2 * sc->rate * bpa) { + if (sc->ufps / (s->nframes_mask + 1) * blksz < 2 * sc->rate * bpa) { printf("%s: audio block too small\n", DEVNAME(sc)); return EIO; } @@ -3694,6 +3696,7 @@ uaudio_attach(struct device *parent, str struct uaudio_softc *sc = (struct uaudio_softc *)self; struct usb_attach_arg *arg = aux; struct usb_config_descriptor *cdesc; + struct usbd_bus_info info; struct uaudio_blob desc; /* @@ -3720,31 +3723,29 @@ uaudio_attach(struct device *parent, str sc->trigger_mode = 0; sc->copy_todo = 0; - /* - * Ideally the USB host controller should expose the number of - * frames we're allowed to schedule, but there's no such - * interface. The uhci(4) driver can buffer up to 128 frames - * (or it crashes), ehci(4) starts recording null frames if we - * exceed 256 (micro-)frames, ohci(4) works with at most 50 - * frames. - */ switch (sc->udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: sc->ufps = 1000; sc->sync_pktsz = 3; - sc->host_nframes = 50; break; case USB_SPEED_HIGH: case USB_SPEED_SUPER: sc->ufps = 8000; sc->sync_pktsz = 4; - sc->host_nframes = 240; break; default: printf("%s: unsupported bus speed\n", DEVNAME(sc)); return; } + + /* + * get the max number of frames the host supports, needed to + * determine the max block size + */ + usbd_bus_info(sc->udev, &info); + sc->host_nframes = info.nframes_max; + sc->host_intr_thres = info.intr_thres; if (!uaudio_process_conf(sc, &desc)) return; Index: uhci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/uhci.c,v retrieving revision 1.147 diff -u -p -r1.147 uhci.c --- uhci.c 12 Mar 2019 08:13:50 -0000 1.147 +++ uhci.c 13 Jun 2019 07:25:34 -0000 @@ -178,6 +178,7 @@ void uhci_root_intr_close(struct usbd_p void uhci_root_intr_done(struct usbd_xfer *); usbd_status uhci_open(struct usbd_pipe *); +void uhci_info(struct usbd_device *, struct usbd_bus_info *); void uhci_poll(struct usbd_bus *); void uhci_softintr(void *); @@ -253,6 +254,7 @@ struct usbd_bus_methods uhci_bus_methods .do_poll = uhci_poll, .allocx = uhci_allocx, .freex = uhci_freex, + .info = uhci_info, }; struct usbd_pipe_methods uhci_root_ctrl_methods = { @@ -486,6 +488,13 @@ uhci_init(struct uhci_softc *sc) UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* enable interrupts */ return (uhci_run(sc, 1)); /* and here we go... */ +} + +void +uhci_info(struct usbd_device *dev, struct usbd_bus_info *info) +{ + info->nframes_max = UHCI_VFRAMELIST_COUNT; + info->intr_thres = 1; } int Index: usbdi.c =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdi.c,v retrieving revision 1.100 diff -u -p -r1.100 usbdi.c --- usbdi.c 18 Nov 2018 16:33:26 -0000 1.100 +++ usbdi.c 13 Jun 2019 07:25:34 -0000 @@ -275,6 +275,12 @@ usbd_close_pipe(struct usbd_pipe *pipe) return (USBD_NORMAL_COMPLETION); } +void +usbd_bus_info(struct usbd_device *dev, struct usbd_bus_info *info) +{ + dev->bus->methods->info(dev, info); +} + usbd_status usbd_transfer(struct usbd_xfer *xfer) { Index: usbdi.h =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdi.h,v retrieving revision 1.70 diff -u -p -r1.70 usbdi.h --- usbdi.h 1 May 2018 18:14:46 -0000 1.70 +++ usbdi.h 13 Jun 2019 07:25:35 -0000 @@ -86,9 +86,15 @@ typedef void (*usbd_callback)(struct usb #define DEVINFOSIZE 1024 +struct usbd_bus_info { + int nframes_max; /* max frames we can schedule */ + int intr_thres; /* interrupt threshold in (micro-)frames */ +}; + usbd_status usbd_open_pipe(struct usbd_interface *iface, u_int8_t address, u_int8_t flags, struct usbd_pipe **pipe); usbd_status usbd_close_pipe(struct usbd_pipe *pipe); +void usbd_bus_info(struct usbd_device *dev, struct usbd_bus_info *info); usbd_status usbd_transfer(struct usbd_xfer *req); struct usbd_xfer *usbd_alloc_xfer(struct usbd_device *); void usbd_free_xfer(struct usbd_xfer *xfer); Index: usbdivar.h =================================================================== RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.79 diff -u -p -r1.79 usbdivar.h --- usbdivar.h 27 Nov 2018 14:56:09 -0000 1.79 +++ usbdivar.h 13 Jun 2019 07:25:35 -0000 @@ -65,6 +65,7 @@ struct usbd_bus_methods { void (*do_poll)(struct usbd_bus *); struct usbd_xfer * (*allocx)(struct usbd_bus *); void (*freex)(struct usbd_bus *, struct usbd_xfer *); + void (*info)(struct usbd_device *, struct usbd_bus_info *); }; struct usbd_pipe_methods { Index: xhci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/xhci.c,v retrieving revision 1.104 diff -u -p -r1.104 xhci.c --- xhci.c 21 May 2019 09:20:40 -0000 1.104 +++ xhci.c 13 Jun 2019 07:25:35 -0000 @@ -126,6 +126,7 @@ void xhci_timeout_task(void *); /* USBD Bus Interface. */ usbd_status xhci_pipe_open(struct usbd_pipe *); +void xhci_info(struct usbd_device *, struct usbd_bus_info *); int xhci_setaddr(struct usbd_device *, int); void xhci_softintr(void *); void xhci_poll(struct usbd_bus *); @@ -161,6 +162,7 @@ struct usbd_bus_methods xhci_bus_methods .do_poll = xhci_poll, .allocx = xhci_allocx, .freex = xhci_freex, + .info = xhci_info, }; struct usbd_pipe_methods xhci_root_ctrl_methods = { @@ -466,6 +468,18 @@ xhci_config(struct xhci_softc *sc) DPRINTF(("%s: USBCMD=%#x\n", DEVNAME(sc), XOREAD4(sc, XHCI_USBCMD))); DPRINTF(("%s: IMAN=%#x\n", DEVNAME(sc), XRREAD4(sc, XHCI_IMAN(0)))); +} + +void +xhci_info(struct usbd_device *dev, struct usbd_bus_info *info) +{ + info->nframes_max = XHCI_MAX_XFER; + + /* + * We get only one interrupt per frame, ie 1 interrupt every 8 + * microframes + */ + info->intr_thres = (dev->speed >= USB_SPEED_HIGH) ? 8 : 1; } int Index: dwc2/dwc2.c =================================================================== RCS file: /cvs/src/sys/dev/usb/dwc2/dwc2.c,v retrieving revision 1.48 diff -u -p -r1.48 dwc2.c --- dwc2/dwc2.c 14 Mar 2019 04:28:10 -0000 1.48 +++ dwc2/dwc2.c 13 Jun 2019 07:25:36 -0000 @@ -89,6 +89,7 @@ STATIC usbd_status dwc2_open(struct usbd STATIC int dwc2_setaddr(struct usbd_device *, int); STATIC void dwc2_poll(struct usbd_bus *); STATIC void dwc2_softintr(void *); +STATIC void dwc2_info(struct usbd_device *, struct usbd_bus_info *); #if 0 STATIC usbd_status dwc2_allocm(struct usbd_bus *, struct usb_dma *, uint32_t); @@ -179,6 +180,7 @@ STATIC struct usbd_bus_methods dwc2_bus_ #if 0 .get_lock = dwc2_get_lock, #endif + .info = dwc2_info, }; STATIC struct usbd_pipe_methods dwc2_root_ctrl_methods = { @@ -610,6 +612,13 @@ dwc2_device_clear_toggle(struct usbd_pip { DPRINTF("toggle %d -> 0", pipe->endpoint->savedtoggle); +} + +STATIC void +dwc2_info(struct usbd_device *dev, struct usbd_bus_info *info) +{ + info->nframes_max = DWC2_MAXISOCPACKETS; + info->intr_thres = (dev->speed >= USB_SPEED_HIGH) ? 8 : 1; } /***********************************************************************/