Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libusb_set_interface_alt_setting returns kIOUSBPipeStalled on macOS #838

Closed
macfreek opened this issue Jan 5, 2021 · 46 comments
Closed
Labels
Milestone

Comments

@macfreek
Copy link
Contributor

macfreek commented Jan 5, 2021

Hi, thanks for making libusb available!

I'm using dfu-util to upgrade the firmware of a development board over USB. For some reasons, that fails on macOS, with the first reports from March 2020. It was suggested that a macOS security update triggered it, but I have not seen anything more clear than that. (Upstream tickets: dfu-util and pycom.)

Tracing down this behaviour, the root cause seems that a call to SetAlternateInterface() in darwin_set_interface_altsetting() (the libusb_set_interface_alt_setting() for macOS) returns a kIOUSBPipeStalled ("pipe is stalled") error. This is repeatable behaviour.

My question: is this known or expected behaviour? And how should either libusb or dfu-util handle this?

My apologies for not being more specific, I'm an end-user of this library, so am not sure about the intended behaviour.

Note that the documentation for SetAlternateInterface that I could find, did not specifically mention kIOUSBPipeStalled. Only kIOReturnSuccess, kIOReturnNoDevice, and kIOReturnNotOpen.

Given the suggestion that this behaviour changed after a security update, I verified on an older macOS system. I can reproduce it on 10.14.6 (Mojave) and 10.11.6 (El Capitan), so I am not yet convinced that this was a trigger.

@macfreek
Copy link
Contributor Author

macfreek commented Jan 6, 2021

FYI, I can reproduce this with pyusb, so the issue is not specific to dfu-util.

import usb
dev = usb.core.find(idVendor = 0x04D8, idProduct=0xF014)
if dev:
    dev.set_configuration(1)
    dev.set_interface_altsetting(0, 0)
else:
    print("Can't find USB device")

raises usb.core.USBError: [Errno 32] Pipe error.

(I tried adding a time.sleep(0.5) just before the call to set_interface_altsetting() but got the same result).

@mcuee mcuee added the macOS label Jan 6, 2021
@mcuee
Copy link
Member

mcuee commented Jan 6, 2021

Please help to post the usb descriptor of the device and the debug log.

lsusb under macOS: (just install using homebrew: "brew install lsusb", if you use homebrew).
https://github.com/jlhonora/lsusb (edit to add a note: this not not very good, please use the "lsusb" utility bundled in the dfu-util binaries for Darwin. It is build for x86_64 but it should work for Apple Silicon Mac as well with Rosetta 2).

Looks like the device FW have a problem to handle libusb_set_interface_alt_setting (libusb_device_handle *dev_handle, int interface_number, int alternate_setting). A simple fix will be changing the code to libusb_claim_interface (libusb_device_handle *dev_handle, int interface_number), if the device has no alternative settings other than the default 0.

Ref:
http://libusb.sourceforge.net/api-1.0/group__libusb__dev.html

@macfreek
Copy link
Contributor Author

macfreek commented Jan 6, 2021

Hi @mcuee, thanks for the quick response. I did some more extensive testing, to see how different devices behave when I try to execute the Python script in comment #2. I list the controller if I was able to find that info for these products.

Devices that respond with a access denied error (most likely because the USB interface is already claimed by a driver in the macOS kernel)

Vendor ID Product ID Device type
0x2341 0x0043 Arduino Uno, FTDI controller
0x04d8 0xf012 Pycom Pysense expansion board in normal mode
0x10c4 0xea60 Doodle Bot, CP2102 controller
0x10c4 0xea60 NodeMCU module, CP2102 controller

Devices that work fine

Vendor ID Product ID Device type
0x1A86 0x7523 Robotdyn Arduino Mega, CH340G controller connected to ATmega2560
0x0403 0x6015 Pycom expansion board version 1, FTDI FT230X controller

Devices that give a "Pipe is stalled" error (errno 32)

Vendor ID Product ID Device type
0x04D8 0xF011 Pycom Pysense in DFU mode
0x04D8 0xF014 Pycom Pytrack in DFU mode
0x067b 0x2303 USB-to-serial cable #1, with Prolific PL2303 controller
0x067b 0x2303 USB-to-serial cable #2, with Prolific controller

The issue I have is with the Pytrack and Pysense while in DFU mode as described on the above URL. The relevant line in the lsusb output is:

Bus 029 Device 005: ID 04d8:f014 Microchip Technology Inc. Application Specific Device

Note that I observed this behaviour with the Prolific USB-to-serial cable only connected on the USB side; not console is attached on the DB9 side.

Suspicion

[Edit] My first suspicion is to suspect an issue with the Prolific PL2303 controller. However, that may be jumping to conclusions. The observation is that these two extension boards (whose controller is unclear to me), while in DFU mode, behave the same as a Prolific USB-to-serial cable when the later is not connected to a device.

Why it is behaving as such is unclear.

It is also unclear when exactly it is behaving like this. I can reproduce this result with both boards on macOS 10.14.6 and 10.11.6. On the other hand, in the reports from dfu-util, people found that it did work on earlier versions on macOS and/or earlier versions of libusb, and that it also works on Windows and Linux.

So my first question would be if someone can reproduce the above results.

@mcuee
Copy link
Member

mcuee commented Jan 6, 2021

Good discussion here:
https://sourceforge.net/p/dfu-util/tickets/86/

So you can try the workaround, just claim the interface and not set alternative setting. Apparent the device FW is broken in dealing with this standard USB request.

From Tormod Volden :

If this device has only one alternate setting (please show me the lsusb output), a good question would be why we always set an alternate setting. This has just been like this since the birth of dfu-util. Maybe we could be smarter about this.

@mcuee
Copy link
Member

