Skip to content
  • Sylvain Fasel's avatar
    ce43856a
    core: add libusb_get_config_string() and libusb_get_interface_string() · ce43856a
    Sylvain Fasel authored
    libusb_get_device_string() exposes only the device-descriptor strings
    (manufacturer, product, serial number).  Applications also need the
    configuration (iConfiguration) and interface (iInterface) strings, e.g.
    to present a human-readable interface name during device selection
    (#1855).
    
    Add two public APIs that mirror libusb_get_device_string() but for
    these descriptor-scoped strings, retrievable without opening the
    device:
    
        int libusb_get_config_string(libusb_device *dev,
            uint8_t config_value, char *data, int length);
        int libusb_get_interface_string(libusb_device *dev,
            uint8_t config_value, uint8_t interface_number,
            uint8_t alt_setting, char *data, int length);
    
    config_value is the bConfigurationValue, or 0 to select the currently
    active configuration (0 is never a valid bConfigurationValue).  As in
    libusb_get_device_string(), data may be NULL (with a non-zero length)
    to query the required buffer size, and the returned string length
    includes the NUL terminator.  LIBUSB_API_VERSION is bumped to
    0x0100010D; the functions first appear in 1.0.31.
    
    USB string descriptors are device-global and addressed by index, so
    the fetch is identical to the device strings; only resolving
    iConfiguration / iInterface from the cached configuration descriptor
    is new.  That resolution is shared in usbi_get_config_string_index() /
    usbi_get_interface_string_index(), and every backend resolves the
    index first, so a nonexistent configuration/interface/alternate
    setting or a missing string (index 0) fails identically everywhere
    (LIBUSB_ERROR_NOT_FOUND) before any platform limit applies
    (LIBUSB_ERROR_NOT_SUPPORTED).  The public wrappers fetch into an
    internal scratch buffer and reuse usbi_utf8_copy() for
    UTF-8-boundary-safe truncation, matching libusb_get_device_string()
    minus its persistent cache, which cannot index the unbounded
    config/interface/alt-setting space; results are therefore not cached,
    as documented.  Because the core always passes that valid scratch
    buffer, backends assert data/length rather than validate them.
    
    Backends:
    - windows (winusb): the parent-hub IOCTL fetch is extracted from
      winusb_get_device_string() into winusb_fetch_string_descriptor()
      (defined after the getters, so the original function and its
      explanatory comment stay in place) and shared by all three string
      getters.
    - darwin: a GET_DESCRIPTOR control request; IOKit does not cache
      interface/configuration strings as IORegistry properties.  The
      device lock is held across the whole open/request/close sequence so
      that the open_count check stays stable, a concurrent libusb_open() /
      libusb_close() cannot interleave with the temporary open (plain
      USBDeviceOpen(), never Seize), and the device interface cannot be
      swapped by a re-enumeration.  The primary language ID is cached in
      the cached device, as the winusb backend does.
    - linux: reads the named sysfs attributes (configuration,
      <dev>:<config>.<interface>/interface), sharing the read/strip helper
      with op_get_device_string() (which as a side effect now returns the
      length including the NUL terminator; its only caller checks the
      result solely for error).  Only the active configuration and the
      currently selected alternate setting are available without opening
      the device; other requests return LIBUSB_ERROR_NOT_SUPPORTED, as
      spelled out in the per-platform documentation notes.
    - backends without an implementation return
      LIBUSB_ERROR_NOT_SUPPORTED.
    
    The listdevs example prints the configuration and per-interface
    strings in --verbose mode.
    
    Assisted-by: claude-code:claude-opus-4-8
    ce43856a
    core: add libusb_get_config_string() and libusb_get_interface_string()
    Sylvain Fasel authored
    libusb_get_device_string() exposes only the device-descriptor strings
    (manufacturer, product, serial number).  Applications also need the
    configuration (iConfiguration) and interface (iInterface) strings, e.g.
    to present a human-readable interface name during device selection
    (#1855).
    
    Add two public APIs that mirror libusb_get_device_string() but for
    these descriptor-scoped strings, retrievable without opening the
    device:
    
        int libusb_get_config_string(libusb_device *dev,
            uint8_t config_value, char *data, int length);
        int libusb_get_interface_string(libusb_device *dev,
            uint8_t config_value, uint8_t interface_number,
            uint8_t alt_setting, char *data, int length);
    
    config_value is the bConfigurationValue, or 0 to select the currently
    active configuration (0 is never a valid bConfigurationValue).  As in
    libusb_get_device_string(), data may be NULL (with a non-zero length)
    to query the required buffer size, and the returned string length
    includes the NUL terminator.  LIBUSB_API_VERSION is bumped to
    0x0100010D; the functions first appear in 1.0.31.
    
    USB string descriptors are device-global and addressed by index, so
    the fetch is identical to the device strings; only resolving
    iConfiguration / iInterface from the cached configuration descriptor
    is new.  That resolution is shared in usbi_get_config_string_index() /
    usbi_get_interface_string_index(), and every backend resolves the
    index first, so a nonexistent configuration/interface/alternate
    setting or a missing string (index 0) fails identically everywhere
    (LIBUSB_ERROR_NOT_FOUND) before any platform limit applies
    (LIBUSB_ERROR_NOT_SUPPORTED).  The public wrappers fetch into an
    internal scratch buffer and reuse usbi_utf8_copy() for
    UTF-8-boundary-safe truncation, matching libusb_get_device_string()
    minus its persistent cache, which cannot index the unbounded
    config/interface/alt-setting space; results are therefore not cached,
    as documented.  Because the core always passes that valid scratch
    buffer, backends assert data/length rather than validate them.
    
    Backends:
    - windows (winusb): the parent-hub IOCTL fetch is extracted from
      winusb_get_device_string() into winusb_fetch_string_descriptor()
      (defined after the getters, so the original function and its
      explanatory comment stay in place) and shared by all three string
      getters.
    - darwin: a GET_DESCRIPTOR control request; IOKit does not cache
      interface/configuration strings as IORegistry properties.  The
      device lock is held across the whole open/request/close sequence so
      that the open_count check stays stable, a concurrent libusb_open() /
      libusb_close() cannot interleave with the temporary open (plain
      USBDeviceOpen(), never Seize), and the device interface cannot be
      swapped by a re-enumeration.  The primary language ID is cached in
      the cached device, as the winusb backend does.
    - linux: reads the named sysfs attributes (configuration,
      <dev>:<config>.<interface>/interface), sharing the read/strip helper
      with op_get_device_string() (which as a side effect now returns the
      length including the NUL terminator; its only caller checks the
      result solely for error).  Only the active configuration and the
      currently selected alternate setting are available without opening
      the device; other requests return LIBUSB_ERROR_NOT_SUPPORTED, as
      spelled out in the per-platform documentation notes.
    - backends without an implementation return
      LIBUSB_ERROR_NOT_SUPPORTED.
    
    The listdevs example prints the configuration and per-interface
    strings in --verbose mode.
    
    Assisted-by: claude-code:claude-opus-4-8
To find the state of this project's repository at the time of any of these versions, check out the tags.
Loading