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;
}
/***********************************************************************/