opentitanlib/transport/common/
usb.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use 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
12/// Represents a device provided by the `rusb` crate.
13pub 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/// Represents a backend using the `rusb` crate.
22#[derive(Default)]
23pub struct RusbContext {}
24
25impl RusbContext {
26    pub fn new() -> Self {
27        RusbContext::default()
28    }
29
30    /// Scan the USB bus for a device matching VID/PID, and optionally also matching a serial
31    /// number.
32    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        // The global context sometimes fails to detect new devices which causes some
41        // really puzzling errors. Here we create a new context for every scan.
42        // Although less efficient, this works around most of the issues with hotplug.
43        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        // We expect to find exactly one matching device. If that happens, the
131        // deferred log messages are unimportant. Otherwise, one of the messages
132        // may yield some insight into what went wrong, so they should be logged
133        // at a higher priority.
134        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
145/// Default timeout of USB operations.
146const 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        // Unfortunately, rusb simply wraps around libusb which does not
180        // give access to the raw configuration descriptor so we must
181        // get it directly from the device.
182        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,   // Standard, device, IN
187                6,      // GET_DESCRIPTOR
188                1 << 8, // DEVICE
189                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,                       // Standard, device, IN
214                    6,                          // GET_DESCRIPTOR
215                    2 << 8 | config_idx as u16, // CONFIGURATION
216                    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        // We do not try to read the serial number of the parent because hubs generally do not have
255        // unique serial numbers.
256        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    /// Gets the usb serial number of the device.
276    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        // Find the configuration matching the currently active one.
322        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
400// Structure representing a USB hub. The device needs to have sufficient permission
401// to be opened.
402pub struct UsbHub {
403    handle: Box<dyn UsbDevice>,
404}
405
406// USB hub operation.
407#[derive(Debug, Copy, Clone)]
408pub enum UsbHubOp {
409    // Power-off a specific port.
410    PowerOff,
411    // Power-on a specific port.
412    PowerOn,
413    // Suspend a specific port.
414    Suspend,
415    // Suspend a specific port.
416    Resume,
417    // Reset a specific port.
418    Reset,
419}
420
421const PORT_SUSPEND: u16 = 2;
422const PORT_RESET: u16 = 4;
423const PORT_POWER: u16 = 8;
424
425impl UsbHub {
426    // Construct a hub from the parent of a device.
427    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    // Construct a hub from a device.
445    pub fn from_device(dev: Box<dyn UsbDevice>) -> Result<UsbHub> {
446        // Make sure the device is a hub.
447        let dev_desc = dev.device_descriptor().descriptor()?;
448        // Assume that if the device has the HUB class then Linux will already enforce
449        // that it follows the specification.
450        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    // Report the status of a port (only returns the port status, not the port change).
462    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        // The device location string is <bus>-<port1>.<port2>...
485        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    // Perform an operation.
508    pub fn op(&self, op: UsbHubOp, port: u8, timeout: Duration, check_status: bool) -> Result<()> {
509        // For power-off/on operations, it is much better to go through the kernel interface
510        // if possible, otherwise the kernel might not notice that the device was connected/disconnected.
511        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        // Expected port status after the operation.
539        let port_status_mask = 1u16 << feature_index;
540        let port_status_after = if set_feature { port_status_mask } else { 0u16 };
541
542        // Perform operation.
543        let _ = self.handle.write_control_timeout(
544            req_type,
545            req,
546            feature_index,
547            port as u16,
548            &[],
549            timeout,
550        )?;
551        // Wait until port has changed status.
552        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}