On Tue, Feb 25, 2025 at 12:07:20PM +0100, Manuel Bouyer wrote:
> Hello,
> I'm trying to pass a USB device to a NetBSD guest (to ease driver
> developement). I'm using:
> qemu-system-x86_64 -audiodev oss,id=oss  -m 2G -accel nvmm -drive 
> if=virtio,file=/home/bouyer/nvmm/tst-can.vnd,format=raw -netdev user,id=n1 
> -device virtio-net-pci,netdev=n1 -usb -device 
> usb-host,bus=usb-bus.0,port=1,vendorid=0x1d50,productid=0x606f -chardev 
> stdio,id=char0 -serial chardev:char0
> 
> If the guest is linux, the USB device is detected (and the appropriate
> driver loaded), so the qemu part is working (at last good enough for linux),
> but with a NetBSD guest this ends up with:
> [    75.292264] uhub0: autoconfiguration error: device problem, disabling 
> port 1
> 
> Any idea before I start investigating uhci ?

I made progress on this; the attached patch allows the NetBSD guest to
see the passed device; although with a 1mn timeout.
This patch is inspired from linux, which, from what I understood, will
first do what we do (fetch descriptor before setting the address), and
if it fail retries with setting the address first.

The port reset causes the full descriptor fetch afterward, so the patch
avoids it if we had to first set the address.

Maybe it could help with some USB hubs or devices too ...

-- 
Manuel Bouyer <bou...@antioche.eu.org>
     NetBSD: 26 ans d'experience feront toujours la difference
--
Index: usb_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v
retrieving revision 1.277
diff -u -r1.277 usb_subr.c
--- usb_subr.c  6 Apr 2022 22:01:45 -0000       1.277
+++ usb_subr.c  26 Feb 2025 22:10:18 -0000
@@ -1383,12 +1383,18 @@
        int addr;
        int i;
        int p;
+       int addr_valid;
 
        KASSERT(usb_in_event_thread(parent));
 
-       if (bus->ub_methods->ubm_newdev != NULL)
-               return (bus->ub_methods->ubm_newdev)(parent, bus, depth, speed,
+       if (bus->ub_methods->ubm_newdev != NULL) {
+               err = (bus->ub_methods->ubm_newdev)(parent, bus, depth, speed,
                    port, up);
+               if (err) {
+                       DPRINTFN(1, "bus newdev %p  failed, error %d", 
bus->ub_methods->ubm_newdev, err, 0, 0);
+               }
+               return err;
+       }
 
        addr = usbd_getnewaddr(bus);
        if (addr < 0) {
@@ -1459,11 +1465,14 @@
        err = usbd_setup_pipe_flags(dev, 0, &dev->ud_ep0, USBD_DEFAULT_INTERVAL,
            &dev->ud_pipe0, USBD_MPSAFE);
        if (err) {
+               DPRINTFN(1, "usbd_setup_pipe_flags failed, error %d", err, 0, 
0, 0);
                usbd_remove_device(dev, up);
                return err;
        }
 
        dd = &dev->ud_ddesc;
+       addr_valid = 0;
+getdesc_again:
        /* Try a few times in case the device is slow (i.e. outside specs.) */
        for (i = 0; i < 10; i++) {
                /* Get the first 8 bytes of the device descriptor. */
@@ -1482,12 +1491,29 @@
        if (err) {
                DPRINTF("addr=%jd, getting first desc failed: %jd", addr, err,
                    0, 0);
-               usbd_remove_device(dev, up);
-               return err;
+
+               if (addr_valid) {
+                       usbd_remove_device(dev, up);
+                       return err;
+               }
+               addr_valid++;
+               /* Set the address */
+               DPRINTFN(5, "setting device address=%jd", addr, 0, 0, 0);
+               err = usbd_set_address(dev, addr);
+               if (err) {
+                       DPRINTF("set address %jd failed, err = %jd", addr, err, 
0, 0);
+                       err = USBD_SET_ADDR_FAILED;
+                       usbd_remove_device(dev, up);
+                       return err;
+               }
+               /* Allow device time to set new address */
+               usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE);
+               dev->ud_addr = addr;    /* new device address now */
+               goto getdesc_again;
        }
 
        /* Windows resets the port here, do likewise */
-       if (up->up_parent)
+       if (up->up_parent && addr_valid == 0)
                usbd_reset_port(up->up_parent, port, &ps);
 
        if (speed == USB_SPEED_HIGH) {

Reply via email to