mcuee commented Jan 7, 2021

A STALL packet generally means that the transfer is not supported by the device. In this case, it is a standard USB request on the Control Endpoint (Control transfer). So this is clearly a device problem. But the work-around should be simple, just do not issue the request if it is not needed.

@tormodvolden
Copy link
Contributor

The request seems to be supported, as it works fine on Linux and Windows, and on macOS with older libusb. It could be something special about Freek's device, but Gijs' findings lead me to believe there is something with the libusb darwin backend.

@mcuee
Copy link
Member

mcuee commented Jan 9, 2021

Good point. The device does seem to support the request. On the other hand, since it is specific to come devices, it is possible that device does not support the request twice in a row.

Previously I have devices with Microchip PIC USB FW which does not correctly set the toggle bit and it will work one time with libusb and next time it will fail. The work-around is to issue libusb_reset_device.

@mcuee
Copy link
Member

mcuee commented Jan 9, 2021

One potential change in the Darwin backend is related to Issue #455. The change #456 is included in 1.0.23. Related change is #523. All in all, it seems to me the device does not like the changes somehow.

@tormodvolden
Copy link
Contributor

I pointed to exactly these two changes in the dfu-util ticket and asked them to check if they were the cause, but there hasn't been anyone following up.

Is the request sent twice in a row? Is it implicitly sent by another function?

Issuing libusb_reset_device can be delicate in the scope of DFU since it can have its own significance for the device, like going to DFU mode or back to RunTime mode.

@mcuee
Copy link
Member

mcuee commented Jan 9, 2021

Let's copy the report from dfu-util Ticket 86 for easier reference.
https://sourceforge.net/p/dfu-util/tickets/86/

This output is with dfu-util 0.9 and libusb 1.0.20 (suppsed to be true for 1.0.21/1.0.22 as well).

Gijs:Expansion firmware V2.0 x pycom$ dfu-util -D exp_board_32_0561b03.dfu -v -v -v
dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

DFU suffix version 100
Match vendor ID from file: 04d8
Match product ID from file: ebfe
[timestamp] [threadID] facility level [function call] <message>
--------------------------------------------------------------------------------
[ 0.004690] [00000307] libusb: debug [libusb_get_device_list] 
[ 0.005161] [00000307] libusb: debug [libusb_get_device_descriptor] 
[ 0.005175] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.005241] [00000307] libusb: debug [libusb_open] open 20.16
[ 0.005465] [00000307] libusb: debug [darwin_open] device open for access
[ 0.005475] [00000307] libusb: debug [libusb_close] 
[ 0.005850] [00000307] libusb: debug [libusb_get_device_descriptor] 
[ 0.005859] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
Opening DFU capable USB device...
[ 0.005871] [00000307] libusb: debug [libusb_open] open 20.16
[ 0.005951] [00000307] libusb: debug [darwin_open] device open for access
ID 04d8:ebfe
Run-time device DFU version 0100
Claiming USB DFU Runtime Interface...
[ 0.005970] [00000307] libusb: debug [libusb_claim_interface] interface 0
[ 0.006044] [00000307] libusb: info [darwin_claim_interface] no interface found; setting configuration: 1
[ 0.008832] [00000307] libusb: debug [get_endpoints] building table of endpoints.
[ 0.008877] [00000307] libusb: debug [darwin_claim_interface] interface opened
[ 0.008888] [00000307] libusb: debug [libusb_set_interface_alt_setting] interface 0 altsetting 0
[ 0.009208] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: checking if device descriptor changed
[ 0.009259] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: checking if configuration descriptor 0 changed
[ 0.009279] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: device reset complete
[ 0.009284] [00000307] libusb: debug [get_endpoints] building table of endpoints.
Determining device status: [ 0.009300] [00000307] libusb: debug [libusb_alloc_transfer] transfer 0x7f9b2d504298
[ 0.009305] [00000307] libusb: debug [libusb_submit_transfer] transfer 0x7f9b2d504298
[ 0.009341] [00000307] libusb: debug [libusb_get_next_timeout] no URB with timeout or all handled by OS; no timeout!
[ 0.009363] [00000307] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.009378] [00000307] libusb: debug [handle_events] poll fds modified, reallocating
[ 0.009395] [00000307] libusb: debug [handle_events] poll() 1 fds with timeout in 60000ms
[ 0.009611] [00002907] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.009768] [00000307] libusb: debug [handle_events] poll() returned 1
[ 0.009789] [00000307] libusb: debug [handle_events] caught a fish on the event pipe
[ 0.009795] [00000307] libusb: debug [darwin_handle_transfer_completion] handling control completion with kernel status -536854449
[ 0.009798] [00000307] libusb: debug [darwin_transfer_status] transfer error: pipe is stalled
[ 0.009801] [00000307] libusb: debug [usbi_handle_transfer_completion] transfer 0x7f9b2d504298 has callback 0x10b709130
[ 0.009804] [00000307] libusb: debug [sync_transfer_cb] actual_length=0
[ 0.009809] [00000307] libusb: debug [libusb_free_transfer] transfer 0x7f9b2d504298
Device does not implement get_status, assuming appIDLE
Device really in Runtime Mode, send DFU detach request...
[ 0.009820] [00000307] libusb: debug [libusb_alloc_transfer] transfer 0x7f9b2d605ca8
[ 0.009823] [00000307] libusb: debug [libusb_submit_transfer] transfer 0x7f9b2d605ca8
[ 0.009866] [00000307] libusb: debug [libusb_get_next_timeout] no URB with timeout or all handled by OS; no timeout!
[ 0.009894] [00000307] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.009901] [00000307] libusb: debug [handle_events] poll() 1 fds with timeout in 60000ms
[ 0.010020] [00002907] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.010088] [00000307] libusb: debug [handle_events] poll() returned 1
[ 0.010095] [00000307] libusb: debug [handle_events] caught a fish on the event pipe
[ 0.010098] [00000307] libusb: debug [darwin_handle_transfer_completion] handling control completion with kernel status 0
[ 0.010102] [00000307] libusb: debug [usbi_handle_transfer_completion] transfer 0x7f9b2d605ca8 has callback 0x10b709130
[ 0.010105] [00000307] libusb: debug [sync_transfer_cb] actual_length=0
[ 0.010110] [00000307] libusb: debug [libusb_free_transfer] transfer 0x7f9b2d605ca8
Resetting USB...
[ 0.010119] [00000307] libusb: debug [libusb_reset_device] 
[ 0.010138] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: checking if device descriptor changed
[ 0.010160] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: checking if configuration descriptor 0 changed
[ 0.010177] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: device reset complete
[ 0.010222] [00000307] libusb: debug [libusb_release_interface] interface 0
[ 0.010454] [00000307] libusb: debug [libusb_close] 

