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, 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
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(
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        // The global context sometimes fails to detect new devices which causes some
40        // really puzzling errors. Here we create a new context for every scan.
41        // Although less efficient, this works around most of the issues with hotplug.
42        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        // We expect to find exactly one matching device. If that happens, the
128        // deferred log messages are unimportant. Otherwise, one of the messages
129        // may yield some insight into what went wrong, so they should be logged
130        // at a higher priority.
131        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        // Unfortunately, rusb simply wraps around libusb which does not
250        // give access to the raw configuration descriptor so we must
251        // get it directly from the device.
252        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,   // Standard, device, IN
257                6,      // GET_DESCRIPTOR
258                1 << 8, // DEVICE
259                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,                       // Standard, device, IN
284                    6,                          // GET_DESCRIPTOR
285                    2 << 8 | config_idx as u16, // CONFIGURATION
286                    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        // We do not try to read the serial number of the parent because hubs generally do not have
325        // unique serial numbers.
326        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    /// Gets the usb serial number of the device.
346    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        // Find the configuration matching the currently active one.
392        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
470// Structure representing a USB hub. The device needs to have sufficient permission
471// to be opened.
472pub struct UsbHub {
473    handle: Box<dyn UsbDevice>,
474}
475
476// USB hub operation.
477pub enum UsbHubOp {
478    // Power-off a specific port.
479    PowerOff,
480    // Power-on a specific port.
481    PowerOn,
482    // Suspend a specific port.
483    Suspend,
484    // Suspend a specific port.
485    Resume,
486    // Reset a specific port.
487    Reset,
488}
489
490const PORT_SUSPEND: u16 = 2;
491const PORT_RESET: u16 = 4;
492const PORT_POWER: u16 = 8;
493
494impl UsbHub {
495    // Construct a hub from the parent of a device.
496    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    // Construct a hub from a device.
514    pub fn from_device(dev: Box<dyn UsbDevice>) -> Result<UsbHub> {
515        // Make sure the device is a hub.
516        let dev_desc = dev.device_descriptor().descriptor()?;
517        // Assume that if the device has the HUB class then Linux will already enforce
518        // that it follows the specification.
519        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    // Report the status of a port (only returns the port status, not the port change).
531    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    // Perform an operation.
548    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        // Expected port status after the operation.
565        let port_status_mask = 1u16 << feature_index;
566        let port_status_after = if set_feature { port_status_mask } else { 0u16 };
567
568        // Perform operation.
569        let _ = self.handle.write_control_timeout(
570            req_type,
571            req,
572            feature_index,
573            port as u16,
574            &[],
575            timeout,
576        )?;
577        // Wait until port has changed status.
578        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}