libusb
|
The libusb_reset_device() function allows you to reset a device. If your program has to call such a function, it should obviously be aware that the reset will cause device state to change (e.g. register values may be reset).
The problem is that any other program could reset the device your program is working with, at any time. libusb does not offer a mechanism to inform you when this has happened, so if someone else resets your device it will not be clear to your own program why the device state has changed.
Ultimately, this is a limitation of writing drivers in userspace. Separation from the USB stack in the underlying kernel makes it difficult for the operating system to deliver such notifications to your program. The Linux kernel USB stack allows such reset notifications to be delivered to in-kernel USB drivers, but it is not clear how such notifications could be delivered to second-class drivers that live in userspace.
The functionality listed below is only available through synchronous, blocking functions. There are no asynchronous/non-blocking alternatives, and no clear ways of implementing these.
When libusb presents a device handle to an application, there is a chance that the corresponding device may be in unconfigured state. For devices with multiple configurations, there is also a chance that the configuration currently selected is not the one that the application wants to use.
The obvious solution is to add a call to libusb_set_configuration() early on during your device initialization routines, but there are caveats to be aware of:
One solution to some of the above problems is to consider the currently active configuration. If the configuration we want is already active, then we don't have to select any configuration:
This is probably suitable for most scenarios, but is inherently racy: another application or driver may change the selected configuration after the libusb_get_configuration() call.
Even in cases where libusb_set_configuration() succeeds, consider that other applications or drivers may change configuration after your application calls libusb_set_configuration().
One possible way to lock your device into a specific configuration is as follows:
The above method works because once an interface is claimed, no application or driver is able to select another configuration.
NOTE: This section is currently Linux-centric. I am not sure if any of these considerations apply to Darwin or other platforms.
When a transfer completes early (i.e. when less data is received/sent in any one packet than the transfer buffer allows for) then libusb is designed to terminate the transfer immediately, not transferring or receiving any more data unless other transfers have been queued by the user.
On legacy platforms, libusb is unable to do this in all situations. After the incomplete packet occurs, "surplus" data may be transferred. Prior to libusb v1.0.2, this information was lost (and for device-to-host transfers, the corresponding data was discarded). As of libusb v1.0.3, this information is kept (the data length of the transfer is updated) and, for device-to-host transfers, any surplus data was added to the buffer. Still, this is not a nice solution because it loses the information about the end of the short packet, and the user probably wanted that surplus data to arrive in the next logical transfer.
A previous workaround was to only ever submit transfers of size 16kb or less.
As of libusb v1.0.4 and Linux v2.6.32, this is fixed. A technical explanation of this issue follows.
When you ask libusb to submit a bulk transfer larger than 16kb in size, libusb breaks it up into a number of smaller subtransfers. This is because the usbfs kernel interface only accepts transfers of up to 16kb in size. The subtransfers are submitted all at once so that the kernel can queue them at the hardware level, therefore maximizing bus throughput.
On legacy platforms, this caused problems when transfers completed early. Upon this event, the kernel would terminate all further packets in that subtransfer (but not any following ones). libusb would note this event and immediately cancel any following subtransfers that had been queued, but often libusb was not fast enough, and the following subtransfers had started before libusb got around to cancelling them.
Thanks to an API extension to usbfs, this is fixed with recent kernel and libusb releases. The solution was to allow libusb to communicate to the kernel where boundaries occur between logical libusb-level transfers. When a short transfer (or other error) occurs, the kernel will cancel all the subtransfers until the boundary without allowing those transfers to start.