@mcuee
Copy link
Member

mcuee commented Jan 9, 2021

Failed log with 1.0.24.

Gijs:Expansion firmware V2.0 x pycom$ dfu-util -D pysense_custom.dfu -v -v -v -v
dfu-util 0.10

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2020 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

libusb version 1.0.24 (11584)
DFU suffix version 100
Match vendor ID from file: 04d8
Match product ID from file: f011
[timestamp] [threadID] facility level [function call] <message>
--------------------------------------------------------------------------------
[ 0.020373] [000f89ed] libusb: debug [libusb_get_device_list]  
[ 0.020425] [000f89ed] libusb: debug [libusb_get_device_descriptor]  
[ 0.020431] [000f89ed] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.020452] [000f89ed] libusb: debug [libusb_open] open 20.8
[ 0.020517] [000f89ed] libusb: debug [darwin_open] device open for access
[ 0.020536] [000f89ed] libusb: debug [libusb_close]  
[ 0.020616] [000f89ed] libusb: debug [libusb_get_device_descriptor]  
[ 0.020622] [000f89ed] libusb: debug [libusb_get_config_descriptor] index 0
Opening DFU capable USB device...
[ 0.020656] [000f89ed] libusb: debug [libusb_open] open 20.8
[ 0.020750] [000f89ed] libusb: debug [darwin_open] device open for access
ID 04d8:f011
Run-time device DFU version 0100
Claiming USB DFU Runtime Interface...
[ 0.020768] [000f89ed] libusb: debug [libusb_claim_interface] interface 0
[ 0.020793] [000f89ed] libusb: info [darwin_claim_interface] no interface found; setting configuration: 1
[ 0.024276] [000f89ed] libusb: debug [get_endpoints] building table of endpoints.
[ 0.024332] [000f89ed] libusb: debug [darwin_claim_interface] interface opened
[ 0.024343] [000f89ed] libusb: debug [libusb_set_interface_alt_setting] interface 0 altsetting 0
[ 0.095443] [000f89ed] libusb: debug [darwin_reset_device] darwin/reset_device: waiting for re-enumeration to complete...
[ 0.097235] [000f89f1] libusb: debug [darwin_devices_detached] detected device detached due to re-enumeration
[ 0.099403] [000f89f1] libusb: debug [darwin_get_cached_device] finding cached device for sessionID 0x703562e76980
[ 0.099622] [000f89f1] libusb: debug [darwin_get_cached_device] matching sessionID/locationID 0x703562e76980/0x14100000 against cached device with sessionID/locationID 0x70354112ffc9/0x14100000
[ 0.099640] [000f89f1] libusb: debug [darwin_get_cached_device] found cached device with matching location that is being re-enumerated
[ 0.099649] [000f89f1] libusb: debug [darwin_get_cached_device] caching new device with sessionID 0x703562e76980
[ 0.105437] [000f89f1] libusb: debug [darwin_cache_device_descriptor] cached device descriptor:
[ 0.105463] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bDescriptorType:    0x01
[ 0.105470] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bcdUSB:             0x0100
[ 0.105474] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bDeviceClass:       0xfe
[ 0.105478] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bDeviceSubClass:    0x01
[ 0.105482] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bDeviceProtocol:    0x00
[ 0.105487] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bMaxPacketSize0:    0x40
[ 0.105491] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   idVendor:           0x04d8
[ 0.105495] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   idProduct:          0xf011
[ 0.105499] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bcdDevice:          0x0005
[ 0.105502] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   iManufacturer:      0x00
[ 0.105544] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   iProduct:           0x00
[ 0.105553] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   iSerialNumber:      0x00
[ 0.105558] [000f89f1] libusb: debug [darwin_cache_device_descriptor]   bNumConfigurations: 0x01
[ 0.105592] [000f89f1] libusb: debug [darwin_check_configuration] active config: 0, first config: 1
[ 0.105599] [000f89f1] libusb: debug [process_new_device] re-using existing device from context 0x7fbe345056a0 for with session 0x70354112ffc9 new session 0x703562e76980
[ 0.105606] [000f89f1] libusb: debug [process_new_device] found device with address 8 port = 1 parent = 0x0 at 0x7fbe345088aa
[ 0.105612] [000f89f1] libusb: debug [darwin_devices_attached] cached device in reset state. reset complete...
[ 0.105631] [000f89ed] libusb: debug [darwin_reset_device] darwin/reset_device: checking whether descriptors changed
[ 0.105638] [000f89ed] libusb: debug [darwin_reset_device] darwin/reset_device: device reset complete. restoring state...
[ 0.105833] [000f89ed] libusb: debug [darwin_open] device open for access
[ 0.106072] [000f89ed] libusb: debug [darwin_restore_state] darwin/restore_state: restoring configuration 1...
[ 0.106872] [000f89ed] libusb: debug [darwin_restore_state] darwin/restore_state: reclaiming interfaces
[ 0.106893] [000f89ed] libusb: debug [darwin_restore_state] darwin/restore_state: re-claiming interface 0
[ 0.111003] [000f89ed] libusb: debug [get_endpoints] building table of endpoints.
[ 0.111057] [000f89ed] libusb: debug [darwin_claim_interface] interface opened
[ 0.111064] [000f89ed] libusb: debug [darwin_restore_state] darwin/restore_state: device state restored
[ 0.111068] [000f89ed] libusb: debug [get_endpoints] building table of endpoints.
dfu-util: Cannot set alt interface zero: LIBUSB_ERROR_OTHER

