blob: 3bf19ba1aff38faf5f6b73d55ef67653b004fd38 [file] [log] [blame] [edit]
use std::{
fmt::{self, Debug},
mem,
ptr::NonNull,
sync::Mutex,
time::Duration,
};
use libc::{c_int, c_uchar, c_uint};
use libusb1_sys::{constants::*, *};
use crate::{
config_descriptor::ConfigDescriptor,
device::{self, Device},
device_descriptor::DeviceDescriptor,
error::{self, Error},
fields::{request_type, Direction, Recipient, RequestType},
interface_descriptor::InterfaceDescriptor,
language::Language,
UsbContext,
};
/// Bit set representing claimed USB interfaces.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
struct ClaimedInterfaces {
inner: [u128; 2],
}
impl ClaimedInterfaces {
/// Create a new bit set.
fn new() -> Self {
Self { inner: [0, 0] }
}
fn get_index_and_mask(interface: u8) -> (usize, u128) {
((interface / 128) as usize, 1 << (interface % 128))
}
/// Mark `interface` as claimed.
fn insert(&mut self, interface: u8) {
let (index, mask) = ClaimedInterfaces::get_index_and_mask(interface);
self.inner[index] |= mask;
}
/// Mark `interface` as not claimed.
fn remove(&mut self, interface: u8) {
let (index, mask) = ClaimedInterfaces::get_index_and_mask(interface);
self.inner[index] &= !mask;
}
/// Returns true if this set contains `interface`.
fn contains(&self, interface: u8) -> bool {
let (index, mask) = ClaimedInterfaces::get_index_and_mask(interface);
self.inner[index] & mask != 0
}
/// Returns a count of the interfaces contained in this set.
fn size(&self) -> usize {
self.inner.iter().map(|v| v.count_ones()).sum::<u32>() as usize
}
/// Returns an iterator over the interfaces in this set.
fn iter(&self) -> ClaimedInterfacesIter {
ClaimedInterfacesIter::new(self)
}
}
/// Iterator over interfaces.
struct ClaimedInterfacesIter<'a> {
// Next interface to check as a possible value to return from the interator.
index: u16,
// Number of elements remaining in this iterator.
remaining: usize,
// The ClaimedInterfaces object that we're iterating over.
source: &'a ClaimedInterfaces,
}
impl ClaimedInterfacesIter<'_> {
/// Create a new iterator over the interfaces in `source`.
fn new(source: &ClaimedInterfaces) -> ClaimedInterfacesIter {
ClaimedInterfacesIter {
index: 0,
remaining: source.size(),
source,
}
}
}
impl<'a> Iterator for ClaimedInterfacesIter<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
while self.index <= u8::MAX as u16 {
let index = self.index as u8;
let contains = self.source.contains(index);
self.index += 1;
if contains {
self.remaining -= 1;
return Some(index);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
/// A handle to an open USB device.
pub struct DeviceHandle<T: UsbContext> {
context: T,
handle: Option<NonNull<libusb_device_handle>>,
interfaces: Mutex<ClaimedInterfaces>,
}
impl<T: UsbContext> Drop for DeviceHandle<T> {
/// Closes the device.
fn drop(&mut self) {
unsafe {
let interfaces = self.interfaces.lock().unwrap();
for iface in interfaces.iter() {
libusb_release_interface(self.as_raw(), iface as c_int);
}
if let Some(handle) = self.handle {
libusb_close(handle.as_ptr());
}
}
}
}
unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
unsafe impl<T: UsbContext> Sync for DeviceHandle<T> {}
impl<T: UsbContext> Debug for DeviceHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("DeviceHandle")
.field("device", &self.device())
.field("handle", &self.handle)
.field("interfaces", &*self.interfaces.lock().unwrap())
.finish()
}
}
impl<T: UsbContext + PartialEq> PartialEq for DeviceHandle<T> {
fn eq(&self, other: &Self) -> bool {
self.context == other.context
&& self.handle == other.handle
&& *self.interfaces.lock().unwrap() == *other.interfaces.lock().unwrap()
}
}
impl<T: UsbContext + PartialEq> Eq for DeviceHandle<T> {}
impl<T: UsbContext> DeviceHandle<T> {
/// Get the raw libusb_device_handle pointer, for advanced use in unsafe code.
///
/// This structure tracks claimed interfaces, and will get out if sync if interfaces are
/// manipulated externally. Use only libusb endpoint IO functions.
pub fn as_raw(&self) -> *mut libusb_device_handle {
// Safety: handle is Some() after initialization
match self.handle {
Some(it) => it.as_ptr(),
_ => unreachable!(),
}
}
/// Consumes the `DeviceHandle`, returning the raw libusb_device_handle
/// pointer, for advanced use in unsafe code.
///
/// # Safety
///
/// Panics if you have any claimed interfaces on this handle.
pub fn into_raw(mut self) -> *mut libusb_device_handle {
assert_eq!(self.interfaces.lock().unwrap().size(), 0);
match self.handle.take() {
Some(it) => it.as_ptr(),
_ => unreachable!(),
}
}
/// Get the context associated with this device
pub fn context(&self) -> &T {
&self.context
}
/// Get the device associated to this handle
pub fn device(&self) -> Device<T> {
unsafe {
device::Device::from_libusb(
self.context.clone(),
std::ptr::NonNull::new_unchecked(libusb_get_device(self.as_raw())),
)
}
}
/// # Safety
///
/// Converts an existing `libusb_device_handle` pointer into a `DeviceHandle<T>`.
/// `handle` must be a pointer to a valid `libusb_device_handle`. Rusb assumes ownership of the handle, and will close it on `drop`.
pub unsafe fn from_libusb(
context: T,
handle: NonNull<libusb_device_handle>,
) -> DeviceHandle<T> {
DeviceHandle {
context,
handle: Some(handle),
interfaces: Mutex::new(ClaimedInterfaces::new()),
}
}
/// Returns the active configuration number.
pub fn active_configuration(&self) -> crate::Result<u8> {
let mut config = mem::MaybeUninit::<c_int>::uninit();
try_unsafe!(libusb_get_configuration(self.as_raw(), config.as_mut_ptr()));
Ok(unsafe { config.assume_init() } as u8)
}
/// Sets the device's active configuration.
pub fn set_active_configuration(&self, config: u8) -> crate::Result<()> {
try_unsafe!(libusb_set_configuration(self.as_raw(), c_int::from(config)));
Ok(())
}
/// Puts the device in an unconfigured state.
pub fn unconfigure(&self) -> crate::Result<()> {
try_unsafe!(libusb_set_configuration(self.as_raw(), -1));
Ok(())
}
/// Resets the device.
pub fn reset(&self) -> crate::Result<()> {
try_unsafe!(libusb_reset_device(self.as_raw()));
Ok(())
}
/// Clear the halt/stall condition for an endpoint.
pub fn clear_halt(&self, endpoint: u8) -> crate::Result<()> {
try_unsafe!(libusb_clear_halt(self.as_raw(), endpoint));
Ok(())
}
/// Indicates whether the device has an attached kernel driver.
///
/// This method is not supported on all platforms.
pub fn kernel_driver_active(&self, iface: u8) -> crate::Result<bool> {
match unsafe { libusb_kernel_driver_active(self.as_raw(), c_int::from(iface)) } {
0 => Ok(false),
1 => Ok(true),
err => Err(error::from_libusb(err)),
}
}
/// Detaches an attached kernel driver from the device.
///
/// This method is not supported on all platforms.
pub fn detach_kernel_driver(&self, iface: u8) -> crate::Result<()> {
try_unsafe!(libusb_detach_kernel_driver(
self.as_raw(),
c_int::from(iface)
));
Ok(())
}
/// Attaches a kernel driver to the device.
///
/// This method is not supported on all platforms.
pub fn attach_kernel_driver(&self, iface: u8) -> crate::Result<()> {
try_unsafe!(libusb_attach_kernel_driver(
self.as_raw(),
c_int::from(iface)
));
Ok(())
}
/// Enable/disable automatic kernel driver detachment.
///
/// When this is enabled rusb will automatically detach the
/// kernel driver on an interface when claiming the interface, and
/// attach it when releasing the interface.
///
/// On platforms which do not have support, this function will
/// return `Error::NotSupported`, and rusb will continue as if
/// this function was never called.
pub fn set_auto_detach_kernel_driver(&self, auto_detach: bool) -> crate::Result<()> {
try_unsafe!(libusb_set_auto_detach_kernel_driver(
self.as_raw(),
auto_detach.into()
));
Ok(())
}
/// Claims one of the device's interfaces.
///
/// An interface must be claimed before operating on it. All claimed interfaces are released
/// when the device handle goes out of scope.
pub fn claim_interface(&self, iface: u8) -> crate::Result<()> {
try_unsafe!(libusb_claim_interface(self.as_raw(), c_int::from(iface)));
self.interfaces.lock().unwrap().insert(iface);
Ok(())
}
/// Releases a claimed interface.
pub fn release_interface(&self, iface: u8) -> crate::Result<()> {
try_unsafe!(libusb_release_interface(self.as_raw(), c_int::from(iface)));
self.interfaces.lock().unwrap().remove(iface);
Ok(())
}
/// Sets an interface's active setting.
pub fn set_alternate_setting(&self, iface: u8, setting: u8) -> crate::Result<()> {
try_unsafe!(libusb_set_interface_alt_setting(
self.as_raw(),
c_int::from(iface),
c_int::from(setting)
));
Ok(())
}
/// Reads from an interrupt endpoint.
///
/// This function attempts to read from the interrupt endpoint with the address given by the
/// `endpoint` parameter and fills `buf` with any data received from the endpoint. The function
/// blocks up to the amount of time specified by `timeout`. Minimal `timeout` is 1 milliseconds,
/// anything smaller will result in an infinite block.
///
/// If the return value is `Ok(n)`, then `buf` is populated with `n` bytes of data received
/// from the endpoint.
///
/// ## Errors
///
/// If this function encounters any form of error while fulfilling the transfer request, an
/// error variant will be returned. If an error variant is returned, no bytes were read.
///
/// The errors returned by this function include:
///
/// * `InvalidParam` if the endpoint is not an input endpoint.
/// * `Timeout` if the transfer timed out.
/// * `Pipe` if the endpoint halted.
/// * `Overflow` if the device offered more data.
/// * `NoDevice` if the device has been disconnected.
/// * `Io` if the transfer encountered an I/O error.
pub fn read_interrupt(
&self,
endpoint: u8,
buf: &mut [u8],
timeout: Duration,
) -> crate::Result<usize> {
if endpoint & LIBUSB_ENDPOINT_DIR_MASK != LIBUSB_ENDPOINT_IN {
return Err(Error::InvalidParam);
}
let mut transferred = mem::MaybeUninit::<c_int>::uninit();
unsafe {
match libusb_interrupt_transfer(
self.as_raw(),
endpoint,
buf.as_mut_ptr() as *mut c_uchar,
buf.len() as c_int,
transferred.as_mut_ptr(),
timeout.as_millis() as c_uint,
) {
0 => Ok(transferred.assume_init() as usize),
err if err == LIBUSB_ERROR_INTERRUPTED => {
let transferred = transferred.assume_init();
if transferred > 0 {
Ok(transferred as usize)
} else {
Err(error::from_libusb(err))
}
}
err => Err(error::from_libusb(err)),
}
}
}
/// Writes to an interrupt endpoint.
///
/// This function attempts to write the contents of `buf` to the interrupt endpoint with the
/// address given by the `endpoint` parameter. The function blocks up to the amount of time
/// specified by `timeout`. Minimal `timeout` is 1 milliseconds, anything smaller will
/// result in an infinite block.
///
/// If the return value is `Ok(n)`, then `n` bytes of `buf` were written to the endpoint.
///
/// ## Errors
///
/// If this function encounters any form of error while fulfilling the transfer request, an
/// error variant will be returned. If an error variant is returned, no bytes were written.
///
/// The errors returned by this function include:
///
/// * `InvalidParam` if the endpoint is not an output endpoint.
/// * `Timeout` if the transfer timed out.
/// * `Pipe` if the endpoint halted.
/// * `NoDevice` if the device has been disconnected.
/// * `Io` if the transfer encountered an I/O error.
pub fn write_interrupt(
&self,
endpoint: u8,
buf: &[u8],
timeout: Duration,
) -> crate::Result<usize> {
if endpoint & LIBUSB_ENDPOINT_DIR_MASK != LIBUSB_ENDPOINT_OUT {
return Err(Error::InvalidParam);
}
let mut transferred = mem::MaybeUninit::<c_int>::uninit();
unsafe {
match libusb_interrupt_transfer(
self.as_raw(),
endpoint,
buf.as_ptr() as *mut c_uchar,
buf.len() as c_int,
transferred.as_mut_ptr(),
timeout.as_millis() as c_uint,
) {
0 => Ok(transferred.assume_init() as usize),
err if err == LIBUSB_ERROR_INTERRUPTED => {
let transferred = transferred.assume_init();
if transferred > 0 {
Ok(transferred as usize)
} else {
Err(error::from_libusb(err))
}
}
err => Err(error::from_libusb(err)),
}
}
}
/// Reads from a bulk endpoint.
///
/// This function attempts to read from the bulk endpoint with the address given by the
/// `endpoint` parameter and fills `buf` with any data received from the endpoint. The function
/// blocks up to the amount of time specified by `timeout`. Minimal `timeout` is 1 milliseconds,
/// anything smaller will result in an infinite block.
///
/// If the return value is `Ok(n)`, then `buf` is populated with `n` bytes of data received
/// from the endpoint.
///
/// ## Errors
///
/// If this function encounters any form of error while fulfilling the transfer request, an
/// error variant will be returned. If an error variant is returned, no bytes were read.
///
/// The errors returned by this function include:
///
/// * `InvalidParam` if the endpoint is not an input endpoint.
/// * `Timeout` if the transfer timed out.
/// * `Pipe` if the endpoint halted.
/// * `Overflow` if the device offered more data.
/// * `NoDevice` if the device has been disconnected.
/// * `Io` if the transfer encountered an I/O error.
pub fn read_bulk(
&self,
endpoint: u8,
buf: &mut [u8],
timeout: Duration,
) -> crate::Result<usize> {
if endpoint & LIBUSB_ENDPOINT_DIR_MASK != LIBUSB_ENDPOINT_IN {
return Err(Error::InvalidParam);
}
let mut transferred = mem::MaybeUninit::<c_int>::uninit();
unsafe {
match libusb_bulk_transfer(
self.as_raw(),
endpoint,
buf.as_mut_ptr() as *mut c_uchar,
buf.len() as c_int,
transferred.as_mut_ptr(),
timeout.as_millis() as c_uint,
) {
0 => Ok(transferred.assume_init() as usize),
err if err == LIBUSB_ERROR_INTERRUPTED || err == LIBUSB_ERROR_TIMEOUT => {
let transferred = transferred.assume_init();
if transferred > 0 {
Ok(transferred as usize)
} else {
Err(error::from_libusb(err))
}
}
err => Err(error::from_libusb(err)),
}
}
}
/// Writes to a bulk endpoint.
///
/// This function attempts to write the contents of `buf` to the bulk endpoint with the address
/// given by the `endpoint` parameter. The function blocks up to the amount of time specified
/// by `timeout`. Minimal `timeout` is 1 milliseconds, anything smaller will result in an
/// infinite block.
///
/// If the return value is `Ok(n)`, then `n` bytes of `buf` were written to the endpoint.
///
/// ## Errors
///
/// If this function encounters any form of error while fulfilling the transfer request, an
/// error variant will be returned. If an error variant is returned, no bytes were written.
///
/// The errors returned by this function include:
///
/// * `InvalidParam` if the endpoint is not an output endpoint.
/// * `Timeout` if the transfer timed out.
/// * `Pipe` if the endpoint halted.
/// * `NoDevice` if the device has been disconnected.
/// * `Io` if the transfer encountered an I/O error.
pub fn write_bulk(&self, endpoint: u8, buf: &[u8], timeout: Duration) -> crate::Result<usize> {
if endpoint & LIBUSB_ENDPOINT_DIR_MASK != LIBUSB_ENDPOINT_OUT {
return Err(Error::InvalidParam);
}
let mut transferred = mem::MaybeUninit::<c_int>::uninit();
unsafe {
match libusb_bulk_transfer(
self.as_raw(),
endpoint,
buf.as_ptr() as *mut c_uchar,
buf.len() as c_int,
transferred.as_mut_ptr(),
timeout.as_millis() as c_uint,
) {
0 => Ok(transferred.assume_init() as usize),
err if err == LIBUSB_ERROR_INTERRUPTED || err == LIBUSB_ERROR_TIMEOUT => {
let transferred = transferred.assume_init();
if transferred > 0 {
Ok(transferred as usize)
} else {
Err(error::from_libusb(err))
}
}
err => Err(error::from_libusb(err)),
}
}
}
/// Reads data using a control transfer.
///
/// This function attempts to read data from the device using a control transfer and fills
/// `buf` with any data received during the transfer. The function blocks up to the amount of
/// time specified by `timeout`. Minimal `timeout` is 1 milliseconds, anything smaller will
/// result in an infinite block.
///
/// The parameters `request_type`, `request`, `value`, and `index` specify the fields of the
/// control transfer setup packet (`bmRequestType`, `bRequest`, `wValue`, and `wIndex`
/// respectively). The values for each of these parameters shall be given in host-endian byte
/// order. The value for the `request_type` parameter can be built with the helper function,
/// [request_type()](fn.request_type.html). The meaning of the other parameters depends on the
/// type of control request.
///
/// If the return value is `Ok(n)`, then `buf` is populated with `n` bytes of data.
///
/// ## Errors
///
/// If this function encounters any form of error while fulfilling the transfer request, an
/// error variant will be returned. If an error variant is returned, no bytes were read.
///
/// The errors returned by this function include:
///
/// * `InvalidParam` if `request_type` does not specify a read transfer.
/// * `Timeout` if the transfer timed out.
/// * `Pipe` if the control request was not supported by the device.
/// * `NoDevice` if the device has been disconnected.
/// * `Io` if the transfer encountered an I/O error.
pub fn read_control(
&self,
request_type: u8,
request: u8,
value: u16,
index: u16,
buf: &mut [u8],
timeout: Duration,
) -> crate::Result<usize> {
if request_type & LIBUSB_ENDPOINT_DIR_MASK != LIBUSB_ENDPOINT_IN {
return Err(Error::InvalidParam);
}
let res = unsafe {
libusb_control_transfer(
self.as_raw(),
request_type,
request,
value,
index,
buf.as_mut_ptr() as *mut c_uchar,
buf.len() as u16,
timeout.as_millis() as c_uint,
)
};
if res < 0 {
Err(error::from_libusb(res))
} else {
Ok(res as usize)
}
}
/// Writes data using a control transfer.
///
/// This function attempts to write the contents of `buf` to the device using a control
/// transfer. The function blocks up to the amount of time specified by `timeout`.
/// Minimal `timeout` is 1 milliseconds, anything smaller will result in an infinite block.
///
/// The parameters `request_type`, `request`, `value`, and `index` specify the fields of the
/// control transfer setup packet (`bmRequestType`, `bRequest`, `wValue`, and `wIndex`
/// respectively). The values for each of these parameters shall be given in host-endian byte
/// order. The value for the `request_type` parameter can be built with the helper function,
/// [request_type()](fn.request_type.html). The meaning of the other parameters depends on the
/// type of control request.
///
/// If the return value is `Ok(n)`, then `n` bytes of `buf` were transfered.
///
/// ## Errors
///
/// If this function encounters any form of error while fulfilling the transfer request, an
/// error variant will be returned. If an error variant is returned, no bytes were read.
///
/// The errors returned by this function include:
///
/// * `InvalidParam` if `request_type` does not specify a write transfer.
/// * `Timeout` if the transfer timed out.
/// * `Pipe` if the control request was not supported by the device.
/// * `NoDevice` if the device has been disconnected.
/// * `Io` if the transfer encountered an I/O error.
pub fn write_control(
&self,
request_type: u8,
request: u8,
value: u16,
index: u16,
buf: &[u8],
timeout: Duration,
) -> crate::Result<usize> {
if request_type & LIBUSB_ENDPOINT_DIR_MASK != LIBUSB_ENDPOINT_OUT {
return Err(Error::InvalidParam);
}
let res = unsafe {
libusb_control_transfer(
self.as_raw(),
request_type,
request,
value,
index,
buf.as_ptr() as *mut c_uchar,
buf.len() as u16,
timeout.as_millis() as c_uint,
)
};
if res < 0 {
Err(error::from_libusb(res))
} else {
Ok(res as usize)
}
}
/// Reads the languages supported by the device's string descriptors.
///
/// This function returns a list of languages that can be used to read the device's string
/// descriptors.
pub fn read_languages(&self, timeout: Duration) -> crate::Result<Vec<Language>> {
let mut buf = [0u8; 255];
let len = self.read_control(
request_type(Direction::In, RequestType::Standard, Recipient::Device),
LIBUSB_REQUEST_GET_DESCRIPTOR,
u16::from(LIBUSB_DT_STRING) << 8,
0,
&mut buf,
timeout,
)?;
if len < 2 || buf[0] != len as u8 || len & 0x01 != 0 {
return Err(Error::BadDescriptor);
}
if len == 2 {
return Ok(Vec::new());
}
Ok(buf[0..len]
.chunks(2)
.skip(1)
.map(|chunk| {
let lang_id = u16::from(chunk[0]) | u16::from(chunk[1]) << 8;
crate::language::from_lang_id(lang_id)
})
.collect())
}
/// Reads a ascii string descriptor from the device.
///
pub fn read_string_descriptor_ascii(&self, index: u8) -> crate::Result<String> {
let mut buf = Vec::<u8>::with_capacity(255);
let ptr = buf.as_mut_ptr() as *mut c_uchar;
let capacity = buf.capacity() as i32;
let res =
unsafe { libusb_get_string_descriptor_ascii(self.as_raw(), index, ptr, capacity) };
if res < 0 {
return Err(error::from_libusb(res));
}
unsafe {
buf.set_len(res as usize);
}
String::from_utf8(buf).map_err(|_| Error::Other)
}
/// Reads a string descriptor from the device.
///
/// `language` should be one of the languages returned from [`read_languages`](#method.read_languages).
pub fn read_string_descriptor(
&self,
language: Language,
index: u8,
timeout: Duration,
) -> crate::Result<String> {
let mut buf = [0u16; 128];
let len = {
// SAFETY: since we create slice from existing slice pointer valid
// alignment of [u8] less or equal to the [u16]
// size is less then allocated buffer (128 * 2 = 256 => 256 < 255)
let buf = unsafe {
std::slice::from_raw_parts_mut(
buf.as_mut_ptr().cast::<u8>(),
255, // Some devices choke on size > 255
)
};
let len = self.read_control(
request_type(Direction::In, RequestType::Standard, Recipient::Device),
LIBUSB_REQUEST_GET_DESCRIPTOR,
u16::from(LIBUSB_DT_STRING) << 8 | u16::from(index),
language.lang_id(),
buf,
timeout,
)?;
if len < 2 || buf[0] != len as u8 || len & 0x01 != 0 {
return Err(Error::BadDescriptor);
}
len
};
if len == 2 {
return Ok(String::new());
}
// len in bytes, skip first element(it's contain descriptor type and len)
String::from_utf16(&buf[1..(len / 2)]).map_err(|_| Error::Other)
}
/// Reads the device's manufacturer string descriptor (ascii).
pub fn read_manufacturer_string_ascii(
&self,
device: &DeviceDescriptor,
) -> crate::Result<String> {
match device.manufacturer_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor_ascii(n),
}
}
/// Reads the device's manufacturer string descriptor.
pub fn read_manufacturer_string(
&self,
language: Language,
device: &DeviceDescriptor,
timeout: Duration,
) -> crate::Result<String> {
match device.manufacturer_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor(language, n, timeout),
}
}
/// Reads the device's product string descriptor (ascii).
pub fn read_product_string_ascii(&self, device: &DeviceDescriptor) -> crate::Result<String> {
match device.product_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor_ascii(n),
}
}
/// Reads the device's product string descriptor.
pub fn read_product_string(
&self,
language: Language,
device: &DeviceDescriptor,
timeout: Duration,
) -> crate::Result<String> {
match device.product_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor(language, n, timeout),
}
}
/// Reads the device's serial number string descriptor (ascii).
pub fn read_serial_number_string_ascii(
&self,
device: &DeviceDescriptor,
) -> crate::Result<String> {
match device.serial_number_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor_ascii(n),
}
}
/// Reads the device's serial number string descriptor.
pub fn read_serial_number_string(
&self,
language: Language,
device: &DeviceDescriptor,
timeout: Duration,
) -> crate::Result<String> {
match device.serial_number_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor(language, n, timeout),
}
}
/// Reads the string descriptor for a configuration's description.
pub fn read_configuration_string(
&self,
language: Language,
configuration: &ConfigDescriptor,
timeout: Duration,
) -> crate::Result<String> {
match configuration.description_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor(language, n, timeout),
}
}
/// Reads the string descriptor for a interface's description.
pub fn read_interface_string(
&self,
language: Language,
interface: &InterfaceDescriptor,
timeout: Duration,
) -> crate::Result<String> {
match interface.description_string_index() {
None => Err(Error::InvalidParam),
Some(n) => self.read_string_descriptor(language, n, timeout),
}
}
}
#[cfg(test)]
mod tests {
use super::ClaimedInterfaces;
use std::u8;
#[test]
fn claimed_interfaces_empty() {
let empty = ClaimedInterfaces::new();
assert_eq!(empty.size(), 0);
for i in 0..=u8::MAX {
assert!(!empty.contains(i), "empty set should not contain {}", i);
}
let mut iter = empty.iter();
assert_eq!(iter.size_hint(), (0, Some(0)));
assert_eq!(iter.next(), None);
}
#[test]
fn claimed_interfaces_one_element() {
let mut interfaces = ClaimedInterfaces::new();
interfaces.insert(94);
assert_eq!(interfaces.size(), 1);
assert!(interfaces.contains(94));
for i in 0..=u8::MAX {
if i == 94 {
continue;
}
assert!(
!interfaces.contains(i),
"interfaces should not contain {}",
i
);
}
let mut iter = interfaces.iter();
assert_eq!(iter.size_hint(), (1, Some(1)));
assert_eq!(iter.next(), Some(94));
assert_eq!(iter.size_hint(), (0, Some(0)));
assert_eq!(iter.next(), None);
}
#[test]
fn claimed_interfaces_many_elements() {
let mut interfaces = ClaimedInterfaces::new();
let elements = vec![94, 0, 255, 17, 183, 6];
for (index, &interface) in elements.iter().enumerate() {
interfaces.insert(interface);
assert_eq!(interfaces.size(), index + 1);
}
// Validate contains().
for &interface in elements.iter() {
assert!(
interfaces.contains(interface),
"interfaces should contain {}",
interface
);
}
// Validate iter().
let contents = interfaces.iter().collect::<Vec<_>>().sort();
assert_eq!(contents, elements.clone().sort());
// Validate size_hint().
let mut iter = interfaces.iter();
let mut read = 0;
loop {
assert!(
read <= elements.len(),
"read elements {} should not exceed elements size {}",
read,
elements.len()
);
let remaining = elements.len() - read;
assert_eq!(iter.size_hint(), (remaining, Some(remaining)));
match iter.next() {
Some(_) => read += 1,
None => break,
}
}
}
}