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