@mcuee
Copy link
Member

mcuee commented Jan 9, 2021

Original report.

 dfu-util -v -v -v -v -d 1209:70b1 -D draw/draw.bin
dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
[timestamp] [threadID] facility level [function call] <message>
--------------------------------------------------------------------------------
[ 0.010689] [00000307] libusb: debug [libusb_get_device_list]
[ 0.010742] [00000307] libusb: debug [discovered_devs_append] need to increase capacity
[ 0.010773] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.010776] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.010791] [00000307] libusb: debug [libusb_open] open 20.8
[ 0.010875] [00000307] libusb: debug [darwin_open] device open for access
[ 0.010887] [00000307] libusb: debug [libusb_alloc_transfer] transfer 0x7fb4d2801d48
[ 0.010889] [00000307] libusb: debug [libusb_submit_transfer] transfer 0x7fb4d2801d48
[ 0.010928] [00000307] libusb: debug [libusb_get_next_timeout] no URB with timeout or all handled by OS; no timeout!
[ 0.010939] [00000307] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.010945] [00000307] libusb: debug [handle_events] poll fds modified, reallocating
[ 0.010954] [00000307] libusb: debug [handle_events] poll() 1 fds with timeout in 60000ms
[ 0.011188] [0000120b] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.011221] [00000307] libusb: debug [handle_events] poll() returned 1
[ 0.011226] [00000307] libusb: debug [handle_events] caught a fish on the event pipe
[ 0.011228] [00000307] libusb: debug [darwin_handle_transfer_completion] handling control completion with kernel status 0
[ 0.011231] [00000307] libusb: debug [usbi_handle_transfer_completion] transfer 0x7fb4d2801d48 has callback 0x10e275f7c
[ 0.011233] [00000307] libusb: debug [sync_transfer_cb] actual_length=4
[ 0.011260] [00000307] libusb: debug [libusb_free_transfer] transfer 0x7fb4d2801d48
[ 0.011288] [00000307] libusb: debug [libusb_alloc_transfer] transfer 0x7fb4d16022e8
[ 0.011289] [00000307] libusb: debug [libusb_submit_transfer] transfer 0x7fb4d16022e8
[ 0.011307] [00000307] libusb: debug [libusb_get_next_timeout] no URB with timeout or all handled by OS; no timeout!
[ 0.011309] [00000307] libusb: debug [libusb_handle_events_timeout_completed] doing our own event handling
[ 0.011311] [00000307] libusb: debug [handle_events] poll() 1 fds with timeout in 60000ms
[ 0.011310] [0000120b] libusb: debug [darwin_async_io_callback] an async io operation has completed
[ 0.011320] [00000307] libusb: debug [handle_events] poll() returned 1
[ 0.011323] [00000307] libusb: debug [handle_events] caught a fish on the event pipe
[ 0.011324] [00000307] libusb: debug [darwin_handle_transfer_completion] handling control completion with kernel status 0
[ 0.011325] [00000307] libusb: debug [usbi_handle_transfer_completion] transfer 0x7fb4d16022e8 has callback 0x10e275f7c
[ 0.011327] [00000307] libusb: debug [sync_transfer_cb] actual_length=60
[ 0.011329] [00000307] libusb: debug [libusb_free_transfer] transfer 0x7fb4d16022e8
[ 0.011332] [00000307] libusb: debug [libusb_close]
[ 0.011361] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011363] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011365] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011367] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011368] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011370] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011371] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011373] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011374] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011376] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011377] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011378] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011380] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011381] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011414] [00000307] libusb: debug [parse_configuration] skipping descriptor 0xb
[ 0.011416] [00000307] libusb: debug [parse_endpoint] skipping descriptor 25
[ 0.011419] [00000307] libusb: debug [parse_endpoint] skipping descriptor b
[ 0.011421] [00000307] libusb: debug [parse_endpoint] skipping descriptor 25
[ 0.011424] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011425] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011428] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011429] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011431] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011432] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
[ 0.011434] [00000307] libusb: debug [parse_endpoint] skipping descriptor 30
[ 0.011436] [00000307] libusb: debug [libusb_get_device_descriptor]
[ 0.011437] [00000307] libusb: debug [libusb_get_config_descriptor] index 0
Opening DFU capable USB device...
[ 0.011452] [00000307] libusb: debug [libusb_open] open 20.8
[ 0.011493] [00000307] libusb: debug [darwin_open] device open for access
ID 1209:70b1
Run-time device DFU version 0101
Claiming USB DFU Interface...
[ 0.011521] [00000307] libusb: debug [libusb_claim_interface] interface 0
[ 0.011825] [00000307] libusb: debug [get_endpoints] building table of endpoints.
[ 0.011880] [00000307] libusb: debug [darwin_claim_interface] interface opened
Setting Alternate Setting #0 ...
[ 0.011886] [00000307] libusb: debug [libusb_set_interface_alt_setting] interface 0 altsetting 0
[ 0.115862] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: waiting for re-enumeration to complete...
[ 0.118823] [0000120b] libusb: debug [darwin_devices_detached] detected device detatched due to re-enumeration
[ 0.121736] [0000120b] libusb: debug [darwin_get_cached_device] finding cached device for sessionID 0x278aa68bfa320
[ 0.121919] [0000120b] libusb: debug [darwin_get_cached_device] parent sessionID: 0x2788eba05a96b
[ 0.121934] [0000120b] libusb: debug [darwin_get_cached_device] matching sessionID/locationID 0x278aa68bfa320/0x14430000 against cached device with sessionID/locationID 0x278a575ef8078/0x14430000
[ 0.121960] [0000120b] libusb: debug [darwin_get_cached_device] found cached device with matching location that is being re-enumerated
[ 0.121974] [0000120b] libusb: debug [darwin_get_cached_device] caching new device with sessionID 0x278aa68bfa320
[ 1.536885] [0000120b] libusb: debug [darwin_cache_device_descriptor] cached device descriptor:
[ 1.536900] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bDescriptorType:    0x01
[ 1.536902] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bcdUSB:             0x0210
[ 1.536904] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bDeviceClass:       0x00
[ 1.536906] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bDeviceSubClass:    0x00
[ 1.536907] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bDeviceProtocol:    0x00
[ 1.536909] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bMaxPacketSize0:    0x40
[ 1.536910] [0000120b] libusb: debug [darwin_cache_device_descriptor]   idVendor:           0x1209
[ 1.536912] [0000120b] libusb: debug [darwin_cache_device_descriptor]   idProduct:          0x70b1
[ 1.536914] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bcdDevice:          0x0101
[ 1.536915] [0000120b] libusb: debug [darwin_cache_device_descriptor]   iManufacturer:      0x01
[ 1.536946] [0000120b] libusb: debug [darwin_cache_device_descriptor]   iProduct:           0x02
[ 1.536956] [0000120b] libusb: debug [darwin_cache_device_descriptor]   iSerialNumber:      0x00
[ 1.536959] [0000120b] libusb: debug [darwin_cache_device_descriptor]   bNumConfigurations: 0x01
[ 1.537981] [0000120b] libusb: debug [darwin_check_configuration] active config: 1, first config: 1
[ 1.537992] [0000120b] libusb: debug [process_new_device] re-using existing device from context 0x7fb4d1405740 for with session 0x278a575ef8078 new session 0x278aa68bfa320
[ 1.537998] [0000120b] libusb: debug [process_new_device] found device with address 8 port = 3 parent = 0x7fb4d2800460 at 0x7fb4d170168a
[ 1.538002] [0000120b] libusb: debug [darwin_devices_attached] cached device in reset state. reset complete...
[ 1.538024] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: checking whether descriptors changed
[ 1.538031] [00000307] libusb: debug [darwin_reset_device] darwin/reset_device: device reset complete. restoring state...
[ 1.538349] [00000307] libusb: debug [darwin_open] device open for access
[ 1.538359] [00000307] libusb: debug [darwin_restore_state] darwin/restore_state: reclaiming interfaces
[ 1.538363] [00000307] libusb: debug [darwin_restore_state] darwin/restore_state: re-claiming interface 0
[ 1.538848] [00000307] libusb: debug [get_endpoints] building table of endpoints.
[ 1.538898] [00000307] libusb: debug [darwin_claim_interface] interface opened
[ 1.538903] [00000307] libusb: debug [darwin_restore_state] darwin/restore_state: device state restored
[ 1.538907] [00000307] libusb: debug [get_endpoints] building table of endpoints.
dfu-util: Cannot set alternate interface

