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 = match iface_desc.description_string_index() {
154 None => String::new(),
155 Some(_) => handle.read_interface_string(*lang, &iface_desc, timeout)?,
156 };
157 let protocol = dfu_core::DfuProtocol::new(
158 &interface_string,
159 functional_descriptor.dfu_version,
160 )?;
161
162 let io = DfuLibusb {
163 usb: RefCell::new(handle),
164 protocol,
165 timeout,
166 iface: iface as u16,
167 functional_descriptor,
168 marker: marker::PhantomData,
169 };
170
171 return Ok(Dfu::new(io));
172 }
173 }
174
175 Err(Error::NoDfuCapableDeviceFound)
176 }
177
178 fn open_device(
179 context: &C,
180 vid: u16,
181 pid: u16,
182 ) -> Result<(rusb::Device<C>, rusb::DeviceHandle<C>), Error> {
183 for device in context.devices()?.iter() {
184 let device_desc = match device.device_descriptor() {
185 Ok(x) => x,
186 Err(_) => continue,
187 };
188
189 if device_desc.vendor_id() == vid && device_desc.product_id() == pid {
190 let handle = device.open()?;
191 return Ok((device, handle));
192 }
193 }
194
195 Err(Error::CouldNotOpenDevice)
196 }
197
198 fn find_functional_descriptor(
199 handle: &rusb::DeviceHandle<C>,
200 config: &rusb::ConfigDescriptor,
201 timeout: std::time::Duration,
202 ) -> Option<Result<dfu_core::functional_descriptor::FunctionalDescriptor, Error>> {
203 macro_rules! find_func_desc {
204 ($data:expr) => {{
205 if let Some(func_desc) =
206 dfu_core::functional_descriptor::FunctionalDescriptor::from_bytes($data)
207 {
208 return Some(func_desc.map_err(Into::into));
209 }
210 }};
211 }
212
213 find_func_desc!(config.extra());
214
215 for if_desc in config.interfaces().flat_map(|x| x.descriptors()) {
216 find_func_desc!(if_desc.extra());
217 }
218
219 let mut buffer = [0x00; 9];
220 match handle.read_control(
221 rusb::constants::LIBUSB_ENDPOINT_IN,
222 rusb::constants::LIBUSB_REQUEST_GET_DESCRIPTOR,
223 0x2100,
224 0,
225 &mut buffer,
226 timeout,
227 ) {
228 Ok(n) => find_func_desc!(&buffer[..n]),
229 Err(err) => return Some(Err(err.into())),
230 }
231
232 None
233 }
234}