1use anyhow::{Context, Result, ensure};
6use rusb::{self, UsbContext};
7use std::time::{Duration, Instant};
8
9use crate::transport::TransportError;
10
11pub 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 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 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 pub fn new(usb_vid: u16, usb_pid: u16, usb_serial: Option<&str>) -> Result<Self> {
128 let serial_str = if let Some(s) = usb_serial {
129 format!(" (serial={})", s)
130 } else {
131 String::new()
132 };
133 let mut devices = UsbBackend::scan(Some((usb_vid, usb_pid)), None, usb_serial)?;
134 if devices.is_empty() {
135 return Err(TransportError::NoDevice(format!(
136 "vid:pid=0x{:04x}:0x{:04x}{}",
137 usb_vid, usb_pid, serial_str
138 ))
139 .into());
140 }
141 if devices.len() > 1 {
142 return Err(TransportError::MultipleDevices(
143 format!("{:?}", devices),
144 format!("vid:pid=0x{:04x}:0x{:04x}{}", usb_vid, usb_pid, serial_str),
145 )
146 .into());
147 }
148
149 let (device, serial_number) = devices.remove(0);
150 Ok(UsbBackend {
151 handle: device.open().context("USB open error")?,
152 device,
153 serial_number,
154 timeout: Duration::from_millis(500),
155 })
156 }
157
158 pub fn from_interface(
159 class: u8,
160 subclass: u8,
161 protocol: u8,
162 usb_serial: Option<&str>,
163 ) -> Result<Self> {
164 Self::from_interface_with_timeout(class, subclass, protocol, usb_serial, Duration::ZERO)
165 }
166
167 pub fn from_interface_with_timeout(
168 class: u8,
169 subclass: u8,
170 protocol: u8,
171 usb_serial: Option<&str>,
172 timeout: Duration,
173 ) -> Result<Self> {
174 let deadline = Instant::now() + timeout;
175 let serial_str = if let Some(s) = usb_serial {
176 format!(" (serial={})", s)
177 } else {
178 String::new()
179 };
180 loop {
181 let mut devices =
182 UsbBackend::scan(None, Some((class, subclass, protocol)), usb_serial)?;
183 if devices.is_empty() {
184 if Instant::now() < deadline {
185 std::thread::sleep(Duration::from_millis(100));
186 continue;
187 } else {
188 return Err(TransportError::NoDevice(format!(
189 "class:subclass:protocol=0x{:02x}:0x{:02x}:0x{:02x}{}",
190 class, subclass, protocol, serial_str
191 ))
192 .into());
193 }
194 }
195 if devices.len() > 1 {
196 return Err(TransportError::MultipleDevices(
197 format!("{:?}", devices),
198 format!(
199 "class:subclass:protocol=0x{:02x}:0x{:02x}:0x{:02x}{}",
200 class, subclass, protocol, serial_str
201 ),
202 )
203 .into());
204 }
205
206 let (device, serial_number) = devices.remove(0);
207 return Ok(UsbBackend {
208 handle: device.open().context("USB open error")?,
209 device,
210 serial_number,
211 timeout: Duration::from_millis(500),
212 });
213 }
214 }
215
216 pub fn handle(&self) -> &rusb::DeviceHandle<rusb::Context> {
217 &self.handle
218 }
219
220 pub fn device(&self) -> &rusb::Device<rusb::Context> {
221 &self.device
222 }
223
224 pub fn get_vendor_id(&self) -> u16 {
225 self.device.device_descriptor().unwrap().vendor_id()
226 }
227
228 pub fn get_product_id(&self) -> u16 {
229 self.device.device_descriptor().unwrap().product_id()
230 }
231
232 pub fn get_serial_number(&self) -> &str {
234 self.serial_number.as_str()
235 }
236
237 pub fn set_active_configuration(&self, config: u8) -> Result<()> {
238 self.handle
239 .set_active_configuration(config)
240 .context("USB error")
241 }
242
243 pub fn claim_interface(&self, iface: u8) -> Result<()> {
244 self.handle.claim_interface(iface).context("USB error")
245 }
246
247 pub fn release_interface(&self, iface: u8) -> Result<()> {
248 self.handle.release_interface(iface).context("USB error")
249 }
250
251 pub fn set_alternate_setting(&self, iface: u8, setting: u8) -> Result<()> {
252 self.handle
253 .set_alternate_setting(iface, setting)
254 .context("USB error")
255 }
256
257 pub fn kernel_driver_active(&self, iface: u8) -> Result<bool> {
258 self.handle.kernel_driver_active(iface).context("USB error")
259 }
260
261 pub fn detach_kernel_driver(&self, iface: u8) -> Result<()> {
262 self.handle.detach_kernel_driver(iface).context("USB error")
263 }
264
265 pub fn attach_kernel_driver(&self, iface: u8) -> Result<()> {
266 self.handle.attach_kernel_driver(iface).context("USB error")
267 }
268
269 pub fn active_config_descriptor(&self) -> Result<rusb::ConfigDescriptor> {
276 self.device.active_config_descriptor().context("USB error")
277 }
278
279 pub fn bus_number(&self) -> u8 {
280 self.device.bus_number()
281 }
282
283 pub fn port_numbers(&self) -> Result<Vec<u8>> {
284 self.device.port_numbers().context("USB error")
285 }
286
287 pub fn read_string_descriptor_ascii(&self, idx: u8) -> Result<String> {
288 self.handle
289 .read_string_descriptor_ascii(idx)
290 .context("USB error")
291 }
292
293 pub fn reset(&self) -> Result<()> {
294 self.handle.reset().context("USB Error")
295 }
296
297 pub fn write_control(
303 &self,
304 request_type: u8,
305 request: u8,
306 value: u16,
307 index: u16,
308 buf: &[u8],
309 ) -> Result<usize> {
310 self.handle
311 .write_control(request_type, request, value, index, buf, self.timeout)
312 .context("USB error")
313 }
314
315 pub fn read_control(
317 &self,
318 request_type: u8,
319 request: u8,
320 value: u16,
321 index: u16,
322 buf: &mut [u8],
323 ) -> Result<usize> {
324 self.handle
325 .read_control(request_type, request, value, index, buf, self.timeout)
326 .context("USB error")
327 }
328
329 pub fn read_bulk(&self, endpoint: u8, data: &mut [u8]) -> Result<usize> {
331 let len = self
332 .handle
333 .read_bulk(endpoint, data, self.timeout)
334 .context("USB error")?;
335 Ok(len)
336 }
337
338 pub fn read_bulk_timeout(
340 &self,
341 endpoint: u8,
342 data: &mut [u8],
343 timeout: Duration,
344 ) -> Result<usize> {
345 let len = self
346 .handle
347 .read_bulk(endpoint, data, timeout)
348 .context("USB error")?;
349 Ok(len)
350 }
351
352 pub fn write_bulk(&self, endpoint: u8, data: &[u8]) -> Result<usize> {
354 let len = self
355 .handle
356 .write_bulk(endpoint, data, self.timeout)
357 .context("USB error")?;
358 Ok(len)
359 }
360}
361
362pub struct UsbHub {
365 handle: rusb::DeviceHandle<rusb::Context>,
366}
367
368pub enum UsbHubOp {
370 PowerOff,
372 PowerOn,
374 Suspend,
376 Resume,
378 Reset,
380}
381
382const PORT_SUSPEND: u16 = 2;
383const PORT_RESET: u16 = 4;
384const PORT_POWER: u16 = 8;
385
386impl UsbHub {
387 pub fn from_device(dev: &rusb::Device<rusb::Context>) -> Result<UsbHub> {
389 let dev_desc = dev.device_descriptor()?;
391 ensure!(
394 dev_desc.class_code() == rusb::constants::LIBUSB_CLASS_HUB,
395 "device is not a hub"
396 );
397 Ok(UsbHub {
398 handle: dev.open().with_context(|| {
399 format!(
400 "Cannot access USB hub on bus {bus}, address {addr}\n\
401 If this test requires access to the HUB, you need to make sure that \
402 the program has sufficient permissions to access the hub\n\
403 See sw/host/tests/chip/usb/README.md for more information\n\
404 The following command may fix the issue:\n\
405 sudo chmod 0666 /dev/bus/usb/{bus:03}/{addr:03}",
406 bus = dev.bus_number(),
407 addr = dev.address(),
408 )
409 })?,
410 })
411 }
412
413 pub fn device(&self) -> rusb::Device<rusb::Context> {
414 self.handle.device()
415 }
416
417 fn port_status(&self, port: u8, timeout: Duration) -> Result<u16> {
419 let req_type = rusb::constants::LIBUSB_RECIPIENT_OTHER
420 | rusb::constants::LIBUSB_REQUEST_TYPE_CLASS
421 | rusb::constants::LIBUSB_ENDPOINT_IN;
422 let mut status = [0u8; 4];
423 let _ = self.handle.read_control(
424 req_type,
425 rusb::constants::LIBUSB_REQUEST_GET_STATUS,
426 0,
427 port as u16,
428 &mut status,
429 timeout,
430 )?;
431 Ok(status[0] as u16 | (status[1] as u16) << 8)
432 }
433
434 pub fn op(&self, op: UsbHubOp, port: u8, timeout: Duration, check_status: bool) -> Result<()> {
436 let (feature_index, set_feature, human_op) = match op {
437 UsbHubOp::Suspend => (PORT_SUSPEND, true, "suspend"),
438 UsbHubOp::Resume => (PORT_SUSPEND, false, "resume"),
439 UsbHubOp::Reset => (PORT_RESET, true, "reset"),
440 UsbHubOp::PowerOn => (PORT_POWER, true, "power on"),
441 UsbHubOp::PowerOff => (PORT_POWER, false, "power off"),
442 };
443 let req = if set_feature {
444 rusb::constants::LIBUSB_REQUEST_SET_FEATURE
445 } else {
446 rusb::constants::LIBUSB_REQUEST_CLEAR_FEATURE
447 };
448 let req_type = rusb::constants::LIBUSB_RECIPIENT_OTHER
449 | rusb::constants::LIBUSB_REQUEST_TYPE_CLASS
450 | rusb::constants::LIBUSB_ENDPOINT_OUT;
451 let port_status_mask = 1u16 << feature_index;
453 let port_status_after = if set_feature { port_status_mask } else { 0u16 };
454
455 let _ =
457 self.handle
458 .write_control(req_type, req, feature_index, port as u16, &[], timeout)?;
459 if !check_status {
461 return Ok(());
462 }
463 let start = Instant::now();
464 loop {
465 let port_status = self.port_status(port, timeout)?;
466 if port_status & port_status_mask == port_status_after {
467 break;
468 }
469 if start.elapsed() >= timeout {
470 anyhow::bail!(
471 "Trying to {} port {} but port did not change status (last status was {:x})",
472 human_op,
473 port,
474 port_status
475 );
476 }
477 }
478 log::info!("{} performed in {:#?}", human_op, start.elapsed());
479
480 Ok(())
481 }
482}