opentitanlib/util/
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::transport::TransportError;
10
11/// The `UsbBackend` provides low-level USB access to debugging devices.
12pub struct UsbBackend {
13    device: rusb::Device<rusb::Context>,
14    handle: rusb::DeviceHandle<rusb::Context>,
15    serial_number: String,
16    timeout: Duration,
17}
18
19impl UsbBackend {
20    /// Scan the USB bus for a device matching VID/PID, and optionally also matching a serial
21    /// number.
22    pub fn scan(
23        usb_vid_pid: Option<(u16, u16)>,
24        usb_protocol: Option<(u8, u8, u8)>,
25        usb_serial: Option<&str>,
26    ) -> Result<Vec<(rusb::Device<rusb::Context>, String)>> {
27        let mut devices = Vec::new();
28        let mut deferred_log_messages = Vec::new();
29        for device in rusb::Context::new()?.devices().context("USB error")?.iter() {
30            let descriptor = match device.device_descriptor() {
31                Ok(desc) => desc,
32                Err(e) => {
33                    deferred_log_messages.push(format!(
34                        "Could not read device descriptor for device at bus={} address={}: {}",
35                        device.bus_number(),
36                        device.address(),
37                        e,
38                    ));
39                    continue;
40                }
41            };
42
43            if let Some((vid, pid)) = usb_vid_pid {
44                if descriptor.vendor_id() != vid {
45                    continue;
46                }
47                if descriptor.product_id() != pid {
48                    continue;
49                }
50            }
51            if let Some((class, subclass, protocol)) = usb_protocol {
52                let config = match device.active_config_descriptor() {
53                    Ok(desc) => desc,
54                    Err(e) => {
55                        deferred_log_messages.push(format!(
56                            "Could not read config descriptor for device at bus={} address={}: {}",
57                            device.bus_number(),
58                            device.address(),
59                            e,
60                        ));
61                        continue;
62                    }
63                };
64                let mut found = false;
65                for intf in config.interfaces() {
66                    for desc in intf.descriptors() {
67                        if desc.class_code() == class
68                            && desc.sub_class_code() == subclass
69                            && desc.protocol_code() == protocol
70                        {
71                            found = true;
72                        }
73                    }
74                }
75                if !found {
76                    continue;
77                }
78            }
79            let handle = match device.open() {
80                Ok(handle) => handle,
81                Err(e) => {
82                    deferred_log_messages.push(format!(
83                        "Could not open device at bus={} address={}: {}",
84                        device.bus_number(),
85                        device.address(),
86                        e,
87                    ));
88                    continue;
89                }
90            };
91
92            let serial_number = match handle.read_serial_number_string_ascii(&descriptor) {
93                Ok(sn) => sn,
94                Err(e) => {
95                    deferred_log_messages.push(format!(
96                        "Could not read serial number from device at bus={} address={}: {}",
97                        device.bus_number(),
98                        device.address(),
99                        e,
100                    ));
101                    continue;
102                }
103            };
104            if let Some(sn) = &usb_serial
105                && &serial_number != sn
106            {
107                continue;
108            }
109            devices.push((device, serial_number));
110        }
111
112        // We expect to find exactly one matching device. If that happens, the
113        // deferred log messages are unimportant. Otherwise, one of the messages
114        // may yield some insight into what went wrong, so they should be logged
115        // at a higher priority.
116        let severity = match devices.len() {
117            1 => log::Level::Info,
118            _ => log::Level::Error,
119        };
120        for s in deferred_log_messages {
121            log::log!(severity, "{}", s);
122        }
123        Ok(devices)
124    }
125
126    /// Create a new UsbBackend.
127    pub fn new(usb_vid: u16, usb_pid: u16, usb_serial: Option<&str>) -> Result<Self> {
128        let mut devices = UsbBackend::scan(Some((usb_vid, usb_pid)), None, usb_serial)?;
129        ensure!(!devices.is_empty(), TransportError::NoDevice);
130        ensure!(devices.len() == 1, TransportError::MultipleDevices);
131
132        let (device, serial_number) = devices.remove(0);
133        Ok(UsbBackend {
134            handle: device.open().context("USB open error")?,
135            device,
136            serial_number,
137            timeout: Duration::from_millis(500),
138        })
139    }
140
141    pub fn from_interface(
142        class: u8,
143        subclass: u8,
144        protocol: u8,
145        usb_serial: Option<&str>,
146    ) -> Result<Self> {
147        Self::from_interface_with_timeout(class, subclass, protocol, usb_serial, Duration::ZERO)
148    }
149
150    pub fn from_interface_with_timeout(
151        class: u8,
152        subclass: u8,
153        protocol: u8,
154        usb_serial: Option<&str>,
155        timeout: Duration,
156    ) -> Result<Self> {
157        let deadline = Instant::now() + timeout;
158        loop {
159            let mut devices =
160                UsbBackend::scan(None, Some((class, subclass, protocol)), usb_serial)?;
161            if devices.is_empty() {
162                if Instant::now() < deadline {
163                    std::thread::sleep(Duration::from_millis(100));
164                    continue;
165                } else {
166                    return Err(TransportError::NoDevice.into());
167                }
168            }
169            ensure!(devices.len() == 1, TransportError::MultipleDevices);
170
171            let (device, serial_number) = devices.remove(0);
172            return Ok(UsbBackend {
173                handle: device.open().context("USB open error")?,
174                device,
175                serial_number,
176                timeout: Duration::from_millis(500),
177            });
178        }
179    }
180
181    pub fn handle(&self) -> &rusb::DeviceHandle<rusb::Context> {
182        &self.handle
183    }
184
185    pub fn get_vendor_id(&self) -> u16 {
186        self.device.device_descriptor().unwrap().vendor_id()
187    }
188
189    pub fn get_product_id(&self) -> u16 {
190        self.device.device_descriptor().unwrap().product_id()
191    }
192
193    /// Gets the usb serial number of the device.
194    pub fn get_serial_number(&self) -> &str {
195        self.serial_number.as_str()
196    }
197
198    pub fn set_active_configuration(&self, config: u8) -> Result<()> {
199        self.handle
200            .set_active_configuration(config)
201            .context("USB error")
202    }
203
204    pub fn claim_interface(&self, iface: u8) -> Result<()> {
205        self.handle.claim_interface(iface).context("USB error")
206    }
207
208    pub fn release_interface(&self, iface: u8) -> Result<()> {
209        self.handle.release_interface(iface).context("USB error")
210    }
211
212    pub fn set_alternate_setting(&self, iface: u8, setting: u8) -> Result<()> {
213        self.handle
214            .set_alternate_setting(iface, setting)
215            .context("USB error")
216    }
217
218    pub fn kernel_driver_active(&self, iface: u8) -> Result<bool> {
219        self.handle.kernel_driver_active(iface).context("USB error")
220    }
221
222    pub fn detach_kernel_driver(&self, iface: u8) -> Result<()> {
223        self.handle.detach_kernel_driver(iface).context("USB error")
224    }
225
226    pub fn attach_kernel_driver(&self, iface: u8) -> Result<()> {
227        self.handle.attach_kernel_driver(iface).context("USB error")
228    }
229
230    //
231    // Enumerating interfaces of the USB device.  The methods below leak rusb data structures,
232    // and may have to be refactored, when we convert UsbDevice into a trait, and want to
233    // support mocked implementations.
234    //
235
236    pub fn active_config_descriptor(&self) -> Result<rusb::ConfigDescriptor> {
237        self.device.active_config_descriptor().context("USB error")
238    }
239
240    pub fn bus_number(&self) -> u8 {
241        self.device.bus_number()
242    }
243
244    pub fn port_numbers(&self) -> Result<Vec<u8>> {
245        self.device.port_numbers().context("USB error")
246    }
247
248    pub fn read_string_descriptor_ascii(&self, idx: u8) -> Result<String> {
249        self.handle
250            .read_string_descriptor_ascii(idx)
251            .context("USB error")
252    }
253
254    pub fn reset(&self) -> Result<()> {
255        self.handle.reset().context("USB Error")
256    }
257
258    //
259    // Sending and receiving data, the below methods provide a nice interface.
260    //
261
262    /// Issue a USB control request with optional host-to-device data.
263    pub fn write_control(
264        &self,
265        request_type: u8,
266        request: u8,
267        value: u16,
268        index: u16,
269        buf: &[u8],
270    ) -> Result<usize> {
271        self.handle
272            .write_control(request_type, request, value, index, buf, self.timeout)
273            .context("USB error")
274    }
275
276    /// Issue a USB control request with optional device-to-host data.
277    pub fn read_control(
278        &self,
279        request_type: u8,
280        request: u8,
281        value: u16,
282        index: u16,
283        buf: &mut [u8],
284    ) -> Result<usize> {
285        self.handle
286            .read_control(request_type, request, value, index, buf, self.timeout)
287            .context("USB error")
288    }
289
290    /// Read bulk data bytes to given USB endpoint.
291    pub fn read_bulk(&self, endpoint: u8, data: &mut [u8]) -> Result<usize> {
292        let len = self
293            .handle
294            .read_bulk(endpoint, data, self.timeout)
295            .context("USB error")?;
296        Ok(len)
297    }
298
299    /// Read bulk data bytes to given USB endpoint.
300    pub fn read_bulk_timeout(
301        &self,
302        endpoint: u8,
303        data: &mut [u8],
304        timeout: Duration,
305    ) -> Result<usize> {
306        let len = self
307            .handle
308            .read_bulk(endpoint, data, timeout)
309            .context("USB error")?;
310        Ok(len)
311    }
312
313    /// Write bulk data bytes to given USB endpoint.
314    pub fn write_bulk(&self, endpoint: u8, data: &[u8]) -> Result<usize> {
315        let len = self
316            .handle
317            .write_bulk(endpoint, data, self.timeout)
318            .context("USB error")?;
319        Ok(len)
320    }
321}