Software APIs
usb_device.cc
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 #include "usb_device.h"
5 
6 #include <cassert>
7 #include <cstring>
8 #include <fcntl.h>
9 #include <iostream>
10 #include <linux/usbdevice_fs.h>
11 #include <unistd.h>
12 
13 #include "usbdev_utils.h"
14 
15 // Hub Class Feature Selectors (Table 11-17. USB 2.0 Protocol Specification)
16 static constexpr unsigned PORT_SUSPEND = 2u;
17 static constexpr unsigned PORT_RESET = 4u;
18 static constexpr unsigned PORT_POWER = 8u;
19 
20 // Initialize USB access, with intent to use a device with the given properties.
21 bool USBDevice::Init(uint16_t vendorID, uint16_t productID, uint8_t devAddress,
22  uint8_t busNumber) {
23 #if STREAMTEST_LIBUSB
24  // Initialize libusb
25  int rc = libusb_init(&ctx_);
26  if (rc < 0) {
27  return ErrorUSB("ERROR: Initializing libusb", rc);
28  }
29 
30  // Remember vendor and product IDs because the device may be opened and
31  // closed many times during the test.
32  vendorID_ = vendorID;
33  productID_ = productID;
34 
35  // Remember the specified bus number and device address.
36  addrSpec_ = devAddress;
37  busSpec_ = busNumber;
38 #endif
39  return true;
40 }
41 
42 // Finalize use of the device.
44  (void)Close();
45 #if STREAMTEST_LIBUSB
46  if (parenth_) {
47  libusb_close(parenth_);
48  parenth_ = nullptr;
49  }
50  libusb_exit(ctx_);
51 #endif
52  return true;
53 }
54 
55 // Open the device, if not already open.
57  // Check whether we have already opened the device.
58  if (devh_) {
59  return true;
60  }
61 
62 #if STREAMTEST_LIBUSB
63  // Locate our USB device.
64  std::cout << "Locating USB device" << std::endl;
65  unsigned numTries = 30u;
66  bool found = false;
67  do {
68  // No device handle at present.
69  devh_ = nullptr;
70 
71  // We need to traverse a list of all devices before opening it; since
72  // we require the port numbers leading to our device, we cannot take the
73  // easier approach of using _open_device_with_vid_pid().
74  libusb_device **dev_list;
75  ssize_t num_devs = libusb_get_device_list(ctx_, &dev_list);
76  int idx;
77  for (idx = 0; idx < num_devs; idx++) {
78  int rc = libusb_get_device_descriptor(dev_list[idx], &devDesc_);
79  if (rc >= 0) {
80  if (verbose_) {
81  std::cout << "Device: "
82  << "VendorID: " << std::hex << devDesc_.idVendor
83  << " ProductID: " << devDesc_.idProduct << std::dec
84  << std::endl;
85  }
86  if (devDesc_.idVendor == vendorID_ &&
87  devDesc_.idProduct == productID_) {
88  // Read device identification; there could be multiple USB devices
89  // present with the same Vendor and Product IDs.
90  uint8_t addr = libusb_get_device_address(dev_list[idx]);
91  uint8_t bus = libusb_get_bus_number(dev_list[idx]);
92  // A device address of 0 is invalid for an addressed/configured
93  // device on the USB, and we use this to denote 'no specific physical
94  // device.'
95  assert(addr);
96 
97  // Filter by bus and address, if specific bus/device required; this
98  // may be because they were specified explicitly when the program
99  // started or simply because we're reopening the same device.
100  if (!addrSpec_ || (addrSpec_ == addr && busSpec_ == bus)) {
101  // We are interested in this device; remember its location.
102  busNumber_ = bus;
103  devAddress_ = addr;
104 
105  // Open a handle to our device.
106  libusb_device *dev = dev_list[idx];
107  rc = libusb_open(dev, &devh_);
108  if (rc < 0) {
109  std::cerr << "Error opening device " << (int)bus << ":"
110  << (int)addr << " - " << libusb_error_name(rc)
111  << std::endl;
112  // Continue trying other devices; the system could have multiple
113  // identical devices visible but access permissions may restrict
114  // which device(s) may be opened.
115  } else {
116  // Obtain the parent
117  libusb_device *parent = libusb_get_parent(dev);
118  if (parent) {
119  rc = libusb_open(parent, &parenth_);
120  if (rc < 0) {
121  std::cerr << "Failed to open parent device - "
122  << libusb_error_name(rc) << std::endl;
123  parenth_ = nullptr;
124  } else {
125  std::cout << "Opened parent\n";
126  }
127  }
128 
129  // Obtain the list of port numbers; required for suspend/resume.
130  uint8_t bus = libusb_get_bus_number(dev);
131  if (verbose_) {
132  std::cout << "Device path: " << (unsigned)bus << "-";
133  }
134  devPath_ = std::to_string(bus) + '-';
135  uint8_t ports[8];
136  int rc = libusb_get_port_numbers(dev, ports, sizeof(ports));
137  if (rc >= 0) {
138  unsigned num_ports = (unsigned)rc;
139  for (unsigned idx = 0u; idx < num_ports; idx++) {
140  if (verbose_) {
141  std::cout << (unsigned)ports[idx];
142  }
143  devPath_ += std::to_string(ports[idx]);
144  if (idx + 1 < num_ports) {
145  std::cout << '.';
146  devPath_ += '.';
147  }
148  // TODO:
149  portNumber_ = ports[idx];
150  }
151  std::cout << std::endl;
152  } else {
153  std::cerr << "Error getting port list: "
154  << libusb_error_name(rc) << std::endl;
155  return false;
156  }
157  break;
158  }
159  }
160  // else This is not the device you are looking for...
161  }
162  }
163  }
164 
165  // Unreference all devices and release device list.
166  libusb_free_device_list(dev_list, 1u);
167 
168  if (devh_) {
169  // Ensure that if we close and reopen this device, we shall return to the
170  // same device.
171  busSpec_ = busNumber_;
172  addrSpec_ = devAddress_;
173  found = true;
174  } else if (numTries-- > 0u) {
175  // Retry a number of times before reporting failure.
176  std::cout << '.' << std::flush;
177  sleep(1);
178  } else {
179  std::cerr << "Unable to locate USB device" << std::endl;
180  return false;
181  }
182  } while (!found);
183 
184  // Report that we have at least found the device.
185  std::cout << "Device found (Bus " << (int)busNumber_ << " Device "
186  << (int)devAddress_ << ")" << std::endl;
187  if (verbose_) {
188  std::cout << " - Path: " << devPath_ << std::endl;
189  }
190 
191  // We need to detach the kernel driver and claim the interface to have maximal
192  // control, eg. suspending device.
193  int rc = libusb_set_auto_detach_kernel_driver(devh_, 1u);
194  if (rc < 0) {
195  std::cerr << "Error detaching kernel driver: " << libusb_error_name(rc)
196  << std::endl;
197  return false;
198  }
199 
200  // Read and check the currently active configuration; this should just be 1
201  // since our test software sets up only a single configuration.
202  int config;
203  rc = libusb_get_configuration(devh_, &config);
204  if (rc < 0) {
205  std::cerr << "Error getting configuration: " << libusb_error_name(rc)
206  << std::endl;
207  }
208 
209  std::cout << "Configuration: " << config << std::endl;
210 #else
211  std::cout << "Running without libusb" << std::endl;
212  devh_ = true;
213 #endif
214  return true;
215 }
216 
217 // Close the device, if open.
219 #if STREAMTEST_LIBUSB
220  if (devh_) {
221  libusb_close(devh_);
222  devh_ = nullptr;
223  }
224 #else
225  devh_ = false;
226 #endif
227  return true;
228 }
229 
231 #if STREAMTEST_LIBUSB
232  struct timeval tv = {0};
233  int rc = libusb_handle_events_timeout(ctx_, &tv);
234  if (rc < 0) {
235  return ErrorUSB("ERROR: Handling events", rc);
236  }
237 #endif
238  return true;
239 }
240 
241 // Return the name of a test phase
243  switch (phase) {
245  return "Suspend";
247  return "SleepResume";
248  case kSuspendPhaseSleepReset:
249  return "SleepReset";
251  return "SleepDisconnect";
253  return "DeepResume";
255  return "DeepReset";
257  return "DeepDisconnect";
259  return "Shutdown";
260  default:
261  return "<Unknown>";
262  }
263 }
264 
266  std::cout << "Reading Test Descriptor" << std::endl;
267 
268  if (!Open()) {
269  return false;
270  }
271 #if STREAMTEST_LIBUSB
272  uint8_t bmRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR |
273  LIBUSB_RECIPIENT_ENDPOINT;
274  std::cout << "req type " << (int)bmRequestType << std::endl;
275  // Send a Vendor-Specific command to read the test descriptor.
276  uint8_t testDesc[0x10u];
277  int rc = libusb_control_transfer(devh_, bmRequestType, kVendorTestConfig, 0u,
278  0u, testDesc, sizeof(testDesc),
279  kControlTransferTimeout);
280  if (rc < 0) {
281  std::cerr << "Error reading test descriptor: " << libusb_error_name(rc)
282  << std::endl;
283  return false;
284  }
285 
286  if (verbose_) {
287  std::cout << "Test Descriptor:" << std::endl;
288  for (unsigned idx = 0u; idx < sizeof(testDesc); idx++) {
289  printf("%u: 0x%02x\n", idx, testDesc[idx]);
290  }
291  }
292 
293  // Validate the received test descriptor.
294  const uint8_t test_sig_head[] = {0x7eu, 0x57u, 0xc0u, 0xf1u};
295  const uint8_t test_sig_tail[] = {0x1fu, 0x0cu, 0x75u, 0xe7u};
296  const uint8_t *dp = testDesc;
297  if (!memcmp(dp, test_sig_head, 4) && 0x10u == get_le16(&dp[4]) &&
298  !memcmp(&dp[12], test_sig_tail, 4)) {
299  usb_testutils_test_number_t testNum =
300  (usb_testutils_test_number_t)get_le16(&dp[6]);
301 
302  if (verbose_) {
303  std::cout << "Test number: " << testNum << " args " << std::hex
304  << (int)dp[8] << " " << (int)dp[9] << " " << (int)dp[10] << " "
305  << (int)dp[11] << std::dec << std::endl;
306  }
307 
308  // Although we needn't retain the test number, having checked it, the test
309  // phase does vary and determines the actions we must perform.
310  testPhase_ = (usbdev_suspend_phase_t)dp[8];
311 
312  // Retain the test number and the test arguments.
313  testNumber_ = testNum;
314  testArg_[0] = dp[8];
315  testArg_[1] = dp[9];
316  testArg_[2] = dp[10];
317  testArg_[3] = dp[11];
318 
319  std::cout << "Test number: " << testNum << " Test Phase: "
320  << PhaseName((usbdev_suspend_phase_t)testArg_[0]) << std::endl;
321  return true;
322  }
323 
324  return false;
325 #else
326  // Default test configuration, since we are unable to issue Vendor-Specific
327  // commands.
328  // - streaming test, 2 bulk streams.
329  testNumber_ = USBDevice::kUsbTestNumberStreams;
330  testArg_[0] = USBDevice::kUsbdevStreamFlagsDefault | 2U;
331  testArg_[1] = 0U;
332  testArg_[2] = 0U;
333  testArg_[3] = 0U;
334 
335  return true;
336 #endif
337 }
338 
339 bool USBDevice::Delay(uint32_t time_us, bool with_traffic) {
340  while (time_us > 0) {
341  uint32_t delay_us = time_us;
342  if (verbose_) {
343  std::cout << "Delaying " << time_us << "us "
344  << (with_traffic ? " - with traffic" : "no traffic")
345  << std::endl;
346  }
347  if (with_traffic) {
348  delay_us = 1000u;
349  // Service streams
350  }
351  usleep(delay_us);
352  time_us -= delay_us;
353  }
354  return true;
355 }
356 
358  std::cout << "Resetting Device" << std::endl;
359 
360  // We need to forget the device address in the event of a Bus Reset
361  // because it will be assigned a new address.
362  addrSpec_ = 0u;
363 
364  if (parenth_) {
365  // Use the hub to reset the device.
366  const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT |
367  LIBUSB_REQUEST_TYPE_CLASS |
368  LIBUSB_RECIPIENT_OTHER;
369  if (verbose_) {
370  std::cout << "Resetting port " << portNumber_ << std::endl;
371  }
372 
373  int rc = libusb_control_transfer(parenth_, bmRequestType,
374  LIBUSB_REQUEST_SET_FEATURE, PORT_RESET,
375  portNumber_, NULL, 0u, 0u);
376  if (rc) {
377  std::cerr << "Failed to reset device - " << libusb_error_name(rc)
378  << std::endl;
379  return false;
380  }
381  if (verbose_) {
382  std::cout << "Done port reset" << std::endl;
383  }
384  } else {
385  if (!Open()) {
386  return false;
387  }
388 
389  int rc = libusb_reset_device(devh_);
390  if (rc < 0) {
391  return false;
392  }
393  }
394  return true;
395 }
396 
398  std::cout << "Suspending Device " << devPath_ << std::endl;
399 
400  if (parenth_) {
401  // Use the hub to suspend the device.
402  const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT |
403  LIBUSB_REQUEST_TYPE_CLASS |
404  LIBUSB_RECIPIENT_OTHER;
405  if (verbose_) {
406  std::cout << "Suspending port " << portNumber_ << std::endl;
407  }
408 
409  int rc = libusb_control_transfer(parenth_, bmRequestType,
410  LIBUSB_REQUEST_SET_FEATURE, PORT_SUSPEND,
411  portNumber_, NULL, 0u, 0u);
412  if (rc) {
413  std::cerr << "Failed to suspend device - " << libusb_error_name(rc)
414  << std::endl;
415  return false;
416  }
417  if (verbose_) {
418  std::cout << "Done suspend" << std::endl;
419  }
420  } else {
421  // We need to relinquish our access to the device otherwise the kernel
422  // will refuse to autosuspend the device!
423  Close();
424 
425  std::string powerPath = "/sys/bus/usb/devices/" + devPath_ + "/power/";
426  std::string filename = powerPath + "autosuspend_delay_ms";
427 
428  int fd = open(filename.c_str(), O_WRONLY);
429  if (fd < 0) {
430  std::cerr << "Failed to open '" << filename << "'" << std::endl;
431  std::cerr << " (Note: this requires super user permissions)"
432  << std::endl;
433  return false;
434  }
435  int rc = write(fd, "0", 1);
436  if (rc < 0) {
437  std::cerr << "Write failed" << std::endl;
438  }
439  rc = close(fd);
440  if (rc < 0) {
441  std::cerr << "Close failed" << std::endl;
442  }
443 
444  // Enable auto-suspend behavior.
445  filename = powerPath + "control";
446  fd = open(filename.c_str(), O_WRONLY);
447  if (fd < 0) {
448  std::cerr << "Failed to open '" << filename << "'" << std::endl;
449  std::cerr << " (Note: this requires super user permissions)"
450  << std::endl;
451  return false;
452  }
453  rc = write(fd, "auto", 4);
454  if (rc < 0) {
455  std::cerr << "Write failed" << std::endl;
456  }
457  rc = close(fd);
458  if (rc < 0) {
459  std::cerr << "Close failed" << std::endl;
460  }
461  }
462 
463  SetState(StateSuspending);
464  return true;
465 }
466 
468  std::cout << "Resuming Device" << std::endl;
469 
470  if (parenth_) {
471  // Use the hub to suspend the device.
472  const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT |
473  LIBUSB_REQUEST_TYPE_CLASS |
474  LIBUSB_RECIPIENT_OTHER;
475  if (verbose_) {
476  std::cout << "Resuming port " << portNumber_ << std::endl;
477  }
478 
479  int rc = libusb_control_transfer(parenth_, bmRequestType,
480  LIBUSB_REQUEST_CLEAR_FEATURE, PORT_SUSPEND,
481  portNumber_, NULL, 0u, 0u);
482  if (rc) {
483  std::cerr << "Failed to resume device - " << libusb_error_name(rc)
484  << std::endl;
485  return false;
486  }
487  if (verbose_) {
488  std::cout << "Done resume" << std::endl;
489  }
490  } else {
491  std::string powerPath = "/sys/bus/usb/devices/" + devPath_ + "/power/";
492  std::string filename = powerPath + "control";
493 
494  int fd = open(filename.c_str(), O_WRONLY);
495  if (fd < 0) {
496  std::cerr << "Failed to open '" << filename << "'" << std::endl;
497  return false;
498  }
499  int rc = write(fd, "on", 2);
500  if (rc < 0) {
501  std::cerr << "Write failed" << std::endl;
502  }
503  close(fd);
504 
505  if (!Open()) {
506  return false;
507  }
508  }
509 
510  SetState(StateResuming);
511  return true;
512 }
513 
514 // Note: The Rust harness uses VBUS_SENSE_EN on the HyperDebug board
515 // (non-portable) to achieve connect and disconnect functionality, which we
516 // cannot achieve with regular hubs.
517 //
518 // Instead what happens with the available hub(s) is that all USB traffic
519 // to/from the USB ceases but the bus remains Idle/Suspended and VBUS remains
520 // asserted. Reconnect and the result is Reset Signaling and reconfiguration.
521 
523  std::cout << "Connecting Device " << devPath_ << std::endl;
524 
525  if (parenth_) {
526  // Use the hub to suspend the device.
527  const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT |
528  LIBUSB_REQUEST_TYPE_CLASS |
529  LIBUSB_RECIPIENT_OTHER;
530  if (verbose_) {
531  std::cout << "Connecting port " << portNumber_ << std::endl;
532  }
533 
534  int rc = libusb_control_transfer(parenth_, bmRequestType,
535  LIBUSB_REQUEST_SET_FEATURE, PORT_POWER,
536  portNumber_, NULL, 0u, 0u);
537  if (rc) {
538  std::cerr << "Failed to connect device - " << libusb_error_name(rc)
539  << std::endl;
540  return false;
541  }
542  if (verbose_) {
543  std::cout << "Done connect" << std::endl;
544  }
545  } else {
546  std::cout << "Connect operation not available" << std::endl;
547  return false;
548  }
549 
550  return true;
551 }
552 
554  std::cout << "Disconnecting Device " << devPath_ << std::endl;
555 
556  if (parenth_) {
557  // Use the hub to suspend the device.
558  const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT |
559  LIBUSB_REQUEST_TYPE_CLASS |
560  LIBUSB_RECIPIENT_OTHER;
561  if (verbose_) {
562  std::cout << "Disconnecting port " << portNumber_ << std::endl;
563  }
564 
565  int rc = libusb_control_transfer(parenth_, bmRequestType,
566  LIBUSB_REQUEST_CLEAR_FEATURE, PORT_POWER,
567  portNumber_, NULL, 0u, 0u);
568  if (rc) {
569  std::cerr << "Failed to disconnect device - " << libusb_error_name(rc)
570  << std::endl;
571  return false;
572  }
573  if (verbose_) {
574  std::cout << "Done disconnect" << std::endl;
575  }
576  } else {
577  std::cout << "Disconnect operation not available" << std::endl;
578  return false;
579  }
580 
581  return true;
582 }