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