From 514bb8790cfe8b93ccfff82bc17081b1030acce0 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 29 May 2008 12:35:01 +0100 Subject: 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. --- libusb/core.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- libusb/libusbi.h | 19 +++++++++++ libusb/os/linux_usbfs.c | 15 +++++++++ 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 @@ -763,6 +763,57 @@ API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle) return dev_handle->dev; } +/** \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 config 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 @@ -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, -- cgit v1.2.1