google / gousb

gousb provides low-level interface for accessing USB devices
Apache License 2.0
838 stars 124 forks source link

Setting an alternate setting of 0 when claiming interface locks up USB device. #36

Closed samuel closed 6 years ago

samuel commented 6 years ago

This is likely an issue with the USB device I'm using (YARD Stick One), but when claiming an interface (calling *Config.Interface) and providing 0 for alt, gousb is calling libusb_set_interface_alt_setting which locks up the device. Because 0 is the default there's no need to explicitly set the interface alt setting, and as such the lockup can be avoided by not making that call. The change to only call lib_usb_set_interface_alt_setting when alt != 0 seems harmless and would allow this to work, but I'm not all that familiar with libusb so I may be missing why it's not possible to avoid the call.

diff --git a/config.go b/config.go
index 70e2330..5eb0f76 100644
--- a/config.go
+++ b/config.go
@@ -124,9 +124,11 @@ func (c *Config) Interface(num, alt int) (*Interface, error) {
        return nil, fmt.Errorf("failed to claim interface %d on %s: %v", num, c, err)
    }

-   if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
-       c.dev.ctx.libusb.release(c.dev.handle, uint8(num))
-       return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err)
+   if alt != 0 {
+       if err := c.dev.ctx.libusb.setAlt(c.dev.handle, uint8(num), uint8(alt)); err != nil {
+           c.dev.ctx.libusb.release(c.dev.handle, uint8(num))
+           return nil, fmt.Errorf("failed to set alternate config %d on interface %d of %s: %v", alt, num, c, err)
+       }
    }

    c.claimed[num] = true
zagrodzki commented 6 years ago

I think it won't be that simple, unfortunately: the alternate settings are semi-persistent, and the device on open might be configured with a different alternate setting. libusb resets the interface to alt setting 0 on "release", but that doesn't mean the device was previously accessed by libusb, and as far as I can tell, that behavior is not required by the spec, so other implementations might not be doing the same.

One thing that we definitely could do according to the spec (9.2.3) is selecting an interface only if the interface has reported multiple alternate settings - we know that from the interface descriptor. I'll prepare a PR.

zagrodzki commented 6 years ago

an alternative would be to issue a "get interface" USB command to check the current alternate setting and change setting only if needed. However, according to the spec, "get interface" must be supported if device has multiple alternate settings, so perhaps that would not be that useful in your case either. Additionally, "get interface" does not have a convenient wrapper in libusb, so the code would be somewhat more complicated.

zagrodzki commented 6 years ago

Can you try the "set_alt_interface" branch to see if that solves the problem for you?

zagrodzki commented 6 years ago

The changes have been merged, I hope this addresses your problem. Please reopen if it doesn't.