summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-06-16 22:50:50 -0500
committerDaniel Drake <dsd@gentoo.org>2008-06-16 22:52:55 -0500
commit947ba8056456a5215724fb502e3e09d50016f699 (patch)
tree0ee6933ea11db95c520834d6b74116562470ff89
parente7a7a49d0331ee0e14145f6e7ec39763b36314ac (diff)
downloadlibusb-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.c114
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