summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-05-29 12:35:01 +0100
committerDaniel Drake <dsd@gentoo.org>2008-05-29 12:35:01 +0100
commit514bb8790cfe8b93ccfff82bc17081b1030acce0 (patch)
tree8a59c368cbca414f2dc46532593ee7be463d5934
parentd1292f8e7300051239a7ed2769d221dc7a6f9fca (diff)
downloadlibusb-514bb8790cfe8b93ccfff82bc17081b1030acce0.tar.gz
Refine libusb_set_configuration() semantics
Applications will generally want to set a configuration before claiming interfaces. The problem is that the interface may already be set, and someone else may have claimed an interface (meaning that all calls to set_configuration will fail, even if it's for the same configuration). There are now 2 options: 1. Use the new libusb_get_configuration() to determine active configuration before calling libusb_set_configuration() 2. Or just call libusb_set_configuration() as usual, which will do nothing if that configuration is already active.
-rw-r--r--libusb/core.c89
-rw-r--r--libusb/libusbi.h19
-rw-r--r--libusb/os/linux_usbfs.c15
3 files changed, 120 insertions, 3 deletions
diff --git a/libusb/core.c b/libusb/core.c
index 292e3ab..a4668f0 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -764,6 +764,57 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle)
}
/** \ingroup dev
+ * Determine the bConfigurationValue of the currently active configuration.
+ *
+ * You could formulate your own control request to obtain this information,
+ * but this function has the advantage that it may be able to retrieve the
+ * information from operating system caches (no I/O involved).
+ *
+ * If the OS does not cache this information, then this function will block
+ * while a control transfer is submitted to retrieve the information.
+ *
+ * This function will return a value of 0 in the <tt>config</tt> output
+ * parameter if the device is in unconfigured state.
+ *
+ * \param dev a device handle
+ * \param config output location for the bConfigurationValue of the active
+ * configuration (only valid for return code 0)
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_get_configuration(libusb_device_handle *dev,
+ int *config)
+{
+ int r = LIBUSB_ERROR_NOT_SUPPORTED;
+
+ usbi_dbg("");
+ if (usbi_backend->get_configuration)
+ r = usbi_backend->get_configuration(dev, config);
+
+ if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
+ uint8_t tmp = 0;
+ usbi_dbg("falling back to control message");
+ r = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
+ if (r == 0) {
+ usbi_err("zero bytes returned in ctrl transfer?");
+ r = LIBUSB_ERROR_IO;
+ } else if (r == 1) {
+ r = 0;
+ *config = tmp;
+ } else {
+ usbi_dbg("control failed, error %d", r);
+ }
+ }
+
+ if (r == 0)
+ usbi_dbg("active config %d", *config);
+
+ return r;
+}
+
+/** \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
@@ -771,12 +822,27 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle)
*
* 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.
+ * 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.
*
* 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.
@@ -786,7 +852,8 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle)
* \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 0 on success
+ * \returns 1 if the configuration was changed
+ * \returns 0 if the configuration was already active
* \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
@@ -795,8 +862,24 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle)
API_EXPORTED int libusb_set_configuration(libusb_device_handle *dev,
int configuration)
{
+ int r;
+ int active;
+
usbi_dbg("configuration %d", configuration);
- return usbi_backend->set_configuration(dev, 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;
}
/** \ingroup dev
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 41c8097..5c00a63 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -436,6 +436,25 @@ struct usbi_os_backend {
uint8_t config_index, unsigned char *buffer, size_t len,
int *host_endian);
+ /* Get the bConfigurationValue for the active configuration for a device.
+ * Optional. This should only be implemented if you can retrieve it from
+ * cache (don't generate I/O).
+ *
+ * If you cannot retrieve this from cache, either do not implement this
+ * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause
+ * libusb to retrieve the information through a standard control transfer.
+ *
+ * This function must be non-blocking.
+ * Return:
+ * - 0 on success
+ * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
+ * was opened
+ * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without
+ * blocking
+ * - another LIBUSB_ERROR code on other failure.
+ */
+ int (*get_configuration)(struct libusb_device_handle *handle, int *config);
+
/* Set the active configuration for a device.
*
* A configuration value of -1 should put the device in unconfigured state.
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 7dc6b6c..2e9e0e2 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -904,6 +904,20 @@ static void op_close(struct libusb_device_handle *dev_handle)
close(fd);
}
+static int op_get_configuration(struct libusb_device_handle *handle,
+ int *config)
+{
+ int r;
+ if (sysfs_can_relate_devices != 1)
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
+ r = sysfs_get_active_config(handle->dev, config);
+ if (*config == -1)
+ *config = 0;
+
+ return 0;
+}
+
static int op_set_configuration(struct libusb_device_handle *handle, int config)
{
struct linux_device_priv *priv = __device_priv(handle->dev);
@@ -1827,6 +1841,7 @@ const struct usbi_os_backend linux_usbfs_backend = {
.open = op_open,
.close = op_close,
+ .get_configuration = op_get_configuration,
.set_configuration = op_set_configuration,
.claim_interface = op_claim_interface,
.release_interface = op_release_interface,