@tormodvolden
Copy link
Contributor

The possible device reset (darwin_reset_device) inside libusb_set_interface_alt_setting is a particuliarity of the darwin backend. Why is it needed on this platform? I wish this was explained in the source. Apparently it seems to work out on libusb 1.0.20, but I guess it is the repercussions of this reset that causes something to fail in 1.0.23. This failure ought to be fixed, but at the same time it would be better if a series of libusb operations would create as much as possible the same USB requests on all platforms, and not add something as significant as USB resets on one platform.

@mcuee
Copy link
Member

mcuee commented Jan 9, 2021

Unfortunately libusb_reset_device bahavior is really platform dependant.

Old reference in June 2011:
https://marc.info/?t=130832974400003&r=1&w=2

It is still true now that WinUSB backend does not support reset. I believe Linux behaviour is still the same as before.
https://github.com/libusb/libusb/wiki/Windows#Known_Restrictions

BTW, I have no issues running the pyusb test code for a few devices under the new Mac Mini M1. So this is device specific.

@hjelmn --> please help to look into this. Thanks.

@macfreek
Copy link
Contributor Author

macfreek commented Jan 10, 2021

I did some more tests with pyusb, basically:

dev.set_configuration(1)
usb.util.claim_interface(dev, 0)
dev.set_interface_altsetting(0, 0)

For now, three confirmations:

  • The problem occurs in version 1.0.23, 1.0.24 and git master. It does not occur with version 1.0.19, 1.0.20, 1.0.21 or 1.0.22.
  • The problem both occurs with a (disconnected) Prolific PL2303 USB-to-serial cable and a Pycom PyTrack.
  • Only dev.set_interface_altsetting(0, 0) raised a pipe error in the above version. usb.util.claim_interface(dev, 0) never gave this error (only an Access denied for USB devices that where already in use, such as when I set the dev to my keyboard).

This is tested on macOS 10.14.6 (I wanted to test on macOS 10.11 too, but have not yet found the time).

For those who like to reproduce:

