On Thu, Oct 02, 2014 at 12:20:14PM +0200, Martin Pieuchot wrote:
> Our USB stack contains a hack needed for ehci(4) and ohci(4) that
> breaks xhci(4). The diff below moves this hack in these drivers,
> and makes it possible to have a working xhci(4) in GENERIC.
>
> I'd like this diff to be tested on as much machines as possible, because
> the code path it touches is very sensible. This also matters if you are
> using uhci(4)!
>
> Please test and report back.
Tested umass on x61s (amd64 MP) and everything seems to be working just
fine.
I also read the diff and I'm okay with it going in.
>
> Thanks,
> Martin
>
> Index: ehci.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/ehci.c,v
> retrieving revision 1.168
> diff -u -p -r1.168 ehci.c
> --- ehci.c 1 Sep 2014 08:13:02 -0000 1.168
> +++ ehci.c 2 Oct 2014 09:30:28 -0000
> @@ -99,6 +99,7 @@ struct ehci_pipe {
> u_int8_t ehci_reverse_bits(u_int8_t, int);
>
> usbd_status ehci_open(struct usbd_pipe *);
> +int ehci_setaddr(struct usbd_device *, int);
> void ehci_poll(struct usbd_bus *);
> void ehci_softintr(void *);
> int ehci_intr1(struct ehci_softc *);
> @@ -215,7 +216,7 @@ void ehci_dump_exfer(struct ehci_xfer *
>
> struct usbd_bus_methods ehci_bus_methods = {
> .open_pipe = ehci_open,
> - .dev_setaddr = usbd_set_address,
> + .dev_setaddr = ehci_setaddr,
> .soft_intr = ehci_softintr,
> .do_poll = ehci_poll,
> .allocx = ehci_allocx,
> @@ -603,6 +604,40 @@ ehci_pcd(struct ehci_softc *sc, struct u
> xfer->status = USBD_NORMAL_COMPLETION;
>
> usb_transfer_complete(xfer);
> +}
> +
> +/*
> + * Work around the half configured control (default) pipe when setting
> + * the address of a device.
> + *
> + * Because a single QH is setup per endpoint in ehci_open(), and the
> + * control pipe is configured before we could have set the address
> + * of the device or read the wMaxPacketSize of the endpoint, we have
> + * to re-open the pipe twice here.
> + */
> +int
> +ehci_setaddr(struct usbd_device *dev, int addr)
> +{
> + /* Root Hub */
> + if (dev->depth == 0)
> + return (0);
> +
> + /* Re-establish the default pipe with the new max packet size. */
> + ehci_close_pipe(dev->default_pipe);
> + if (ehci_open(dev->default_pipe))
> + return (EINVAL);
> +
> + if (usbd_set_address(dev, addr))
> + return (1);
> +
> + dev->address = addr;
> +
> + /* Re-establish the default pipe with the new address. */
> + ehci_close_pipe(dev->default_pipe);
> + if (ehci_open(dev->default_pipe))
> + return (EINVAL);
> +
> + return (0);
> }
>
> void
> Index: ohci.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/ohci.c,v
> retrieving revision 1.139
> diff -u -p -r1.139 ohci.c
> --- ohci.c 10 Aug 2014 11:18:57 -0000 1.139
> +++ ohci.c 2 Oct 2014 09:33:03 -0000
> @@ -88,6 +88,7 @@ usbd_status ohci_alloc_std_chain(struct
> struct ohci_soft_td **);
>
> usbd_status ohci_open(struct usbd_pipe *);
> +int ohci_setaddr(struct usbd_device *, int);
> void ohci_poll(struct usbd_bus *);
> void ohci_softintr(void *);
> void ohci_waitintr(struct ohci_softc *, struct usbd_xfer *);
> @@ -232,7 +233,7 @@ struct ohci_pipe {
>
> struct usbd_bus_methods ohci_bus_methods = {
> .open_pipe = ohci_open,
> - .dev_setaddr = usbd_set_address,
> + .dev_setaddr = ohci_setaddr,
> .soft_intr = ohci_softintr,
> .do_poll = ohci_poll,
> .allocx = ohci_allocx,
> @@ -2003,6 +2004,40 @@ ohci_open(struct usbd_pipe *pipe)
> bad0:
> return (USBD_NOMEM);
>
> +}
> +
> +/*
> + * Work around the half configured control (default) pipe when setting
> + * the address of a device.
> + *
> + * Because a single ED is setup per endpoint in ohci_open(), and the
> + * control pipe is configured before we could have set the address
> + * of the device or read the wMaxPacketSize of the endpoint, we have
> + * to re-open the pipe twice here.
> + */
> +int
> +ohci_setaddr(struct usbd_device *dev, int addr)
> +{
> + /* Root Hub */
> + if (dev->depth == 0)
> + return (0);
> +
> + /* Re-establish the default pipe with the new max packet size. */
> + ohci_device_ctrl_close(dev->default_pipe);
> + if (ohci_open(dev->default_pipe))
> + return (EINVAL);
> +
> + if (usbd_set_address(dev, addr))
> + return (1);
> +
> + dev->address = addr;
> +
> + /* Re-establish the default pipe with the new address. */
> + ohci_device_ctrl_close(dev->default_pipe);
> + if (ohci_open(dev->default_pipe))
> + return (EINVAL);
> +
> + return (0);
> }
>
> /*
> Index: usb_subr.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/usb_subr.c,v
> retrieving revision 1.109
> diff -u -p -r1.109 usb_subr.c
> --- usb_subr.c 1 Oct 2014 08:29:01 -0000 1.109
> +++ usb_subr.c 2 Oct 2014 09:24:18 -0000
> @@ -901,8 +901,8 @@ usbd_probe_and_attach(struct device *par
> "error=%s\n", parent->dv_xname, port,
> addr, usbd_errstr(err)));
> #else
> - printf("%s: port %d, set config at addr %d failed\n",
> - parent->dv_xname, port, addr);
> + printf("%s: port %d, set config %d at addr %d failed\n",
> + parent->dv_xname, port, confi, addr);
> #endif
>
> goto fail;
> @@ -1160,23 +1160,6 @@ usbd_new_device(struct device *parent, s
>
> USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize);
>
> - /* Re-establish the default pipe with the new max packet size. */
> - usbd_close_pipe(dev->default_pipe);
> - err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
> - &dev->default_pipe);
> - if (err) {
> - usb_free_device(dev);
> - up->device = NULL;
> - return (err);
> - }
> -
> - err = usbd_reload_device_desc(dev);
> - if (err) {
> - usb_free_device(dev);
> - up->device = NULL;
> - return (err);
> - }
> -
> /* Set the address if the HC didn't do it already. */
> if (bus->methods->dev_setaddr != NULL &&
> bus->methods->dev_setaddr(dev, addr)) {
> @@ -1192,10 +1175,7 @@ usbd_new_device(struct device *parent, s
> dev->address = addr;
> bus->devices[addr] = dev;
>
> - /* Re-establish the default pipe with the new address. */
> - usbd_close_pipe(dev->default_pipe);
> - err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
> - &dev->default_pipe);
> + err = usbd_reload_device_desc(dev);
> if (err) {
> usb_free_device(dev);
> up->device = NULL;
>