Software APIs
usbdev_utils.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 "usbdev_utils.h"
5 
6 #include <cerrno>
7 #include <cstdio>
8 #include <cstring>
9 #include <ctime>
10 #include <fcntl.h>
11 #include <iostream>
12 #include <sys/time.h>
13 #include <termios.h>
14 #include <unistd.h>
15 
16 #include "stream_test.h"
17 
18 // Utility function to report an error returned from file/terminal-related
19 // functions.
20 static void report_error(const char *rsn) {
21  std::cerr << "ERROR: " << rsn << ": " << strerror(errno) << " (error "
22  << errno << ")" << std::endl;
23 }
24 
25 // Open and configure a serial port connection to/from the USB device.
26 int port_open(const char *dev_name, bool write) {
27  const char *port_type = write ? "output" : "input";
28  int fd = open(dev_name, write ? O_WRONLY : O_RDONLY);
29  if (fd < 0) {
30  std::cerr << "ERROR: Could not open " << port_type << " port '" << dev_name
31  << "'" << std::endl;
32  ReportSyntax();
33  return -1;
34  }
35 
36  // We need to ensure that we can send full 8-bit binary data with no character
37  // translations and no character echo etc.
38  struct termios tty;
39  if (tcgetattr(fd, &tty) != 0) {
40  report_error("Failed getting terminal attributes");
41  close(fd);
42  return -1;
43  }
44 
45  // 8 bits, no parity, no hardware handshaking.
46  tty.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
47  tty.c_cflag |= CS8 | CREAD | CLOCAL;
48 
49  // No character echo.
50  tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
51 
52  // No software handshaking, no special characters.
53  tty.c_iflag &= ~(IXON | IXOFF | IXANY);
54  tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
55 
56  // Disable line feed conversions and special characters on output traffic.
57  tty.c_oflag &= ~(OPOST | ONLCR);
58 
59  // Non-blocking.
60  tty.c_cc[VTIME] = 0;
61  tty.c_cc[VMIN] = 0;
62 
63  // Set in/out baud rate to be as high as possible; just in case, but it has
64  // no impact upon the measured transfer speed.
65  cfsetispeed(&tty, B4000000);
66  cfsetospeed(&tty, B4000000);
67 
68  // Save tty settings, also checking for error.
69  if (tcsetattr(fd, TCSANOW, &tty) != 0) {
70  report_error("Failed setting terminal attributes");
71  close(fd);
72  return -1;
73  }
74 
75  return fd;
76 }
77 
78 // Receive a sequence of bytes from the USB device, non-blocking.
79 ssize_t recv_bytes(int in, uint8_t *buf, size_t len) {
80  ssize_t nread = 0;
81 
82  // Read as many bytes as we can from the input port.
83  ssize_t n = read(in, buf, len);
84  if (cfg.verbose) {
85  printf("Received %zd byte(s)\n", n);
86  for (int idx = 0; idx < n; idx++) {
87  printf("0x%02x\n", buf[idx]);
88  }
89  fflush(stdout);
90  }
91  if (n < 0) {
92  report_error("Failed to read from input port");
93  return -1;
94  }
95 
96  nread += n;
97  buf += n;
98  len -= (size_t)n;
99 
100  return nread;
101 }
102 
103 // Send a sequence of bytes to the USB device, non-blocking.
104 ssize_t send_bytes(int out, const uint8_t *data, size_t len) {
105  ssize_t nwritten = 0;
106 
107  if (len > 0u) {
108  ssize_t n = write(out, data, len);
109  if (n < 0) {
110  report_error("Failed to write to output port");
111  return -1;
112  }
113 
114  nwritten += n;
115  data += n;
116  len -= n;
117  }
118 
119  return nwritten;
120 }
121 
122 // Current monotonic wall clock time in microseconds.
123 uint64_t time_us(void) {
124  struct timeval ts;
125  int ret = gettimeofday(&ts, NULL);
126  if (ret < 0)
127  return (uint64_t)0u;
128  return ((uint64_t)ts.tv_sec * 1000000u) + ts.tv_usec;
129 }
130 
131 // Dump a sequence of bytes as hexadecimal and ASCII for diagnostic purposes.
132 void buffer_dump(FILE *out, const uint8_t *data, size_t n) {
133  static const char hex_digits[] = "0123456789abcdef";
134  const unsigned ncols = 0x20u;
135  char buf[ncols * 4u + 2u];
136 
137  while (n > 0u) {
138  const unsigned chunk = (n > ncols) ? ncols : (unsigned)n;
139  const uint8_t *row = data;
140  unsigned idx = 0u;
141  char *dp = buf;
142 
143  // Columns of hexadecimal bytes.
144  while (idx < chunk) {
145  dp[0] = hex_digits[row[idx] >> 4];
146  dp[1] = hex_digits[row[idx++] & 0xfu];
147  dp[2] = ' ';
148  dp += 3;
149  }
150  while (idx++ < ncols) {
151  dp[2] = dp[1] = dp[0] = ' ';
152  dp += 3;
153  }
154 
155  // Printable ASCII characters.
156  for (idx = 0u; idx < chunk; idx++) {
157  uint8_t ch = row[idx];
158  *dp++ = (ch < ' ' || ch >= 0x80u) ? '.' : ch;
159  }
160  *dp = '\0';
161  fprintf(out, "%s\n", buf);
162  data += chunk;
163  n -= chunk;
164  }
165 
166  fflush(stdout);
167 }
168 
169 // Link locations for inline functions.
170 extern uint64_t elapsed_time(uint64_t start);
171 extern uint16_t get_le16(const uint8_t *p);