Re: qemu USB pass-through/NetBSD guest

Manuel Bouyer <[email protected]>
Newsgroups gmane.os.netbsd.devel.kernel
Message-ID <[email protected]>
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 <[email protected]>
     NetBSD: 26 ans d'experience feront toujours la difference
--
diff (text/plain, 2.2 KB)
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) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.