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};
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_devices(
33 usb_vid: Option<u16>,
34 usb_pid: Option<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) = usb_vid
58 && descriptor.vendor_id() != vid
59 {
60 continue;
61 }
62 if let Some(pid) = usb_pid
63 && descriptor.product_id() != pid
64 {
65 continue;
66 }
67 if let Some((class, subclass, protocol)) = usb_protocol {
68 let config = match device.active_config_descriptor() {
69 Ok(desc) => desc,
70 Err(e) => {
71 deferred_log_messages.push(format!(
72 "Could not read config descriptor for device at bus={} address={}: {}",
73 device.bus_number(),
74 device.address(),
75 e,
76 ));
77 continue;
78 }
79 };
80 let mut found = false;
81 for intf in config.interfaces() {
82 for desc in intf.descriptors() {
83 if desc.class_code() == class
84 && desc.sub_class_code() == subclass
85 && desc.protocol_code() == protocol
86 {
87 found = true;
88 }
89 }
90 }
91 if !found {
92 continue;
93 }
94 }
95 let handle = match device.open() {
96 Ok(handle) => handle,
97 Err(e) => {
98 deferred_log_messages.push(format!(
99 "Could not open device at bus={} address={}: {}",
100 device.bus_number(),
101 device.address(),
102 e,
103 ));
104 continue;
105 }
106 };
107
108 let serial_number = if descriptor.serial_number_string_index().is_some() {
109 match handle.read_serial_number_string_ascii(&descriptor) {
110 Ok(sn) => Some(sn),
111 Err(e) => {
112 deferred_log_messages.push(format!(
113 "Could not read serial number from device at bus={} address={}: {}",
114 device.bus_number(),
115 device.address(),
116 e,
117 ));
118 continue;
119 }
120 }
121 } else {
122 None
123 };
124 if usb_serial.is_some() && serial_number.as_deref() != usb_serial {
125 continue;
126 }
127 devices.push((device, serial_number));
128 }
129
130 let severity = match devices.len() {
135 1 => log::Level::Info,
136 _ => log::Level::Error,
137 };
138 for s in deferred_log_messages {
139 log::log!(severity, "{}", s);
140 }
141 Ok(devices)
142 }
143}
144
145const USB_DEVICE_TIMEOUT_MILLIS: u64 = 500;
147
148impl OtUsbContext for RusbContext {
149 fn scan(
150 &self,
151 usb_vid: Option<u16>,
152 usb_pid: Option<u16>,
153 usb_protocol: Option<(u8, u8, u8)>,
154 usb_serial: Option<&str>,
155 ) -> Result<Vec<Box<dyn UsbDevice>>> {
156 RusbContext::scan_devices(usb_vid, usb_pid, usb_protocol, usb_serial)?
157 .into_iter()
158 .map(|(device, serial)| {
159 Ok(Box::new(RusbDevice::new(
160 device
161 .open()
162 .with_context(|| format!("Cannot open device {device:?}"))?,
163 serial,
164 Duration::from_millis(USB_DEVICE_TIMEOUT_MILLIS),
165 )?) as Box<dyn UsbDevice>)
166 })
167 .collect()
168 }
169}
170
171impl RusbDevice {
172 pub fn new(
173 handle: rusb::DeviceHandle<rusb::Context>,
174 serial_number: Option<String>,
175 timeout: Duration,
176 ) -> Result<Self> {
177 let mut configurations = Vec::new();
178
179 let dev_desc_size = handle.device().device_descriptor()?.length() as usize;
183 let mut device_desc = vec![0u8; dev_desc_size];
184 let size = handle
185 .read_control(
186 0x80, 6, 1 << 8, 0,
190 &mut device_desc,
191 timeout,
192 )
193 .context("could not retrieve device descriptor")?;
194 ensure!(
195 size == dev_desc_size,
196 "Device did not return the full device descriptor"
197 );
198
199 let nr_config = handle
200 .device()
201 .device_descriptor()
202 .context("could not retrieve device descriptor")?
203 .num_configurations();
204 for config_idx in 0..nr_config {
205 let tot_len = handle
206 .device()
207 .config_descriptor(config_idx)
208 .context("could not retrieve config descriptor")?
209 .total_length() as usize;
210 let mut desc = vec![0u8; tot_len];
211 let size = handle
212 .read_control(
213 0x80, 6, 2 << 8 | config_idx as u16, 0,
217 &mut desc,
218 timeout,
219 )
220 .context("could not retrieve config descriptor")?;
221 ensure!(
222 size == tot_len,
223 "Device did not return the full configuration descriptor"
224 );
225 configurations.push(desc)
226 }
227
228 Ok(RusbDevice {
229 handle,
230 serial_number,
231 timeout,
232 device_desc,
233 configurations,
234 })
235 }
236}
237
238impl UsbDevice for RusbDevice {
239 fn get_timeout(&self) -> Duration {
240 self.timeout
241 }
242
243 fn get_parent(&self) -> Result<Box<dyn UsbDevice>> {
244 let device = self
245 .handle
246 .device()
247 .get_parent()
248 .context("Unable to get parent USB device")?;
249 let handle = device.open().context(format!(
250 "Could not open device at bus={} address={}",
251 device.bus_number(),
252 device.address(),
253 ))?;
254 Ok(Box::new(RusbDevice::new(handle, None, self.get_timeout())?))
257 }
258
259 fn get_vendor_id(&self) -> u16 {
260 self.handle
261 .device()
262 .device_descriptor()
263 .unwrap()
264 .vendor_id()
265 }
266
267 fn get_product_id(&self) -> u16 {
268 self.handle
269 .device()
270 .device_descriptor()
271 .unwrap()
272 .product_id()
273 }
274
275 fn get_serial_number(&self) -> Option<&str> {
277 self.serial_number.as_deref()
278 }
279
280 fn set_active_configuration(&self, config: u8) -> Result<()> {
281 self.handle
282 .set_active_configuration(config)
283 .context("USB error")
284 }
285
286 fn claim_interface(&self, iface: u8) -> Result<()> {
287 self.handle.claim_interface(iface).context("USB error")
288 }
289
290 fn release_interface(&self, iface: u8) -> Result<()> {
291 self.handle.release_interface(iface).context("USB error")
292 }
293
294 fn set_alternate_setting(&self, iface: u8, setting: u8) -> Result<()> {
295 self.handle
296 .set_alternate_setting(iface, setting)
297 .context("USB error")
298 }
299
300 fn kernel_driver_active(&self, iface: u8) -> Result<bool> {
301 self.handle.kernel_driver_active(iface).context("USB error")
302 }
303
304 fn detach_kernel_driver(&self, iface: u8) -> Result<()> {
305 self.handle.detach_kernel_driver(iface).context("USB error")
306 }
307
308 fn attach_kernel_driver(&self, iface: u8) -> Result<()> {
309 self.handle.attach_kernel_driver(iface).context("USB error")
310 }
311
312 fn device_descriptor(&self) -> desc::Device<'_> {
313 desc::Device::new(&self.device_desc)
314 }
315
316 fn active_configuration(&self) -> Result<desc::Configuration<'_>> {
317 let active_cfg_val = self
318 .handle
319 .active_configuration()
320 .context("Cannot retrieve active configuration value")?;
321 for cfg in self.configurations.iter() {
323 let cfg = desc::Configuration::new(cfg);
324 if let Ok(desc) = cfg.descriptor()
325 && desc.config_val == active_cfg_val
326 {
327 return Ok(cfg);
328 }
329 }
330 anyhow::bail!("No configuration corresponds to the configuration value {active_cfg_val:?}")
331 }
332
333 fn bus_number(&self) -> u8 {
334 self.handle.device().bus_number()
335 }
336
337 fn address(&self) -> u8 {
338 self.handle.device().address()
339 }
340
341 fn port_numbers(&self) -> Result<Vec<u8>> {
342 self.handle.device().port_numbers().context("USB error")
343 }
344
345 fn read_string_descriptor_ascii(&self, idx: u8) -> Result<String> {
346 self.handle
347 .read_string_descriptor_ascii(idx)
348 .context("USB error")
349 }
350
351 fn reset(&self) -> Result<()> {
352 self.handle.reset().context("USB Error")
353 }
354
355 fn write_control_timeout(
356 &self,
357 request_type: u8,
358 request: u8,
359 value: u16,
360 index: u16,
361 buf: &[u8],
362 timeout: Duration,
363 ) -> Result<usize> {
364 self.handle
365 .write_control(request_type, request, value, index, buf, timeout)
366 .context("USB error")
367 }
368
369 fn read_control_timeout(
370 &self,
371 request_type: u8,
372 request: u8,
373 value: u16,
374 index: u16,
375 buf: &mut [u8],
376 timeout: Duration,
377 ) -> Result<usize> {
378 self.handle
379 .read_control(request_type, request, value, index, buf, timeout)
380 .context("USB error")
381 }
382
383 fn read_bulk_timeout(&self, endpoint: u8, data: &mut [u8], timeout: Duration) -> Result<usize> {
384 let len = self
385 .handle
386 .read_bulk(endpoint, data, timeout)
387 .context("USB error")?;
388 Ok(len)
389 }
390
391 fn write_bulk_timeout(&self, endpoint: u8, data: &[u8], timeout: Duration) -> Result<usize> {
392 let len = self
393 .handle
394 .write_bulk(endpoint, data, timeout)
395 .context("USB error")?;
396 Ok(len)
397 }
398}
399
400pub struct UsbHub {
403 handle: Box<dyn UsbDevice>,
404}
405
406#[derive(Debug, Copy, Clone)]
408pub enum UsbHubOp {
409 PowerOff,
411 PowerOn,
413 Suspend,
415 Resume,
417 Reset,
419}
420
421const PORT_SUSPEND: u16 = 2;
422const PORT_RESET: u16 = 4;
423const PORT_POWER: u16 = 8;
424
425impl UsbHub {
426 pub fn from_parent_device(dev: &dyn UsbDevice) -> Result<UsbHub> {
428 let handle = dev.get_parent().with_context(|| {
429 format!(
430 "Cannot access USB parent hub of device on bus {bus}, address {addr}\n\
431 If this test requires access to the HUB, you need to make sure that \
432 the program has sufficient permissions to access the hub\n\
433 See sw/host/tests/chip/usb/README.md for more information\n\
434 The following command may fix the issue:\n\
435 sudo chmod 0666 /dev/bus/usb/{bus:03}/ADDR\n\
436 where ADDR is the address of the hub",
437 bus = dev.bus_number(),
438 addr = dev.address(),
439 )
440 })?;
441 UsbHub::from_device(handle)
442 }
443
444 pub fn from_device(dev: Box<dyn UsbDevice>) -> Result<UsbHub> {
446 let dev_desc = dev.device_descriptor().descriptor()?;
448 ensure!(
451 dev_desc.class == rusb::constants::LIBUSB_CLASS_HUB,
452 "device is not a hub"
453 );
454 Ok(UsbHub { handle: dev })
455 }
456
457 pub fn device(&self) -> &dyn UsbDevice {
458 &*self.handle
459 }
460
461 fn port_status(&self, port: u8, timeout: Duration) -> Result<u16> {
463 let req_type = rusb::constants::LIBUSB_RECIPIENT_OTHER
464 | rusb::constants::LIBUSB_REQUEST_TYPE_CLASS
465 | rusb::constants::LIBUSB_ENDPOINT_IN;
466 let mut status = [0u8; 4];
467 let _ = self.handle.read_control_timeout(
468 req_type,
469 rusb::constants::LIBUSB_REQUEST_GET_STATUS,
470 0,
471 port as u16,
472 &mut status,
473 timeout,
474 )?;
475 Ok(status[0] as u16 | (status[1] as u16) << 8)
476 }
477
478 fn try_sysfs_op(&self, op: UsbHubOp, port: u8) -> Result<()> {
479 let disable_content = match op {
480 UsbHubOp::PowerOn => b"0",
481 UsbHubOp::PowerOff => b"1",
482 _ => bail!("operation not supported by the kernel"),
483 };
484 let hub_ports = self
486 .handle
487 .port_numbers()?
488 .iter()
489 .map(|x| x.to_string())
490 .collect::<Vec<_>>()
491 .join(".");
492 let dev_loc = format!("{}-{}", self.handle.bus_number(), hub_ports);
493 let cfg = self.handle.active_configuration()?.descriptor()?.config_val;
494 let disable_file_path =
495 format!("/sys/bus/usb/devices/{dev_loc}:{cfg}.0/{dev_loc}-port{port}/disable");
496 ensure!(
497 fs::exists(&disable_file_path).unwrap_or(false),
498 "sysfs file {} not found, are you using a very old kernel?",
499 disable_file_path
500 );
501 fs::write(&disable_file_path, disable_content)
502 .with_context(|| format!("Unable to write {}", disable_file_path))?;
503 log::info!("Hub operation {op:?} performed using the sysfs interface");
504 Ok(())
505 }
506
507 pub fn op(&self, op: UsbHubOp, port: u8, timeout: Duration, check_status: bool) -> Result<()> {
509 match self.try_sysfs_op(op, port) {
512 Ok(()) => return Ok(()),
513 Err(err) => {
514 log::error!(
515 "Could not perform hub operation {op:?} using sysfs, falling back to direct hub operations: {err:#}"
516 );
517 log::error!(
518 "This could lead to unexpected behaviours such as the kernel not properly detecting devices on this port, see sw/host/tests/chip/usb/README.md for more details"
519 );
520 }
521 }
522
523 let (feature_index, set_feature) = match op {
524 UsbHubOp::Suspend => (PORT_SUSPEND, true),
525 UsbHubOp::Resume => (PORT_SUSPEND, false),
526 UsbHubOp::Reset => (PORT_RESET, true),
527 UsbHubOp::PowerOn => (PORT_POWER, true),
528 UsbHubOp::PowerOff => (PORT_POWER, false),
529 };
530 let req = if set_feature {
531 rusb::constants::LIBUSB_REQUEST_SET_FEATURE
532 } else {
533 rusb::constants::LIBUSB_REQUEST_CLEAR_FEATURE
534 };
535 let req_type = rusb::constants::LIBUSB_RECIPIENT_OTHER
536 | rusb::constants::LIBUSB_REQUEST_TYPE_CLASS
537 | rusb::constants::LIBUSB_ENDPOINT_OUT;
538 let port_status_mask = 1u16 << feature_index;
540 let port_status_after = if set_feature { port_status_mask } else { 0u16 };
541
542 let _ = self.handle.write_control_timeout(
544 req_type,
545 req,
546 feature_index,
547 port as u16,
548 &[],
549 timeout,
550 )?;
551 if !check_status {
553 return Ok(());
554 }
555 let start = Instant::now();
556 loop {
557 let port_status = self.port_status(port, timeout)?;
558 if port_status & port_status_mask == port_status_after {
559 break;
560 }
561 ensure!(
562 start.elapsed() <= timeout,
563 "Trying to {op:?} port {port} but port did not change status (last status was {port_status:x})",
564 );
565 }
566 log::info!("Hub operation {op:?} performed in {:#?}", start.elapsed());
567
568 Ok(())
569 }
570}