diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-06-16 22:50:50 -0500 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-06-16 22:52:55 -0500 |
commit | 947ba8056456a5215724fb502e3e09d50016f699 (patch) | |
tree | 0ee6933ea11db95c520834d6b74116562470ff89 | |
parent | e7a7a49d0331ee0e14145f6e7ec39763b36314ac (diff) | |
download | libusb-947ba8056456a5215724fb502e3e09d50016f699.tar.gz |
Refine configuration selection again
At Alan Stern's suggestion, just offer the bare "set configuration" and
"get configuration" functionality, and let applications worry about the
specific race conditions and unusual situations.
-rw-r--r-- | libusb/core.c | 114 |
1 files changed, 72 insertions, 42 deletions
diff --git a/libusb/core.c b/libusb/core.c index 8c5c8db..20aed50 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -178,6 +178,59 @@ pthread_mutex_t usbi_open_devs_lock = PTHREAD_MUTEX_INITIALIZER; * LIBUSB_TRANSFER_NO_DEVICE status code. * - Many functions such as libusb_set_configuration() return the special * LIBUSB_ERROR_NO_DEVICE error code when the device has been disconnected. + * + * \section configsel Configuration selection and handling + * + * 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: + * -# If the device is already in the desired configuration, calling + * libusb_set_configuration() using the same configuration value will cause + * a lightweight device reset. This may not be desirable behaviour. + * -# libusb will be unable to change configuration if the device is in + * another configuration and other programs or drivers have claimed + * interfaces under that configuration. + * -# In the case where the desired configuration is already active, libusb + * may not even be able to perform a lightweight device reset. For example, + * take my USB keyboard with fingerprint reader: I'm interested in driving + * the fingerprint reader interface through libusb, but the kernel's + * USB-HID driver will almost always have claimed the keyboard interface. + * Because the kernel has claimed an interface, it is not even possible to + * perform the lightweight device reset, so libusb_set_configuration() will + * fail. (Luckily the device in question only has a single configuration.) + * + * 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: +\code +cfg = libusb_get_configuration(dev); +if (cfg != desired) + libusb_set_configuration(dev, desired); +\endcode + * + * This is probably suitable for most scenarios, but is inherently racy: + * another application or driver may change the selected configuration + * <em>after</em> 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: + * -# Set the desired configuration (or use the logic above to realise that + * it is already in the desired configuration) + * -# Claim the interface that you wish to use + * -# Check that the currently active configuration is the one that you want + * to use. + * + * The above method works because once an interface is claimed, no application + * or driver is able to select another configuration. */ /** @@ -820,34 +873,28 @@ API_EXPORTED int libusb_get_configuration(libusb_device_handle *dev, } /** \ingroup dev - * Set the active configuration for a device. The operating system may have - * already set an active configuration on the device, but for portability - * reasons you should use this function to select the configuration you want - * before claiming any interfaces. - * - * If you wish to change to another configuration at some later time, you - * must release all claimed interfaces using libusb_release_interface() before - * setting a new active configuration. Also, consider that other applications - * or drivers may have claimed interfaces, in which case you are unable to - * change the configuration. + * Set the active configuration for a device. + * + * The operating system may or may not have already set an active + * configuration on the device. It is up to your application to ensure the + * correct configuration is selected before you attempt to claim interfaces + * and perform other operations. + * + * If you call this function on a device already configured with the selected + * configuration, then this function will act as a lightweight device reset: + * it will issue a SET_CONFIGURATION request using the current configuration, + * causing most USB-related device state to be reset (altsetting reset to zero, + * endpoint halts cleared, toggles reset). + * + * You cannot change/reset configuration if your application has claimed + * interfaces - you should free them with libusb_release_interface() first. + * You cannot change/reset configuration if other applications or drivers have + * claimed interfaces. * * A configuration value of -1 will put the device in unconfigured state. * The USB specifications state that a configuration value of 0 does this, * however buggy devices exist which actually have a configuration 0. * - * This function checks the current active configuration before setting the - * new one. If the requested configuration is already active, this function - * does nothing more. - * - * This function is inherently racy: there is a small chance that someone may - * change the configuration after libusb has determined the active - * configuration but before the new one has been applied (or not applied, if - * libusb thinks the specified configuration is already active). After changing - * configuration, you may choose to claim an interface and then call - * libusb_get_configuration() to ensure that the requested change actually took - * place. The fact that you have now claimed an interface means that nobody - * else can change the configuration. - * * You should always use this function rather than formulating your own * SET_CONFIGURATION control request. This is because the underlying operating * system needs to know when such changes happen. @@ -857,8 +904,7 @@ API_EXPORTED int libusb_get_configuration(libusb_device_handle *dev, * \param dev a device handle * \param configuration the bConfigurationValue of the configuration you * wish to activate, or -1 if you wish to put the device in unconfigured state - * \returns 1 if the configuration was changed - * \returns 0 if the configuration was already active + * \returns 0 on success * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected @@ -867,24 +913,8 @@ API_EXPORTED int libusb_get_configuration(libusb_device_handle *dev, API_EXPORTED int libusb_set_configuration(libusb_device_handle *dev, int configuration) { - int r; - int active; - usbi_dbg("configuration %d", configuration); - r = libusb_get_configuration(dev, &active); - if (r < 0) - return r; - if (active == 0) - active = -1; - if (active == configuration) { - usbi_dbg("already active"); - return 0; - } - - r = usbi_backend->set_configuration(dev, configuration); - if (r == 0) - r = 1; - return r; + return usbi_backend->set_configuration(dev, configuration); } /** \ingroup dev |