1pub mod desc;
6
7use anyhow::Result;
8use serde::{Deserialize, Serialize};
9use std::time::{Duration, Instant};
10use thiserror::Error;
11
12use crate::impl_serializable_error;
13use crate::transport::TransportError;
14
15#[derive(Debug, Error, Serialize, Deserialize)]
17pub enum UsbError {
18 #[error("Generic error: {0}")]
19 Generic(String),
20}
21impl_serializable_error!(UsbError);
22
23pub trait UsbDevice {
25 fn get_vendor_id(&self) -> u16;
27
28 fn get_product_id(&self) -> u16;
30
31 fn get_serial_number(&self) -> Option<&str>;
33
34 fn get_parent(&self) -> Result<Box<dyn UsbDevice>>;
36
37 fn set_active_configuration(&self, config: u8) -> Result<()>;
39
40 fn claim_interface(&self, iface: u8) -> Result<()>;
42
43 fn release_interface(&self, iface: u8) -> Result<()>;
45
46 fn set_alternate_setting(&self, iface: u8, setting: u8) -> Result<()>;
48
49 fn kernel_driver_active(&self, iface: u8) -> Result<bool>;
51
52 fn detach_kernel_driver(&self, iface: u8) -> Result<()>;
54
55 fn attach_kernel_driver(&self, iface: u8) -> Result<()>;
57
58 fn device_descriptor(&self) -> desc::Device<'_>;
60
61 fn active_configuration(&self) -> Result<desc::Configuration<'_>>;
63
64 fn bus_number(&self) -> u8;
66
67 fn address(&self) -> u8;
69
70 fn port_numbers(&self) -> Result<Vec<u8>>;
72
73 fn read_string_descriptor_ascii(&self, idx: u8) -> Result<String>;
75
76 fn reset(&self) -> Result<()>;
81
82 fn get_timeout(&self) -> Duration;
84
85 fn write_control_timeout(
87 &self,
88 request_type: u8,
89 request: u8,
90 value: u16,
91 index: u16,
92 buf: &[u8],
93 timeout: Duration,
94 ) -> Result<usize>;
95
96 fn write_control(
100 &self,
101 request_type: u8,
102 request: u8,
103 value: u16,
104 index: u16,
105 buf: &[u8],
106 ) -> Result<usize> {
107 self.write_control_timeout(request_type, request, value, index, buf, self.get_timeout())
108 }
109
110 fn read_control_timeout(
112 &self,
113 request_type: u8,
114 request: u8,
115 value: u16,
116 index: u16,
117 buf: &mut [u8],
118 timeout: Duration,
119 ) -> Result<usize>;
120
121 fn read_control(
125 &self,
126 request_type: u8,
127 request: u8,
128 value: u16,
129 index: u16,
130 buf: &mut [u8],
131 ) -> Result<usize> {
132 self.read_control_timeout(request_type, request, value, index, buf, self.get_timeout())
133 }
134
135 fn read_bulk_timeout(&self, endpoint: u8, data: &mut [u8], timeout: Duration) -> Result<usize>;
137
138 fn read_bulk(&self, endpoint: u8, data: &mut [u8]) -> Result<usize> {
142 self.read_bulk_timeout(endpoint, data, self.get_timeout())
143 }
144
145 fn write_bulk_timeout(&self, endpoint: u8, data: &[u8], timeout: Duration) -> Result<usize>;
147
148 fn write_bulk(&self, endpoint: u8, data: &[u8]) -> Result<usize> {
152 self.write_bulk_timeout(endpoint, data, self.get_timeout())
153 }
154
155 fn eq(&self, other: &dyn UsbDevice) -> bool {
157 self.bus_number() == other.bus_number() && self.address() == other.address()
158 }
159}
160
161impl std::fmt::Debug for dyn UsbDevice {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
163 write!(
164 f,
165 "{}-{} [vid={:x},pid={:x},address={},serial={}]",
166 self.bus_number(),
167 self.port_numbers()
168 .map(|ports| ports
169 .iter()
170 .map(|x| x.to_string())
171 .collect::<Vec<_>>()
172 .join("."))
173 .unwrap_or("<ports unavailable>".into()),
174 self.get_vendor_id(),
175 self.get_product_id(),
176 self.address(),
177 self.get_serial_number().unwrap_or("<not available>"),
178 )
179 }
180}
181
182const DEVICE_POLL_INTERVAL_MILLIS: u64 = 100;
184
185fn look_for_device<T: UsbContext + ?Sized>(
186 ctx: &T,
187 usb_vid: Option<u16>,
188 usb_pid: Option<u16>,
189 usb_protocol: Option<(u8, u8, u8)>,
190 usb_serial: Option<&str>,
191 timeout: Duration,
192 search_criterion: &str,
193) -> Result<Box<dyn UsbDevice>> {
194 let deadline = Instant::now() + timeout;
195 let search_criterion = if let Some(s) = usb_serial {
196 format!("{} (serial={})", search_criterion, s)
197 } else {
198 search_criterion.to_string()
199 };
200 loop {
201 let mut devices = ctx.scan(usb_vid, usb_pid, usb_protocol, usb_serial)?;
202 if devices.is_empty() {
203 if Instant::now() < deadline {
204 std::thread::sleep(Duration::from_millis(DEVICE_POLL_INTERVAL_MILLIS));
205 continue;
206 } else {
207 return Err(TransportError::NoDevice(search_criterion).into());
208 }
209 }
210 if devices.len() > 1 {
211 return Err(TransportError::MultipleDevices(
212 format!("{:?}", devices),
213 search_criterion,
214 )
215 .into());
216 }
217
218 return Ok(devices.remove(0));
219 }
220}
221
222pub trait UsbContext {
224 fn scan(
228 &self,
229 usb_vid: Option<u16>,
230 usb_pid: Option<u16>,
231 usb_protocol: Option<(u8, u8, u8)>,
232 usb_serial: Option<&str>,
233 ) -> Result<Vec<Box<dyn UsbDevice>>>;
234
235 fn device_by_id(
239 &self,
240 usb_vid: u16,
241 usb_pid: u16,
242 usb_serial: Option<&str>,
243 ) -> Result<Box<dyn UsbDevice>> {
244 self.device_by_id_with_timeout(usb_vid, usb_pid, usb_serial, Duration::ZERO)
245 }
246
247 fn device_by_id_with_timeout(
251 &self,
252 usb_vid: u16,
253 usb_pid: u16,
254 usb_serial: Option<&str>,
255 timeout: Duration,
256 ) -> Result<Box<dyn UsbDevice>> {
257 look_for_device(
258 self,
259 Some(usb_vid),
260 Some(usb_pid),
261 None,
262 usb_serial,
263 timeout,
264 &format!("vid:pid=0x{:04x}:0x{:04x}", usb_vid, usb_pid),
265 )
266 }
267
268 fn device_by_interface(
272 &self,
273 class: u8,
274 subclass: u8,
275 protocol: u8,
276 usb_serial: Option<&str>,
277 ) -> Result<Box<dyn UsbDevice>> {
278 self.device_by_interface_with_timeout(class, subclass, protocol, usb_serial, Duration::ZERO)
279 }
280
281 fn device_by_interface_with_timeout(
285 &self,
286 class: u8,
287 subclass: u8,
288 protocol: u8,
289 usb_serial: Option<&str>,
290 timeout: Duration,
291 ) -> Result<Box<dyn UsbDevice>> {
292 look_for_device(
293 self,
294 None,
295 None,
296 Some((class, subclass, protocol)),
297 usb_serial,
298 timeout,
299 &format!(
300 "class:subclass:protocol=0x{:02x}:0x{:02x}:0x{:02x}",
301 class, subclass, protocol
302 ),
303 )
304 }
305}