Refine configuration selection again
authorDaniel Drake <dsd@gentoo.org>
Tue, 17 Jun 2008 03:50:50 +0000 (22:50 -0500)
committerDaniel Drake <dsd@gentoo.org>
Tue, 17 Jun 2008 03:52:55 +0000 (22:52 -0500)
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.

libusb/core.c

index 8c5c8db..20aed50 100644 (file)
@@ -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