Software APIs
usbdev_serial.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_serial.h"
5 
6 #include <cstdio>
7 #include <iostream>
8 #include <unistd.h>
9 
10 #include "stream_test.h"
11 #include "usbdev_utils.h"
12 
13 USBDevSerial::~USBDevSerial() { Stop(); }
14 
15 // Iniitialise a stream between the specified input and output ports.
16 bool USBDevSerial::Open(const char *in_name, const char *out_name) {
17  // Take a copy of the port names.
18  inPort_ = in_name;
19  outPort_ = out_name;
20  return OpenPorts();
21 }
22 
23 // Open the input and output ports to the board/device for this stream.
24 bool USBDevSerial::OpenPorts() {
25  in_ = port_open(inPort_.c_str(), false);
26  if (in_ < 0) {
27  return false;
28  }
29  out_ = port_open(outPort_.c_str(), true);
30  if (out_ < 0) {
31  close(in_);
32  return false;
33  }
34 
35  if (cfg.verbose) {
36  printf("S%u: input '%s' (%d) output '%s' (%d)\n", id_, inPort_.c_str(), in_,
37  outPort_.c_str(), out_);
38  }
39 
40  return true;
41 }
42 
44  // Close any open port handles.
45  if (in_ >= 0) {
46  close(in_);
47  in_ = -1;
48  }
49  if (out_ >= 0) {
50  close(out_);
51  out_ = -1;
52  }
53 }
54 
56  // This is the same behavior as stopping.
57  Stop();
58 }
59 
61  // I don't think we can expect this to suspend/resume at the byte level
62  // anyway; we seem to resume at a different point in the LFSR, presumably
63  // because some internal buffer within the kernel/driver level dumps some
64  // bytes when we close the handle.
65  return OpenPorts();
66 }
67 
68 // Return a summary report of the stream settings of status.
69 std::string USBDevSerial::Report(bool status, bool verbose) const { return ""; }
70 
71 // Sending of OUT traffic to device.
72 bool USBDevSerial::ServiceOUT() {
73  uint8_t *data;
74  size_t to_send = DataAvailable(&data);
75  if (to_send > 0U) {
76  ssize_t nsent;
77  if (send_) {
78  if (cfg.verbose) {
79  std::cout << PrefixID() << "Trying to send " << to_send << "byte(s)"
80  << std::endl;
81  }
82  // Propagate the modified bytes to the output port.
83  nsent = send_bytes(out_, &buf_.data[buf_.rd_idx], to_send);
84  if (nsent < 0) {
85  return false;
86  }
87  } else {
88  nsent = to_send;
89  }
90 
91  if (cfg.verbose) {
92  std::cout << PrefixID() << (send_ ? "Sent" : "Dropped") << nsent
93  << " byte(s)" << std::endl;
94  }
95 
96  ConsumeData(nsent);
97  }
98 
99  return true;
100 }
101 
102 // Retrieving of IN traffic from device.
103 bool USBDevSerial::ServiceIN() {
104  // Decide how many bytes to try to read into our buffer.
105  uint8_t *dp;
106  uint32_t space_bytes = SpaceAvailable(&dp);
107 
108  uint32_t to_fetch = space_bytes;
109  if (to_fetch > transfer_bytes_ - bytes_recvd_) {
110  to_fetch = transfer_bytes_ - bytes_recvd_;
111  }
112 
113  ssize_t nrecvd;
114  if (!SigReceived() || retrieve_) {
115  // Read as many bytes as we can from the input port.
116  nrecvd = recv_bytes(in_, dp, to_fetch);
117  if (nrecvd < 0) {
118  return false;
119  }
120 
121  // Update the circular buffer with the amount of data that we've written.
122  CommitData(nrecvd);
123 
124  if (nrecvd > 0 && !SigReceived()) {
125  uint32_t dropped = SigDetect(&sig_, dp, (uint32_t)nrecvd);
126 
127  // Consume stream signature, rather than propagating it to the output
128  // side.
129  if (SigReceived()) {
130  SigProcess(sig_);
131  dropped += sizeof(usbdev_stream_sig_t);
132  }
133 
134  // Skip past any dropped bytes, including the signature, so that if there
135  // are additional bytes we may process them.
136  nrecvd = ((uint32_t)nrecvd > dropped) ? ((uint32_t)nrecvd - dropped) : 0;
137  dp += dropped;
138 
139  if (dropped) {
140  DiscardData(dropped);
141  }
142  }
143  } else {
144  // Generate a stream of bytes _as if_ we'd received them correctly from
145  // the device.
146  GenerateData(dp, to_fetch);
147  nrecvd = to_fetch;
148 
149  // Update the circular buffer with the amount of data that we've written.
150  CommitData(nrecvd);
151  }
152 
153  bool ok = true;
154  if (nrecvd > 0) {
155  // Check the received LFSR-generated byte(s) and combine them with the
156  // output of our host-side LFSR.
157  ok = ProcessData(dp, nrecvd);
158  }
159 
160  return ok;
161 }
162 
163 // Service this stream.
165  // The base class may perform some diagnostic reporting common to all streams.
166  bool ok = USBDevStream::Service();
167  if (ok) {
168  // Handle OUT traffic first to try to create more buffer space.
169  ok = ServiceOUT();
170  if (ok) {
171  ok = ServiceIN();
172  }
173  }
174  return ok;
175 }