1 use std::{fmt, slice};
2 
3 use libusb1_sys::*;
4 
5 use crate::interface_descriptor::{self, Interface};
6 
7 /// Describes a configuration.
8 pub struct ConfigDescriptor {
9     descriptor: *const libusb_config_descriptor,
10 }
11 
12 impl Drop for ConfigDescriptor {
drop(&mut self)13     fn drop(&mut self) {
14         unsafe {
15             libusb_free_config_descriptor(self.descriptor);
16         }
17     }
18 }
19 
20 unsafe impl Sync for ConfigDescriptor {}
21 unsafe impl Send for ConfigDescriptor {}
22 
23 impl ConfigDescriptor {
24     /// Returns the size of the descriptor in bytes
length(&self) -> u825     pub fn length(&self) -> u8 {
26         unsafe { (*self.descriptor).bLength }
27     }
28 
29     /// Returns the total length in bytes of data returned for this configuration: all interfaces and endpoints
total_length(&self) -> u1630     pub fn total_length(&self) -> u16 {
31         unsafe { (*self.descriptor).wTotalLength }
32     }
33 
34     /// Returns the descriptor type
descriptor_type(&self) -> u835     pub fn descriptor_type(&self) -> u8 {
36         unsafe { (*self.descriptor).bDescriptorType }
37     }
38 
39     /// Returns the configuration number.
number(&self) -> u840     pub fn number(&self) -> u8 {
41         unsafe { (*self.descriptor).bConfigurationValue }
42     }
43 
44     /// Returns the device's maximum power consumption (in milliamps) in this configuration.
max_power(&self) -> u1645     pub fn max_power(&self) -> u16 {
46         unsafe { u16::from((*self.descriptor).bMaxPower) * 2 }
47     }
48 
49     /// Indicates if the device is self-powered in this configuration.
self_powered(&self) -> bool50     pub fn self_powered(&self) -> bool {
51         unsafe { (*self.descriptor).bmAttributes & 0x40 != 0 }
52     }
53 
54     /// Indicates if the device has remote wakeup capability in this configuration.
remote_wakeup(&self) -> bool55     pub fn remote_wakeup(&self) -> bool {
56         unsafe { (*self.descriptor).bmAttributes & 0x20 != 0 }
57     }
58 
59     /// Returns the index of the string descriptor that describes the configuration.
description_string_index(&self) -> Option<u8>60     pub fn description_string_index(&self) -> Option<u8> {
61         unsafe {
62             match (*self.descriptor).iConfiguration {
63                 0 => None,
64                 n => Some(n),
65             }
66         }
67     }
68 
69     /// Returns the number of interfaces for this configuration.
num_interfaces(&self) -> u870     pub fn num_interfaces(&self) -> u8 {
71         unsafe { (*self.descriptor).bNumInterfaces }
72     }
73 
74     /// Returns a collection of the configuration's interfaces.
interfaces(&self) -> Interfaces75     pub fn interfaces(&self) -> Interfaces {
76         let interfaces = unsafe {
77             slice::from_raw_parts(
78                 (*self.descriptor).interface,
79                 (*self.descriptor).bNumInterfaces as usize,
80             )
81         };
82 
83         Interfaces {
84             iter: interfaces.iter(),
85         }
86     }
87 
88     /// Returns the unknown 'extra' bytes that libusb does not understand.
extra(&self) -> &[u8]89     pub fn extra(&self) -> &[u8] {
90         unsafe {
91             match (*self.descriptor).extra_length {
92                 len if len > 0 => slice::from_raw_parts((*self.descriptor).extra, len as usize),
93                 _ => &[],
94             }
95         }
96     }
97 }
98 
99 impl fmt::Debug for ConfigDescriptor {
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>100     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
101         let mut debug = fmt.debug_struct("ConfigDescriptor");
102 
103         let descriptor: &libusb_config_descriptor = unsafe { &*self.descriptor };
104 
105         debug.field("bLength", &descriptor.bLength);
106         debug.field("bDescriptorType", &descriptor.bDescriptorType);
107         debug.field("wTotalLength", &descriptor.wTotalLength);
108         debug.field("bNumInterfaces", &descriptor.bNumInterfaces);
109         debug.field("bConfigurationValue", &descriptor.bConfigurationValue);
110         debug.field("iConfiguration", &descriptor.iConfiguration);
111         debug.field("bmAttributes", &descriptor.bmAttributes);
112         debug.field("bMaxPower", &descriptor.bMaxPower);
113         debug.field("extra", &self.extra());
114 
115         debug.finish()
116     }
117 }
118 
119 /// Iterator over a configuration's interfaces.
120 pub struct Interfaces<'a> {
121     iter: slice::Iter<'a, libusb_interface>,
122 }
123 
124 impl<'a> Iterator for Interfaces<'a> {
125     type Item = Interface<'a>;
126 
next(&mut self) -> Option<Interface<'a>>127     fn next(&mut self) -> Option<Interface<'a>> {
128         self.iter
129             .next()
130             .map(|interface| unsafe { interface_descriptor::from_libusb(interface) })
131     }
132 
size_hint(&self) -> (usize, Option<usize>)133     fn size_hint(&self) -> (usize, Option<usize>) {
134         self.iter.size_hint()
135     }
136 }
137 
138 #[doc(hidden)]
from_libusb(config: *const libusb_config_descriptor) -> ConfigDescriptor139 pub(crate) unsafe fn from_libusb(config: *const libusb_config_descriptor) -> ConfigDescriptor {
140     ConfigDescriptor { descriptor: config }
141 }
142 
143 #[cfg(test)]
144 mod test {
145     use std::mem::ManuallyDrop;
146 
147     // The Drop trait impl calls libusb_free_config_descriptor(), which would attempt to free
148     // unallocated memory for a stack-allocated config descriptor. Allocating a config descriptor
149     // is not a simple malloc()/free() inside libusb. Mimicking libusb's allocation would be
150     // error-prone, difficult to maintain, and provide little benefit for the tests. It's easier to
151     // use mem::forget() to prevent the Drop trait impl from running. The config descriptor passed
152     // as `$config` should be stack-allocated to prevent memory leaks in the test suite.
153     macro_rules! with_config {
154         ($name:ident : $config:expr => $body:block) => {{
155             let config = $config;
156             let $name = ManuallyDrop::new(unsafe { super::from_libusb(&config) });
157             $body;
158         }};
159     }
160 
161     #[test]
it_has_number()162     fn it_has_number() {
163         with_config!(config: config_descriptor!(bConfigurationValue: 42) => {
164             assert_eq!(42, config.number());
165         });
166     }
167 
168     #[test]
it_has_max_power()169     fn it_has_max_power() {
170         with_config!(config: config_descriptor!(bMaxPower: 21) => {
171             assert_eq!(42, config.max_power());
172         });
173     }
174 
175     #[test]
it_interprets_self_powered_bit_in_attributes()176     fn it_interprets_self_powered_bit_in_attributes() {
177         with_config!(config: config_descriptor!(bmAttributes: 0b0000_0000) => {
178             assert_eq!(false, config.self_powered());
179         });
180 
181         with_config!(config: config_descriptor!(bmAttributes: 0b0100_0000) => {
182             assert_eq!(true, config.self_powered());
183         });
184     }
185 
186     #[test]
it_interprets_remote_wakeup_bit_in_attributes()187     fn it_interprets_remote_wakeup_bit_in_attributes() {
188         with_config!(config: config_descriptor!(bmAttributes: 0b0000_0000) => {
189             assert_eq!(false, config.remote_wakeup());
190         });
191 
192         with_config!(config: config_descriptor!(bmAttributes: 0b0010_0000) => {
193             assert_eq!(true, config.remote_wakeup());
194         });
195     }
196 
197     #[test]
it_has_description_string_index()198     fn it_has_description_string_index() {
199         with_config!(config: config_descriptor!(iConfiguration: 42) => {
200             assert_eq!(Some(42), config.description_string_index());
201         });
202     }
203 
204     #[test]
it_handles_missing_description_string_index()205     fn it_handles_missing_description_string_index() {
206         with_config!(config: config_descriptor!(iConfiguration: 0) => {
207             assert_eq!(None, config.description_string_index());
208         });
209     }
210 
211     #[test]
it_has_num_interfaces()212     fn it_has_num_interfaces() {
213         let interface1 = interface!(interface_descriptor!(bInterfaceNumber: 1));
214         let interface2 = interface!(interface_descriptor!(bInterfaceNumber: 2));
215 
216         with_config!(config: config_descriptor!(interface1, interface2) => {
217             assert_eq!(2, config.num_interfaces());
218         });
219     }
220 
221     #[test]
it_has_interfaces()222     fn it_has_interfaces() {
223         let interface = interface!(interface_descriptor!(bInterfaceNumber: 1));
224 
225         with_config!(config: config_descriptor!(interface) => {
226             let interface_numbers = config.interfaces().map(|interface| {
227                 interface.number()
228             }).collect::<Vec<_>>();
229 
230             assert_eq!(vec![1], interface_numbers);
231         });
232     }
233 
234     // Successful compilation shows that the lifetime of the endpoint descriptor(s) is the same
235     // as the lifetime of the config descriptor.
236     #[test]
it_had_interfaces_with_endpoints()237     fn it_had_interfaces_with_endpoints() {
238         let endpoint1 = endpoint_descriptor!(bEndpointAddress: 0x81);
239         let endpoint2 = endpoint_descriptor!(bEndpointAddress: 0x01);
240         let endpoint3 = endpoint_descriptor!(bEndpointAddress: 0x02);
241         let interface1 = interface!(interface_descriptor!(endpoint1, endpoint2));
242         let interface2 = interface!(interface_descriptor!(endpoint3));
243 
244         with_config!(config: config_descriptor!(interface1, interface2) => {
245             // Exists only to name config's lifetime.
246             fn named_lifetime<'a>(config: &'a super::ConfigDescriptor) {
247                 let addresses: Vec<_> = config.interfaces().flat_map(|intf| intf.descriptors()).flat_map(|desc| desc.endpoint_descriptors()).map(|ep| ep.address()).collect();
248                 assert_eq!(addresses, &[0x81, 0x01, 0x02]);
249                 let desc: crate::InterfaceDescriptor<'a> = config.interfaces().flat_map(|intf| intf.descriptors()).next().expect("There's one interface");
250                 let _: crate::EndpointDescriptor<'a> = desc.endpoint_descriptors().next().expect("There's one endpoint");
251             }
252             named_lifetime(&*config);
253         })
254     }
255 }
256