Install libusb:

  1. git clone git@github.com:libusb/libusb.git
  2. cd libusb
  3. git checkout v1.0.23
  4. ./autogen.sh
  5. make
  6. sudo make prefix=/usr/local/libusb-1.0.23 install
  7. make distclean

Repeat steps 3-7 for each version of libusb you like to test.

Run the following test script

#!/usr/bin/env python3
import usb

# BACKEND_PATH = '/usr/local/libusb-1.0.22/lib/libusb-1.0.0.dylib'
BACKEND_PATH = '/usr/local/libusb-1.0.23/lib/libusb-1.0.0.dylib'

from usb.backend import libusb1
backend = libusb1.get_backend(find_library=lambda x: BACKEND_PATH)
assert backend, "Can't find backend at "+BACKEND_PATH

# Find a USB-to-serial cable, with Prolific PL2303 controller
dev = usb.core.find(idVendor = 0x067b, idProduct=0x2303, backend=backend)
assert dev, "Can't find USB device with Prolific PL2303"

print('------------------------------------------------------------')
print('{} {}'.format(dev.manufacturer, dev.product))
print('idVendor = 0x{:04x}, idProduct=0x{:04x}'.format(dev.idVendor, dev.idProduct))
print('Backend: {}'.format(dev.backend.lib))
print('------------------------------------------------------------')
# print(dev)
# print('------------------------------------------------------------')
try:
    print('dev.set_configuration(1) ...')
    dev.set_configuration(1)
    print('usb.util.claim_interface(dev, 0) ...')
    usb.util.claim_interface(dev, 0)
    print('dev.set_interface_altsetting(0, 0) ...')
    dev.set_interface_altsetting(0, 0)
except Exception as err:
    print("--> Exception: {}".format(err))
else:
    print("--> Success")
print('Releasing interface 0...')
usb.util.release_interface(dev, 0)
print("done")

For libusb 1.0.22, this produces:

------------------------------------------------------------
Prolific Technology Inc. USB-Serial Controller
idVendor = 0x067b, idProduct=0x2303
Backend: <CDLL '/usr/local/libusb-1.0.22/lib/libusb-1.0.0.dylib', handle 7fb5fec1a130 at 0x101e4be80>
Kernel driver active (interface 0): False
------------------------------------------------------------
dev.set_configuration(1) ...
usb.util.claim_interface(dev, 0) ...
dev.set_interface_altsetting(0, 0) ...
--> Success
Releasing interface 0...
done

For libusb 1.0.23, this produces:

------------------------------------------------------------
Prolific Technology Inc. USB-Serial Controller
idVendor = 0x067b, idProduct=0x2303
Backend: <CDLL '/usr/local/libusb-1.0.23/lib/libusb-1.0.0.dylib', handle 7ff8eb41bcd0 at 0x1022c6e50>
Kernel driver active (interface 0): False
------------------------------------------------------------
dev.set_configuration(1) ...
usb.util.claim_interface(dev, 0) ...
dev.set_interface_altsetting(0, 0) ...
--> Exception: [Errno 32] Pipe error
Releasing interface 0...
done

@macfreek
Copy link
Contributor Author

macfreek commented Jan 10, 2021

Trying to narrow it down:

  • Commit 9a23fe1 from 15 December 2018 at 22:12:07 CET seems to work fine
  • Commit 065e586 from 15 December 2018 at 22:26:14 CET seems to fail

So I suspect that commit 065e586 contains the root cause.

One key change in this code is the following in darwin_set_interface_altsetting() (omitting all non-relevant lines)

Before commit:

  kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
  kresult = get_endpoints (dev_handle, iface);
  return darwin_to_libusb (kresult);

After commit:

  kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
  ret = get_endpoints (dev_handle, iface);
  return darwin_to_libusb (kresult);

So the return value was changed from get_endpoints() to SetAlternateInterface()

Now, I don't claim to understand the code enough to grasp what darwin_set_interface_altsetting() should return (and if return ret; is the answer to Life, Universe and Everything -or this bug for that matter-, but at least this seems the root cause of the change in behaviour.

@macfreek
Copy link
Contributor Author

Just letting you know that I was able to do a firmware upgrade with dfu-util, using the fix in PR #843.

@hjelmn
Copy link
Member

hjelmn commented Jan 11, 2021

The reset is required macOS per the IOKit documentation. The PR is the appropriate fix.

@macfreek
Copy link
Contributor Author

@hjelmn, @mcuee

If you feel confident that this is indeed the correct behaviour, I certainly would appreciate it if the PR would be merged, and perhaps not take too long before releasing a new patch version (1.0.25).

To give an indication of the number of affected users, these are the reports for dfu-util with pycom devices: pycom-micropython-sigfox#422, Pycom forum thread#5182, thread#5452, thread#5739, thread#6008, thread#6715, thread#6722. Surely the problem is more widespread than just pycom devices (and perhaps dfu-util). For example, the OP of dfu-util#86 mentioned a different device, a Tomu.

@tormodvolden
Copy link
Contributor

Nathan, do you have an URL to this documentation, if it is public?

Freek, thanks a lot for bisecting and finding the regression.

So to understand this correctly, there is actually a PIPE error from SetAlternateInterface() and device reset, but now the endpoints are found so libusb_set_interface_alt_setting() will return success even if the requested alternate interface was not set?

@mcuee
Copy link
Member

mcuee commented Jan 12, 2021

@tormodvolden Looks like that is true. The device apprarently does not support SetAlternateInterface() properly and the device gets reset.

The following code has not been changed from 1.0.22 to 1.0.23. But it is ineed strange that macOS IOkit will require device reset to recover from a pipe stall error.

  kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
  if (kresult != kIOReturnSuccess)
    darwin_reset_device (dev_handle);

