expose host capabilities relative to usb isochronous xfers

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

expose host capabilities relative to usb isochronous xfers

Alexandre Ratchov-2
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.

Index: ehci.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ehci.c,v
retrieving revision 1.204
diff -u -p -u -p -r1.204 ehci.c
--- ehci.c 31 Mar 2019 06:16:38 -0000 1.204
+++ ehci.c 12 Jun 2019 13:33:38 -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 = {
@@ -493,6 +498,21 @@ ehci_init(struct ehci_softc *sc)
  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
 ehci_intr(void *v)
 {
Index: ohci.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ohci.c,v
retrieving revision 1.156
diff -u -p -u -p -r1.156 ohci.c
--- ohci.c 11 Mar 2019 17:50:08 -0000 1.156
+++ ohci.c 12 Jun 2019 13:33:40 -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 -u -p -r1.144 uaudio.c
--- uaudio.c 9 May 2019 07:09:04 -0000 1.144
+++ uaudio.c 12 Jun 2019 13:33:41 -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 / (sc->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 -u -p -r1.147 uhci.c
--- uhci.c 12 Mar 2019 08:13:50 -0000 1.147
+++ uhci.c 12 Jun 2019 13:33:41 -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 -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)
 {
Index: usbdi.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdi.h,v
retrieving revision 1.70
diff -u -p -u -p -r1.70 usbdi.h
--- usbdi.h 1 May 2018 18:14:46 -0000 1.70
+++ usbdi.h 12 Jun 2019 13:33:42 -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 -u -p -r1.79 usbdivar.h
--- usbdivar.h 27 Nov 2018 14:56:09 -0000 1.79
+++ usbdivar.h 12 Jun 2019 13:33:42 -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 -u -p -r1.104 xhci.c
--- xhci.c 21 May 2019 09:20:40 -0000 1.104
+++ xhci.c 12 Jun 2019 13:33:42 -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 = {
@@ -468,6 +470,18 @@ xhci_config(struct xhci_softc *sc)
  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
 xhci_detach(struct device *self, int flags)
 {

Reply | Threaded
Open this post in threaded view
|

Re: expose host capabilities relative to usb isochronous xfers

Artturi Alm
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.

-Artturi

Reply | Threaded
Open this post in threaded view
|

Re: expose host capabilities relative to usb isochronous xfers

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