Hi,

I've recently come across a uaudio device which doesn't work in OpenBSD:

uaudio2 at uhub0 port 1 configuration 1 interface 3 "E+ Corp. DAC Audio" rev 
1.10/0.01 addr 2
uaudio2: class v1, full-speed, async, channels: 2 play, 0 rec, 3 ctls
audio3 at uaudio2

When opening the audio device, the kernel reports `uaudio0: can't get iface
handle` in the dmesg buffer.

I posted information about the device and sthen@ and kettenis@ spotted
the problem: the device exposes non-consecutive interface numbers, and
is thus not compliant with the USB standard.

It has interfaces 0, 1 and 3, in that order. The audio stream is on
interface 3, which the kernel expects to find at index 3 in the ifaces
array, but actually it's at index 2!

I've pasted a load of info about the device (lsusb and debug kernel
output) here:
https://gist.github.com/vext01/958756d0fd515cc321f99a3ee1e3351a

The following diff makes this device work. It searches the ifaces array
in the event that the correct entry is not found at the expect index.
This should be a "no functional change" for all existing compliant
devices.

I've tested this for the past two days and all seems well, but I'd like
some USB stack hackers to verify it.

Cheers


Index: usbdi.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.106
diff -u -p -r1.106 usbdi.c
--- usbdi.c     3 Apr 2020 20:11:47 -0000       1.106
+++ usbdi.c     24 Jan 2021 20:44:28 -0000
@@ -638,12 +638,23 @@ usbd_status
 usbd_device2interface_handle(struct usbd_device *dev, u_int8_t ifaceno,
     struct usbd_interface **iface)
 {
+       u_int8_t idx;
+
        if (dev->cdesc == NULL)
                return (USBD_NOT_CONFIGURED);
-       if (ifaceno >= dev->cdesc->bNumInterface)
-               return (USBD_INVAL);
-       *iface = &dev->ifaces[ifaceno];
-       return (USBD_NORMAL_COMPLETION);
+       if (ifaceno < dev->cdesc->bNumInterface &&
+           dev->ifaces[ifaceno].idesc->bInterfaceNumber == ifaceno) {
+               *iface = &dev->ifaces[ifaceno];
+               return (USBD_NORMAL_COMPLETION);
+       }
+       /* With some non-compliant devices, the interface may be at the wrong 
index */
+       for (idx = 0; idx < dev->cdesc->bNumInterface; idx++) {
+               if (dev->ifaces[idx].idesc->bInterfaceNumber == ifaceno) {
+                       *iface = &dev->ifaces[idx];
+                       return (USBD_NORMAL_COMPLETION);
+               }
+       }
+       return (USBD_INVAL);
 }
 
 /* XXXX use altno */


-- 
Best Regards
Edd Barrett

http://www.theunixzoo.co.uk

Reply via email to