On the other hand, this does have a side effect. Interestingly prior to 1.0.23, the reset actually does not reset as per Issue #455. So the side effect of the code does not really manifest.

Anyway, probably dfu-util can just remove the call as it is really not necessary. DFU is anyway mainly deal with control endpoint.

@mcuee
Copy link
Member

mcuee commented Jan 12, 2021

@hjelmn
On the libusb side, not so sure if the following can be done as a potential improvement.

Assuming the reset is indeed as per IOkit documentation (sounds strange), then there is a side effect. So we can probably check the current alternative setting and do nothing (most of the cases) if it is already at the correct alternative setting. Majority the device should only have the defaul alt setting 0. And for those which needs to be set a different alt settings, then the device should anyway supports the call propely.

This is not necessary in Linux and Windows WinUSB backend, as there is no side effect of reset.

@mcuee
Copy link
Member

mcuee commented Jan 12, 2021

The other thing is probably to add a debug message here to warn the user that Set Alternative Interface call has failed and device will reset.

 kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
  if (kresult != kIOReturnSuccess)
    darwin_reset_device (dev_handle);

@tormodvolden
Copy link
Contributor

tormodvolden commented Jan 12, 2021

@mcuee, the device seems to support libusb_set_interface_alt_setting() since the function returns success on Linux and Windows. But for what I know it can be the backends are hiding a failure there also. And someone with the device can probably confirm that there is no reset on Linux or Windows - the libusb platform code there has no reset in the alternate setting function but I don't know if the kernel would issue a reset as part of the alternate setting ioctl.

@tormodvolden
Copy link
Contributor

I already worked on a patch to avoid unnecessary alternate settings in the dfu-util, but that wouldn't help towards solving the underlying libusb issue.

IMO the libusb library should not do any check if it is necessary or not but pass on the request from the program. If the device doesn't support it, the error should be propagated back to the program. Normally I would think the program should then perform the reset if it wants to, but if is this is platform specific as it seems for macOS (I haven't found it documented yet) then I understand the need for "magic" in the platform code, to save the program from dealing with this.

@macfreek
Copy link
Contributor Author

macfreek commented Jan 13, 2021

All: thanks for taking this up. It's very much appreciated.
I followed up on the suggestion of @mcuee to log a warning if SetAlternateInterface fails.

