1use std::cell::RefCell;
2use std::marker;
3use thiserror::Error;
4
5pub type Dfu<C> = dfu_core::sync::DfuSync<DfuLibusb<C>, Error>;
6
7#[derive(Debug, Error)]
8pub enum Error {
9 #[error("Could not find device or an error occurred.")]
10 CouldNotOpenDevice,
11 #[error(transparent)]
12 Dfu(#[from] dfu_core::Error),
13 #[error(transparent)]
14 Io(#[from] std::io::Error),
15 #[error("rusb: {0}")]
16 LibUsb(#[from] rusb::Error),
17 #[error("The device has no languages.")]
18 MissingLanguage,
19 #[error("Could not find interface.")]
20 InvalidInterface,
21 #[error("Could not find alt interface.")]
22 InvalidAlt,
23 #[error("Could not parse functional descriptor: {0}")]
24 FunctionalDescriptor(#[from] dfu_core::functional_descriptor::Error),
25 #[error("No DFU capable device found.")]
26 NoDfuCapableDeviceFound,
27}
28
29pub struct DfuLibusb<C: rusb::UsbContext> {
30 usb: RefCell<rusb::DeviceHandle<C>>,
31 protocol: dfu_core::DfuProtocol<dfu_core::memory_layout::MemoryLayout>,
32 timeout: std::time::Duration,
33 iface: u16,
34 functional_descriptor: dfu_core::functional_descriptor::FunctionalDescriptor,
35 marker: marker::PhantomData<C>,
36}
37
38impl<C: rusb::UsbContext> dfu_core::DfuIo for DfuLibusb<C> {
39 type Read = usize;
40 type Write = usize;
41 type Reset = ();
42 type Error = Error;
43 type MemoryLayout = dfu_core::memory_layout::MemoryLayout;
44
45 #[allow(unused_variables)]
46 fn read_control(
47 &self,
48 request_type: u8,
49 request: u8,
50 value: u16,
51 buffer: &mut [u8],
52 ) -> Result<Self::Read, Self::Error> {
53 let request_type = request_type | rusb::constants::LIBUSB_ENDPOINT_IN;
55 let res = self.usb.borrow().read_control(
56 request_type,
57 request,
58 value,
59 self.iface,
60 buffer,
61 self.timeout,
62 );
63 assert!(
64 !matches!(res, Err(rusb::Error::InvalidParam)),
65 "invalid param: {:08b} {:?}",
66 request_type,
67 res,
68 );
69 Ok(res?)
70 }
71
72 #[allow(unused_variables)]
73 fn write_control(
74 &self,
75 request_type: u8,
76 request: u8,
77 value: u16,
78 buffer: &[u8],
79 ) -> Result<Self::Write, Self::Error> {
80 let res = self.usb.borrow().write_control(
81 request_type,
82 request,
83 value,
84 self.iface,
85 buffer,
86 self.timeout,
87 );
88 assert!(
89 !matches!(res, Err(rusb::Error::InvalidParam)),
90 "invalid param: {:08b}",
91 request_type,
92 );
93 Ok(res?)
94 }
95
96 fn usb_reset(&self) -> Result<Self::Reset, Self::Error> {
97 Ok(self.usb.borrow_mut().reset()?)
98 }
99
100 fn protocol(&self) -> &dfu_core::DfuProtocol<Self::MemoryLayout> {
101 &self.protocol
102 }
103
104 fn functional_descriptor(&self) -> &dfu_core::functional_descriptor::FunctionalDescriptor {
105 &self.functional_descriptor
106 }
107}
108
109impl<C: rusb::UsbContext> DfuLibusb<C> {
110 pub fn open(context: &C, vid: u16, pid: u16, iface: u8, alt: u8) -> Result<Dfu<C>, Error> {
111 let (device, handle) = Self::open_device(context, vid, pid)?;
112 Self::from_usb_device(device, handle, iface, alt)
113 }
114
115 pub fn from_usb_device(
116 device: rusb::Device<C>,
117 mut handle: rusb::DeviceHandle<C>,
118 iface: u8,
119 alt: u8,
120 ) -> Result<Dfu<C>, Error> {
121 let timeout = std::time::Duration::from_secs(3);
122 handle.claim_interface(iface)?;
123 handle.set_alternate_setting(iface, alt)?;
124 let device_descriptor = device.device_descriptor()?;
125 let languages = handle.read_languages(timeout)?;
126 let lang = languages.first().ok_or(Error::MissingLanguage)?;
127
128 for index in 0..device_descriptor.num_configurations() {
129 let config_descriptor = device.config_descriptor(index)?;
130
131 if let Some(functional_descriptor) =
132 Self::find_functional_descriptor(&handle, &config_descriptor, timeout)
133 .transpose()?
134 {
135 let interface = config_descriptor
136 .interfaces()
137 .find(|x| x.number() == iface)
138 .ok_or(Error::InvalidInterface)?;
139 let iface_desc = interface
140 .descriptors()
141 .find(|x| x.setting_number() == alt)
142 .ok_or(Error::InvalidAlt)?;
143
144 let interface_string = handle.read_interface_string(*lang, &iface_desc, timeout)?;
145 let protocol = dfu_core::DfuProtocol::new(
146 &interface_string,
147 functional_descriptor.dfu_version,
148 )?;
149
150 let io = DfuLibusb {
151 usb: RefCell::new(handle),
152 protocol,
153 timeout,
154 iface: iface as u16,
155 functional_descriptor,
156 marker: marker::PhantomData,
157 };
158
159 return Ok(dfu_core::sync::DfuSync::new(io));
160 }
161 }
162
163 Err(Error::NoDfuCapableDeviceFound)
164 }
165
166 fn open_device(
167 context: &C,
168 vid: u16,
169 pid: u16,
170 ) -> Result<(rusb::Device<C>, rusb::DeviceHandle<C>), Error> {
171 for device in context.devices()?.iter() {
172 let device_desc = match device.device_descriptor() {
173 Ok(x) => x,
174 Err(_) => continue,
175 };
176
177 if device_desc.vendor_id() == vid && device_desc.product_id() == pid {
178 let handle = device.open()?;
179 return Ok((device, handle));
180 }
181 }
182
183 Err(Error::CouldNotOpenDevice)
184 }
185
186 fn find_functional_descriptor(
187 handle: &rusb::DeviceHandle<C>,
188 config: &rusb::ConfigDescriptor,
189 timeout: std::time::Duration,
190 ) -> Option<Result<dfu_core::functional_descriptor::FunctionalDescriptor, Error>> {
191 macro_rules! find_func_desc {
192 ($data:expr) => {{
193 if let Some(func_desc) =
194 dfu_core::functional_descriptor::FunctionalDescriptor::from_bytes($data)
195 {
196 return Some(func_desc.map_err(Into::into));
197 }
198 }};
199 }
200
201 find_func_desc!(config.extra());
202
203 for if_desc in config.interfaces().flat_map(|x| x.descriptors()) {
204 find_func_desc!(if_desc.extra());
205 }
206
207 let mut buffer = [0x00; 9];
208 match handle.read_control(
209 rusb::constants::LIBUSB_ENDPOINT_IN,
210 rusb::constants::LIBUSB_REQUEST_GET_DESCRIPTOR,
211 0x2100,
212 0,
213 &mut buffer,
214 timeout,
215 ) {
216 Ok(n) => find_func_desc!(&buffer[..n]),
217 Err(err) => return Some(Err(err.into())),
218 }
219
220 None
221 }
222}