1use anyhow::{Context, Result, ensure};
6use rusb::{self, UsbContext};
7use std::time::{Duration, Instant};
8
9use crate::io::usb::{UsbContext as OtUsbContext, UsbDevice, desc};
10use crate::transport::TransportError;
11
12pub struct RusbDevice {
14 handle: rusb::DeviceHandle<rusb::Context>,
15 serial_number: Option<String>,
16 timeout: Duration,
17 device_desc: Vec<u8>,
18 configurations: Vec<Vec<u8>>,
19}
20
21#[derive(Default)]
23pub struct RusbContext {}
24
25impl RusbContext {
26 pub fn new() -> Self {
27 RusbContext::default()
28 }
29
30 fn scan(
33 usb_vid_pid: Option<(u16, u16)>,
34 usb_protocol: Option<(u8, u8, u8)>,
35 usb_serial: Option<&str>,
36 ) -> Result<Vec<(rusb::Device<rusb::Context>, Option<String>)>> {
37 let mut devices = Vec::new();
38 let mut deferred_log_messages = Vec::new();
39 for device in rusb::Context::new()?.devices().context("USB error")?.iter() {
43 let descriptor = match device.device_descriptor() {
44 Ok(desc) => desc,
45 Err(e) => {
46 deferred_log_messages.push(format!(
47 "Could not read device descriptor for device at bus={} address={}: {}",
48 device.bus_number(),
49 device.address(),
50 e,
51 ));
52 continue;
53 }
54 };
55
56 if let Some((vid, pid)) = usb_vid_pid {
57 if descriptor.vendor_id() != vid {
58 continue;
59 }
60 if descriptor.product_id() != pid {
61 continue;
62 }
63 }
64 if let Some((class, subclass, protocol)) = usb_protocol {
65 let config = match device.active_config_descriptor() {
66 Ok(desc) => desc,
67 Err(e) => {
68 deferred_log_messages.push(format!(
69 "Could not read config descriptor for device at bus={} address={}: {}",
70 device.bus_number(),
71 device.address(),
72 e,
73 ));
74 continue;
75 }
76 };
77 let mut found = false;
78 for intf in config.interfaces() {
79 for desc in intf.descriptors() {
80 if desc.class_code() == class
81 && desc.sub_class_code() == subclass
82 && desc.protocol_code() == protocol
83 {
84 found = true;
85 }
86 }
87 }
88 if !found {
89 continue;
90 }
91 }
92 let handle = match device.open() {
93 Ok(handle) => handle,
94 Err(e) => {
95 deferred_log_messages.push(format!(
96 "Could not open device at bus={} address={}: {}",
97 device.bus_number(),
98 device.address(),
99 e,
100 ));
101 continue;
102 }
103 };
104
105 let serial_number = if descriptor.serial_number_string_index().is_some() {
106 match handle.read_serial_number_string_ascii(&descriptor) {
107 Ok(sn) => Some(sn),
108 Err(e) => {
109 deferred_log_messages.push(format!(
110 "Could not read serial number from device at bus={} address={}: {}",
111 device.bus_number(),
112 device.address(),
113 e,
114 ));
115 continue;
116 }
117 }
118 } else {
119 None
120 };
121 if usb_serial.is_some() && serial_number.as_deref() != usb_serial {
122 continue;
123 }
124 devices.push((device, serial_number));
125 }
126
127 let severity = match devices.len() {
132 1 => log::Level::Info,
133 _ => log::Level::Error,
134 };
135 for s in deferred_log_messages {
136 log::log!(severity, "{}", s);
137 }
138 Ok(devices)
139 }
140}
141
142impl OtUsbContext for RusbContext {
143 fn device_by_id_with_timeout(
144 &self,
145 usb_vid: u16,
146 usb_pid: u16,
147 usb_serial: Option<&str>,
148 timeout: Duration,
149 ) -> Result<Box<dyn UsbDevice>> {
150 let deadline = Instant::now() + timeout;
151 let serial_str = if let Some(s) = usb_serial {
152 format!(" (serial={})", s)
153 } else {
154 String::new()
155 };
156 loop {
157 let mut devices = RusbContext::scan(Some((usb_vid, usb_pid)), None, usb_serial)?;
158 if devices.is_empty() {
159 if Instant::now() < deadline {
160 std::thread::sleep(Duration::from_millis(100));
161 continue;
162 } else {
163 return Err(TransportError::NoDevice(format!(
164 "vid:pid=0x{:04x}:0x{:04x}{}",
165 usb_vid, usb_pid, serial_str
166 ))
167 .into());
168 }
169 }
170 if devices.len() > 1 {
171 return Err(TransportError::MultipleDevices(
172 format!("{:?}", devices),
173 format!("vid:pid=0x{:04x}:0x{:04x}{}", usb_vid, usb_pid, serial_str),
174 )
175 .into());
176 }
177
178 let (device, serial_number) = devices.remove(0);
179 return Ok(Box::new(RusbDevice::new(
180 device
181 .open()
182 .with_context(|| format!("Cannot open device {device:?}"))?,
183 serial_number,
184 Duration::from_millis(500),
185 )?));
186 }
187 }
188
189 fn device_by_interface_with_timeout(
190 &self,
191 class: u8,
192 subclass: u8,
193 protocol: u8,
194 usb_serial: Option<&str>,
195 timeout: Duration,
196 ) -> Result<Box<dyn UsbDevice>> {
197 let deadline = Instant::now() + timeout;
198 let serial_str = if let Some(s) = usb_serial {
199 format!(" (serial={})", s)
200 } else {
201 String::new()
202 };
203 loop {
204 let mut devices =
205 RusbContext::scan(None, Some((class, subclass, protocol)), usb_serial)?;
206 if devices.is_empty() {
207 if Instant::now() < deadline {
208 std::thread::sleep(Duration::from_millis(100));
209 continue;
210 } else {
211 return Err(TransportError::NoDevice(format!(
212 "class:subclass:protocol=0x{:02x}:0x{:02x}:0x{:02x}{}",
213 class, subclass, protocol, serial_str
214 ))
215 .into());
216 }
217 }
218 if devices.len() > 1 {
219 return Err(TransportError::MultipleDevices(
220 format!("{:?}", devices),
221 format!(
222 "class:subclass:protocol=0x{:02x}:0x{:02x}:0x{:02x}{}",
223 class, subclass, protocol, serial_str
224 ),
225 )
226 .into());
227 }
228
229 let (device, serial_number) = devices.remove(0);
230 return Ok(Box::new(RusbDevice::new(
231 device
232 .open()
233 .with_context(|| format!("Cannot open device {device:?}"))?,
234 serial_number,
235 Duration::from_millis(500),
236 )?));
237 }
238 }
239}
240
241impl RusbDevice {
242 pub fn new(
243 handle: rusb::DeviceHandle<rusb::Context>,
244 serial_number: Option<String>,
245 timeout: Duration,
246 ) -> Result<Self> {
247 let mut configurations = Vec::new();
248
249 let dev_desc_size = handle.device().device_descriptor()?.length() as usize;
253 let mut device_desc = vec![0u8; dev_desc_size];
254 let size = handle
255 .read_control(
256 0x80, 6, 1 << 8, 0,
260 &mut device_desc,
261 timeout,
262 )
263 .context("could not retrieve device descriptor")?;
264 ensure!(
265 size == dev_desc_size,
266 "Device did not return the full device descriptor"
267 );
268
269 let nr_config = handle
270 .device()
271 .device_descriptor()
272 .context("could not retrieve device descriptor")?
273 .num_configurations();
274 for config_idx in 0..nr_config {
275 let tot_len = handle
276 .device()
277 .config_descriptor(config_idx)
278 .context("could not retrieve config descriptor")?
279 .total_length() as usize;
280 let mut desc = vec![0u8; tot_len];
281 let size = handle
282 .read_control(
283 0x80, 6, 2 << 8 | config_idx as u16, 0,
287 &mut desc,
288 timeout,
289 )
290 .context("could not retrieve config descriptor")?;
291 ensure!(
292 size == tot_len,
293 "Device did not return the full configuration descriptor"
294 );
295 configurations.push(desc)
296 }
297
298 Ok(RusbDevice {
299 handle,
300 serial_number,
301 timeout,
302 device_desc,
303 configurations,
304 })
305 }
306}
307
308impl UsbDevice for RusbDevice {
309 fn get_timeout(&self) -> Duration {
310 self.timeout
311 }
312
313 fn get_parent(&self) -> Result<Box<dyn UsbDevice>> {
314 let device = self
315 .handle
316 .device()
317 .get_parent()
318 .context("Unable to get parent USB device")?;
319 let handle = device.open().context(format!(
320 "Could not open device at bus={} address={}",
321 device.bus_number(),
322 device.address(),
323 ))?;
324 Ok(Box::new(RusbDevice::new(handle, None, self.get_timeout())?))
327 }
328
329 fn get_vendor_id(&self) -> u16 {
330 self.handle
331 .device()
332 .device_descriptor()
333 .unwrap()
334 .vendor_id()
335 }
336
337 fn get_product_id(&self) -> u16 {
338 self.handle
339 .device()
340 .device_descriptor()
341 .unwrap()
342 .product_id()
343 }
344
345 fn get_serial_number(&self) -> Option<&str> {
347 self.serial_number.as_deref()
348 }
349
350 fn set_active_configuration(&self, config: u8) -> Result<()> {
351 self.handle
352 .set_active_configuration(config)
353 .context("USB error")
354 }
355
356 fn claim_interface(&self, iface: u8) -> Result<()> {
357 self.handle.claim_interface(iface).context("USB error")
358 }
359
360 fn release_interface(&self, iface: u8) -> Result<()> {
361 self.handle.release_interface(iface).context("USB error")
362 }
363
364 fn set_alternate_setting(&self, iface: u8, setting: u8) -> Result<()> {
365 self.handle
366 .set_alternate_setting(iface, setting)
367 .context("USB error")
368 }
369
370 fn kernel_driver_active(&self, iface: u8) -> Result<bool> {
371 self.handle.kernel_driver_active(iface).context("USB error")
372 }
373
374 fn detach_kernel_driver(&self, iface: u8) -> Result<()> {
375 self.handle.detach_kernel_driver(iface).context("USB error")
376 }
377
378 fn attach_kernel_driver(&self, iface: u8) -> Result<()> {
379 self.handle.attach_kernel_driver(iface).context("USB error")
380 }
381
382 fn device_descriptor(&self) -> desc::Device<'_> {
383 desc::Device::new(&self.device_desc)
384 }
385
386 fn active_configuration(&self) -> Result<desc::Configuration<'_>> {
387 let active_cfg_val = self
388 .handle
389 .active_configuration()
390 .context("Cannot retrieve active configuration value")?;
391 for cfg in self.configurations.iter() {
393 let cfg = desc::Configuration::new(cfg);
394 if let Ok(desc) = cfg.descriptor()
395 && desc.config_val == active_cfg_val
396 {
397 return Ok(cfg);
398 }
399 }
400 anyhow::bail!("No configuration corresponds to the configuration value {active_cfg_val:?}")
401 }
402
403 fn bus_number(&self) -> u8 {
404 self.handle.device().bus_number()
405 }
406
407 fn address(&self) -> u8 {
408 self.handle.device().address()
409 }
410
411 fn port_numbers(&self) -> Result<Vec<u8>> {
412 self.handle.device().port_numbers().context("USB error")
413 }
414
415 fn read_string_descriptor_ascii(&self, idx: u8) -> Result<String> {
416 self.handle
417 .read_string_descriptor_ascii(idx)
418 .context("USB error")
419 }
420
421 fn reset(&self) -> Result<()> {
422 self.handle.reset().context("USB Error")
423 }
424
425 fn write_control_timeout(
426 &self,
427 request_type: u8,
428 request: u8,
429 value: u16,
430 index: u16,
431 buf: &[u8],
432 timeout: Duration,
433 ) -> Result<usize> {
434 self.handle
435 .write_control(request_type, request, value, index, buf, timeout)
436 .context("USB error")
437 }
438
439 fn read_control_timeout(
440 &self,
441 request_type: u8,
442 request: u8,
443 value: u16,
444 index: u16,
445 buf: &mut [u8],
446 timeout: Duration,
447 ) -> Result<usize> {
448 self.handle
449 .read_control(request_type, request, value, index, buf, timeout)
450 .context("USB error")
451 }
452
453 fn read_bulk_timeout(&self, endpoint: u8, data: &mut [u8], timeout: Duration) -> Result<usize> {
454 let len = self
455 .handle
456 .read_bulk(endpoint, data, timeout)
457 .context("USB error")?;
458 Ok(len)
459 }
460
461 fn write_bulk_timeout(&self, endpoint: u8, data: &[u8], timeout: Duration) -> Result<usize> {
462 let len = self
463 .handle
464 .write_bulk(endpoint, data, timeout)
465 .context("USB error")?;
466 Ok(len)
467 }
468}
469
470pub struct UsbHub {
473 handle: Box<dyn UsbDevice>,
474}
475
476pub enum UsbHubOp {
478 PowerOff,
480 PowerOn,
482 Suspend,
484 Resume,
486 Reset,
488}
489
490const PORT_SUSPEND: u16 = 2;
491const PORT_RESET: u16 = 4;
492const PORT_POWER: u16 = 8;
493
494impl UsbHub {
495 pub fn from_parent_device(dev: &dyn UsbDevice) -> Result<UsbHub> {
497 let handle = dev.get_parent().with_context(|| {
498 format!(
499 "Cannot access USB parent hub of device on bus {bus}, address {addr}\n\
500 If this test requires access to the HUB, you need to make sure that \
501 the program has sufficient permissions to access the hub\n\
502 See sw/host/tests/chip/usb/README.md for more information\n\
503 The following command may fix the issue:\n\
504 sudo chmod 0666 /dev/bus/usb/{bus:03}/ADDR\n\
505 where ADDR is the address of the hub",
506 bus = dev.bus_number(),
507 addr = dev.address(),
508 )
509 })?;
510 UsbHub::from_device(handle)
511 }
512
513 pub fn from_device(dev: Box<dyn UsbDevice>) -> Result<UsbHub> {
515 let dev_desc = dev.device_descriptor().descriptor()?;
517 ensure!(
520 dev_desc.class == rusb::constants::LIBUSB_CLASS_HUB,
521 "device is not a hub"
522 );
523 Ok(UsbHub { handle: dev })
524 }
525
526 pub fn device(&self) -> &dyn UsbDevice {
527 &*self.handle
528 }
529
530 fn port_status(&self, port: u8, timeout: Duration) -> Result<u16> {
532 let req_type = rusb::constants::LIBUSB_RECIPIENT_OTHER
533 | rusb::constants::LIBUSB_REQUEST_TYPE_CLASS
534 | rusb::constants::LIBUSB_ENDPOINT_IN;
535 let mut status = [0u8; 4];
536 let _ = self.handle.read_control_timeout(
537 req_type,
538 rusb::constants::LIBUSB_REQUEST_GET_STATUS,
539 0,
540 port as u16,
541 &mut status,
542 timeout,
543 )?;
544 Ok(status[0] as u16 | (status[1] as u16) << 8)
545 }
546
547 pub fn op(&self, op: UsbHubOp, port: u8, timeout: Duration, check_status: bool) -> Result<()> {
549 let (feature_index, set_feature, human_op) = match op {
550 UsbHubOp::Suspend => (PORT_SUSPEND, true, "suspend"),
551 UsbHubOp::Resume => (PORT_SUSPEND, false, "resume"),
552 UsbHubOp::Reset => (PORT_RESET, true, "reset"),
553 UsbHubOp::PowerOn => (PORT_POWER, true, "power on"),
554 UsbHubOp::PowerOff => (PORT_POWER, false, "power off"),
555 };
556 let req = if set_feature {
557 rusb::constants::LIBUSB_REQUEST_SET_FEATURE
558 } else {
559 rusb::constants::LIBUSB_REQUEST_CLEAR_FEATURE
560 };
561 let req_type = rusb::constants::LIBUSB_RECIPIENT_OTHER
562 | rusb::constants::LIBUSB_REQUEST_TYPE_CLASS
563 | rusb::constants::LIBUSB_ENDPOINT_OUT;
564 let port_status_mask = 1u16 << feature_index;
566 let port_status_after = if set_feature { port_status_mask } else { 0u16 };
567
568 let _ = self.handle.write_control_timeout(
570 req_type,
571 req,
572 feature_index,
573 port as u16,
574 &[],
575 timeout,
576 )?;
577 if !check_status {
579 return Ok(());
580 }
581 let start = Instant::now();
582 loop {
583 let port_status = self.port_status(port, timeout)?;
584 if port_status & port_status_mask == port_status_after {
585 break;
586 }
587 if start.elapsed() >= timeout {
588 anyhow::bail!(
589 "Trying to {} port {} but port did not change status (last status was {:x})",
590 human_op,
591 port,
592 port_status
593 );
594 }
595 }
596 log::info!("{} performed in {:#?}", human_op, start.elapsed());
597
598 Ok(())
599 }
600}