However, the real crux is if the device should be reset by libusb upon failure, or if that is the prerogative of the calling software.

  • On one hand this is what was done before, and I'm no fan of changing the API.
  • On the other hand, that might now have worked (due to libusb_reset_device not working on macOS #455).
  • And perhaps more important, this seems to deviate from the behaviour on other platforms. Ideally, the libusb_set_interface_alt_setting() should result in the same USB signals on the wire, regardless of the operating system. That doesn't seem the case right now.

I'm leaning a bit towards the "remove darwin_reset_device in darwin_set_interface_altsetting" fence, but such change may have side-effects I'm unaware of, so I'm happy to look expectedly to the maintainer for some Wise Words on this matter.

As to the question if IOKit documentation demands such a reset, I can't find it, but I only searched the web for methods with the same name, not looked into the actual included library. The documentation I found is sparse or even non-existent (The question why Apple publishes non-existent documentation is another topic ;) ). There is another, seemingly related, SetAlternateInterface method, that takes a UInt16 instead of a UInt8, with a little documentation and the actual implementation.

@hjelmn
Copy link
Member

hjelmn commented Jan 13, 2021

@macfreek Yeah. The documentation is poor and I can't remember where I read that it would need to be reset in this case. The code is working with the proposed PR which should get things working again. I will try to determine if is safe to remove the reset at another time.

@hjelmn
Copy link
Member

hjelmn commented Jan 13, 2021

Yup. Really bad move on Apple's part. Especially given 10.9's usb stack had a number of regressions. Had to guess where they screwed up.

@mcuee
Copy link
Member

mcuee commented Jan 13, 2021

As for whether we need to have 1.0.25 release to address the anomalies for 1.0.24, I think it is a good idea since this macOS issue seems to affect quite some users.

Linux side -- this may affect some users and the fix has been merged.
#831

Windows side -- not so sure if #844 has a quick fix or not. I will check with Chris.

@mcuee mcuee added this to the 1.0.25 milestone Jan 13, 2021
@macfreek
Copy link
Contributor Author

@macfreek Yeah. The documentation is poor and I can't remember where I read that it would need to be reset in this case.

Perhaps you read this line in IOUSBFamily USB.h:

#define kIOUSBPipeStalled           iokit_usb_err(79)  // 0xe000404f  Pipe has stalled, error needs to be cleared

macfreek added a commit to macfreek/libusb that referenced this issue Jan 13, 2021
darwin_set_interface_altsetting no longer returns a pipe stalled error [kIOUSBPipeStalled/LIBUSB_ERROR_PIPE] if the interface is succesfully reset.

Closes: libusb#838

Signed-off-by: Freek Dijkstra
macfreek added a commit to macfreek/libusb that referenced this issue Jan 13, 2021
Revert unintended change of commit 065e586. darwin_set_interface_altsetting no longer returns a pipe stalled error [kIOUSBPipeStalled/LIBUSB_ERROR_PIPE] if the interface is succesfully reset.

Closes: libusb#838

Signed-off-by: Freek Dijkstra
@tormodvolden
Copy link
Contributor

tormodvolden commented Jan 13, 2021

Pipe has stalled, error needs to be cleared

If this is the reason behind the device_reset(), it is maybe enough to call darwin_clear_halt().

I don't have a Pycom board to test on, but I tried on a PL2303 USB-serial adapter (which was reported by macfreek to behave the same in this regard) on Linux after unbinding the kernel driver from the USB interface. Indeed there is a PIPE error, and CLEAR_FEATURE requests to each of the three endpoints in the interface are sent by the kernel, and there is no error from libusb.

If I try to set an alternate interface different than zero (not available on this interface) using pyusb, there will be an error but it seems pyusb is doing some checks and will not send an invalid SET_INTERFACE USB request.

@mcuee
Copy link
Member

mcuee commented Jan 13, 2021

I agree that a clear halt will be the normal solution to deal with pipe stall. However, in this case, we are not so sure why last time IOkit documentation was saying that reset is needed, so we have to keep the current reset code (magic code) as of now. We probably have to deal with potential improvement in another time.

@tormodvolden
Copy link
Contributor

tormodvolden commented Jan 13, 2021

Yes, so having a reference to this IOkit documentation would be good (I tried long searching for it on the free internet), with a comment in the code.

Further, the code should probably special-case exclusive access error and return appropriate error instead of resetting the device in that case. And actually only do device reset (or clear halt) if there is a PIPE error.

Anyway +1 on getting macfreek's regression fix in first.

@hjelmn
Copy link
Member

hjelmn commented Jan 13, 2021

@macfreek Yeah. The documentation is poor and I can't remember where I read that it would need to be reset in this case.

Perhaps you read this line in IOUSBFamily USB.h:

#define kIOUSBPipeStalled           iokit_usb_err(79)  // 0xe000404f  Pipe has stalled, error needs to be cleared

Yeah. That line is familiar. For some reason ClearPipeStallBothEnds was insufficient. At the time DeviceReset was likely what we used for reset and it was (and still is) essentially a no-op. We now use re-enumerate for reset. It is worth trying with ClearPipeStallBothEnds on each pipe and see if that works now. I can't check if the darwin kernel is already doing it without looking at the IOUSBFamily source code (which is not available for any recent version).

@hjelmn
Copy link
Member

hjelmn commented Jan 13, 2021

Wow, that line is 12 years old. I will have to dig into an old Mac OS X version (10.5 or 10.6) for Xcode to find the documentation I was reading. Probably was still using a PowerBook G4 so probably 10.5.

This is likely the relevant version:

https://opensource.apple.com/source/IOUSBFamily/IOUSBFamily-349.4.3/

@dickens
Copy link
Member

dickens commented Jan 13, 2021

Just to add some OS-agnostic info, clearing a pipe stall is only applicable to bulk/interrupt endpoints. A pipe stall in the case of a failed set interface request would be returned by the control pipe, which never needs to be cleared. As an observer and non-Mac user, it makes no sense to me why any action would need to be taken if a set interface request failed.

@tormodvolden
Copy link
Contributor

A successful SET_INTERFACE request will also clear all endpoints of the interface. So the Linux kernel will do that if the request fails with PIPE error and would otherwise have been a NOP (only one alternate interface). If there are multiple alternate interfaces the PIPE error will be returned. I assume the darwin backend tried to do something similar, but instead of going through the endpoints it was easier to call device_reset() which basically did the same (at some point in time). And the logic for single vs multiple alternate interfaces was not implemented.

@dickens
Copy link
Member

dickens commented Jan 13, 2021

I assume the darwin backend tried to do something similar, but instead of going through the endpoints it was easier to call device_reset() which basically did the same (at some point in time). And the logic for single vs multiple alternate interfaces was not implemented.

This affects the entire device, which a set interface call should not do. This is unfortunate, even if it is the status quo.

@hjelmn
Copy link
Member

hjelmn commented Jan 13, 2021

@dickens I agree. I wish I could remember what the reasoning was but 12 years is an eternity in software. The reset was not present in libusb 0.1.12 so I specifically added it when implementing the libusb 1.0 backend. It was undoubtedly to work around some issue seen during development. If there was some sort of bug in that version of IOUSBFamily it is likely long gone (since at least 10.9.0 given the IOUSBFamily rewrite).

@hjelmn
Copy link
Member

hjelmn commented Jan 13, 2021

For reference for when the reset appeared:

b49f6bf

macfreek added a commit to macfreek/libusb that referenced this issue Jan 13, 2021
darwin_set_interface_altsetting() no longer returns the status of the underlying SetAlternateInterface(), but instead the result of a subsequent GetNumEndpoints() call. This reverts to the behaviour prior to commit 065e586.

Since darwin_set_interface_altsetting() resets the interface after SetAlternateInterface(), one of the consequences is that if SetAlternateInterface() returns a kIOUSBPipeStalled error, and the interface is successfully reset, darwin_set_interface_altsetting() now returns LIBUSB_SUCCESS instead of LIBUSB_ERROR_PIPE.

Closes: libusb#838

Signed-off-by: Freek Dijkstra
@hjelmn hjelmn closed this as completed in bf943bc Jan 13, 2021
@mcuee
Copy link
Member

mcuee commented Jan 14, 2021

Wow, that is really the earlier days of libusb-1.0 with Daniel Drake as admin.

I just created a new ticket #847 to capture potential improvement in the future.

@hjelmn
Copy link
Member

hjelmn commented Jan 14, 2021

@mcuee Yup. Been doing this for awhile. Remember discussing the direction to take for libusb 1.0 with Johannes before he handed it off to Daniel. This project is ~ 20 years old now!

@mcuee
Copy link
Member

mcuee commented Jan 14, 2021

Indeed this project is about 21 years old with the first release version of libusb 0.1.0 on 2000-02-16.
https://sourceforge.net/projects/libusb/files/libusb-0.1%20%28LEGACY%29/

I only started with libusb, libusb-win32 and pyusb in Oct 2005 because of the involvement in pickit-devel. Now interestingly I am one of the admins of all three projects even though I am not a developer. Time really flies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants