| use std::{fmt, slice}; |
| |
| use libusb1_sys::*; |
| |
| use crate::interface_descriptor::{self, Interface}; |
| |
| /// Describes a configuration. |
| pub struct ConfigDescriptor { |
| descriptor: *const libusb_config_descriptor, |
| } |
| |
| impl Drop for ConfigDescriptor { |
| fn drop(&mut self) { |
| unsafe { |
| libusb_free_config_descriptor(self.descriptor); |
| } |
| } |
| } |
| |
| unsafe impl Sync for ConfigDescriptor {} |
| unsafe impl Send for ConfigDescriptor {} |
| |
| impl ConfigDescriptor { |
| /// Returns the size of the descriptor in bytes |
| pub fn length(&self) -> u8 { |
| unsafe { (*self.descriptor).bLength } |
| } |
| |
| /// Returns the total length in bytes of data returned for this configuration: all interfaces and endpoints |
| pub fn total_length(&self) -> u16 { |
| unsafe { (*self.descriptor).wTotalLength } |
| } |
| |
| /// Returns the descriptor type |
| pub fn descriptor_type(&self) -> u8 { |
| unsafe { (*self.descriptor).bDescriptorType } |
| } |
| |
| /// Returns the configuration number. |
| pub fn number(&self) -> u8 { |
| unsafe { (*self.descriptor).bConfigurationValue } |
| } |
| |
| /// Returns the device's maximum power consumption (in milliamps) in this configuration. |
| pub fn max_power(&self) -> u16 { |
| unsafe { u16::from((*self.descriptor).bMaxPower) * 2 } |
| } |
| |
| /// Indicates if the device is self-powered in this configuration. |
| pub fn self_powered(&self) -> bool { |
| unsafe { (*self.descriptor).bmAttributes & 0x40 != 0 } |
| } |
| |
| /// Indicates if the device has remote wakeup capability in this configuration. |
| pub fn remote_wakeup(&self) -> bool { |
| unsafe { (*self.descriptor).bmAttributes & 0x20 != 0 } |
| } |
| |
| /// Returns the index of the string descriptor that describes the configuration. |
| pub fn description_string_index(&self) -> Option<u8> { |
| unsafe { |
| match (*self.descriptor).iConfiguration { |
| 0 => None, |
| n => Some(n), |
| } |
| } |
| } |
| |
| /// Returns the number of interfaces for this configuration. |
| pub fn num_interfaces(&self) -> u8 { |
| unsafe { (*self.descriptor).bNumInterfaces } |
| } |
| |
| /// Returns a collection of the configuration's interfaces. |
| pub fn interfaces(&self) -> Interfaces { |
| let interfaces = unsafe { |
| slice::from_raw_parts( |
| (*self.descriptor).interface, |
| (*self.descriptor).bNumInterfaces as usize, |
| ) |
| }; |
| |
| Interfaces { |
| iter: interfaces.iter(), |
| } |
| } |
| |
| /// Returns the unknown 'extra' bytes that libusb does not understand. |
| pub fn extra(&self) -> &[u8] { |
| unsafe { |
| match (*self.descriptor).extra_length { |
| len if len > 0 => slice::from_raw_parts((*self.descriptor).extra, len as usize), |
| _ => &[], |
| } |
| } |
| } |
| } |
| |
| impl fmt::Debug for ConfigDescriptor { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| let mut debug = fmt.debug_struct("ConfigDescriptor"); |
| |
| let descriptor: &libusb_config_descriptor = unsafe { &*self.descriptor }; |
| |
| debug.field("bLength", &descriptor.bLength); |
| debug.field("bDescriptorType", &descriptor.bDescriptorType); |
| debug.field("wTotalLength", &descriptor.wTotalLength); |
| debug.field("bNumInterfaces", &descriptor.bNumInterfaces); |
| debug.field("bConfigurationValue", &descriptor.bConfigurationValue); |
| debug.field("iConfiguration", &descriptor.iConfiguration); |
| debug.field("bmAttributes", &descriptor.bmAttributes); |
| debug.field("bMaxPower", &descriptor.bMaxPower); |
| debug.field("extra", &self.extra()); |
| |
| debug.finish() |
| } |
| } |
| |
| /// Iterator over a configuration's interfaces. |
| pub struct Interfaces<'a> { |
| iter: slice::Iter<'a, libusb_interface>, |
| } |
| |
| impl<'a> Iterator for Interfaces<'a> { |
| type Item = Interface<'a>; |
| |
| fn next(&mut self) -> Option<Interface<'a>> { |
| self.iter |
| .next() |
| .map(|interface| unsafe { interface_descriptor::from_libusb(interface) }) |
| } |
| |
| fn size_hint(&self) -> (usize, Option<usize>) { |
| self.iter.size_hint() |
| } |
| } |
| |
| #[doc(hidden)] |
| pub(crate) unsafe fn from_libusb(config: *const libusb_config_descriptor) -> ConfigDescriptor { |
| ConfigDescriptor { descriptor: config } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use std::mem::ManuallyDrop; |
| |
| // The Drop trait impl calls libusb_free_config_descriptor(), which would attempt to free |
| // unallocated memory for a stack-allocated config descriptor. Allocating a config descriptor |
| // is not a simple malloc()/free() inside libusb. Mimicking libusb's allocation would be |
| // error-prone, difficult to maintain, and provide little benefit for the tests. It's easier to |
| // use mem::forget() to prevent the Drop trait impl from running. The config descriptor passed |
| // as `$config` should be stack-allocated to prevent memory leaks in the test suite. |
| macro_rules! with_config { |
| ($name:ident : $config:expr => $body:block) => {{ |
| let config = $config; |
| let $name = ManuallyDrop::new(unsafe { super::from_libusb(&config) }); |
| $body; |
| }}; |
| } |
| |
| #[test] |
| fn it_has_number() { |
| with_config!(config: config_descriptor!(bConfigurationValue: 42) => { |
| assert_eq!(42, config.number()); |
| }); |
| } |
| |
| #[test] |
| fn it_has_max_power() { |
| with_config!(config: config_descriptor!(bMaxPower: 21) => { |
| assert_eq!(42, config.max_power()); |
| }); |
| } |
| |
| #[test] |
| fn it_interprets_self_powered_bit_in_attributes() { |
| with_config!(config: config_descriptor!(bmAttributes: 0b0000_0000) => { |
| assert_eq!(false, config.self_powered()); |
| }); |
| |
| with_config!(config: config_descriptor!(bmAttributes: 0b0100_0000) => { |
| assert_eq!(true, config.self_powered()); |
| }); |
| } |
| |
| #[test] |
| fn it_interprets_remote_wakeup_bit_in_attributes() { |
| with_config!(config: config_descriptor!(bmAttributes: 0b0000_0000) => { |
| assert_eq!(false, config.remote_wakeup()); |
| }); |
| |
| with_config!(config: config_descriptor!(bmAttributes: 0b0010_0000) => { |
| assert_eq!(true, config.remote_wakeup()); |
| }); |
| } |
| |
| #[test] |
| fn it_has_description_string_index() { |
| with_config!(config: config_descriptor!(iConfiguration: 42) => { |
| assert_eq!(Some(42), config.description_string_index()); |
| }); |
| } |
| |
| #[test] |
| fn it_handles_missing_description_string_index() { |
| with_config!(config: config_descriptor!(iConfiguration: 0) => { |
| assert_eq!(None, config.description_string_index()); |
| }); |
| } |
| |
| #[test] |
| fn it_has_num_interfaces() { |
| let interface1 = interface!(interface_descriptor!(bInterfaceNumber: 1)); |
| let interface2 = interface!(interface_descriptor!(bInterfaceNumber: 2)); |
| |
| with_config!(config: config_descriptor!(interface1, interface2) => { |
| assert_eq!(2, config.num_interfaces()); |
| }); |
| } |
| |
| #[test] |
| fn it_has_interfaces() { |
| let interface = interface!(interface_descriptor!(bInterfaceNumber: 1)); |
| |
| with_config!(config: config_descriptor!(interface) => { |
| let interface_numbers = config.interfaces().map(|interface| { |
| interface.number() |
| }).collect::<Vec<_>>(); |
| |
| assert_eq!(vec![1], interface_numbers); |
| }); |
| } |
| |
| // Successful compilation shows that the lifetime of the endpoint descriptor(s) is the same |
| // as the lifetime of the config descriptor. |
| #[test] |
| fn it_had_interfaces_with_endpoints() { |
| let endpoint1 = endpoint_descriptor!(bEndpointAddress: 0x81); |
| let endpoint2 = endpoint_descriptor!(bEndpointAddress: 0x01); |
| let endpoint3 = endpoint_descriptor!(bEndpointAddress: 0x02); |
| let interface1 = interface!(interface_descriptor!(endpoint1, endpoint2)); |
| let interface2 = interface!(interface_descriptor!(endpoint3)); |
| |
| with_config!(config: config_descriptor!(interface1, interface2) => { |
| // Exists only to name config's lifetime. |
| fn named_lifetime<'a>(config: &'a super::ConfigDescriptor) { |
| let addresses: Vec<_> = config.interfaces().flat_map(|intf| intf.descriptors()).flat_map(|desc| desc.endpoint_descriptors()).map(|ep| ep.address()).collect(); |
| assert_eq!(addresses, &[0x81, 0x01, 0x02]); |
| let desc: crate::InterfaceDescriptor<'a> = config.interfaces().flat_map(|intf| intf.descriptors()).next().expect("There's one interface"); |
| let _: crate::EndpointDescriptor<'a> = desc.endpoint_descriptors().next().expect("There's one endpoint"); |
| } |
| named_lifetime(&*config); |
| }) |
| } |
| } |