Software APIs
usbdev_stream.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_stream.h"
5 
6 #include <cassert>
7 #include <cstdio>
8 #include <cstring>
9 #include <iostream>
10 
11 #include "stream_test.h"
12 #include "usb_device.h"
13 #include "usbdev_utils.h"
14 
15 // Stream signature words.
16 #define STREAM_SIGNATURE_HEAD 0x579EA01AU
17 #define STREAM_SIGNATURE_TAIL 0x160AE975U
18 
19 // Seed numbers for the LFSR generators in each transfer direction for
20 // the given stream number.
21 #define USBTST_LFSR_SEED(s) (uint8_t)(0x10U + (s)*7U)
22 #define USBDPI_LFSR_SEED(s) (uint8_t)(0x9BU - (s)*7U)
23 
24 // Simple LFSR for 8-bit sequences.
25 #define LFSR_ADVANCE(lfsr) \
26  (((lfsr) << 1) ^ \
27  ((((lfsr) >> 1) ^ ((lfsr) >> 2) ^ ((lfsr) >> 3) ^ ((lfsr) >> 7)) & 1u))
28 
29 USBDevStream::USBDevStream(unsigned id, uint32_t transfer_bytes, bool retrieve,
30  bool check, bool send, bool verbose) {
31  // Remember Stream IDentifier and flags.
32  SetProperties(id, retrieve, check, send);
33  verbose_ = verbose;
34 
35  // Stream is not in the process of being closed.
36  closing_ = false;
37 
38  // Not yet received stream signature.
39  sig_recvd_ = kSigStateStart;
40 
41  // Initialise LFSR state.
42  tst_lfsr_ = USBTST_LFSR_SEED(id);
43  dpi_lfsr_ = USBDPI_LFSR_SEED(id);
44 
45  // Initialize circular buffer.
46  buf_.wr_idx = 0U;
47  buf_.rd_idx = 0U;
48  buf_.end_idx = 0U;
49 
50  // Number of bytes to be transferred.
51  transfer_bytes_ = transfer_bytes;
52 
53  // Total counts of bytes received and sent.
54  bytes_recvd_ = 0U;
55  bytes_sent_ = 0U;
56 }
57 
58 // Detect a stream signature within the byte stream;
59 // simple restartable parser that drops all bytes until we find a header
60 // signature.
61 //
62 // The signature permits usbdev_stream_test to pass test parameters to this
63 // program as well as overcoming the issue of the mapping from device
64 // endpoints/streams to USB ports not being under our control.
65 uint32_t USBDevStream::SigDetect(usbdev_stream_sig_t *sig, const uint8_t *sp,
66  uint32_t nrecv) {
67  uint32_t dropped = 0U;
68  assert(nrecv > 0);
69  do {
70  if (sig_recvd_ == kSigStateStart) {
71  sig_recvd_ = kSigStateCheckHead;
72  sig_cnt_ = 0U;
73  }
74 
75  // Collect the signature into a structure with appropriate alignment
76  // for direct access to members.
77  uint8_t *sigp = reinterpret_cast<uint8_t *>(sig);
78  sigp[sig_cnt_] = *sp;
79 
80  // Advance the parser beyond this byte
81  // Note: valid signature bytes remain excluded from the returned count of
82  // dropped bytes.
83  const size_t tail_offset = offsetof(usbdev_stream_sig_t, tail_sig);
84  bool discard = false;
85  switch (sig_recvd_) {
86  // Check the bytes of the header signature.
87  case kSigStateCheckHead: {
88  const unsigned sh = sig_cnt_ << 3;
89  uint8_t match = (uint8_t)(STREAM_SIGNATURE_HEAD >> sh);
90  if (match == *sp) {
91  if (++sig_cnt_ >= 4U) {
92  sig_recvd_ = kSigStateSkipBody;
93  }
94  } else {
95  discard = true;
96  }
97  } break;
98 
99  // Just collect the signature body for later validation.
100  case kSigStateSkipBody: {
101  if (++sig_cnt_ >= tail_offset) {
102  sig_recvd_ = kSigStateCheckTail;
103  }
104  } break;
105 
106  // Check the bytes of the tail signature
107  case kSigStateCheckTail: {
108  const unsigned sh = (sig_cnt_ - tail_offset) << 3;
109  const uint8_t match = (uint8_t)(STREAM_SIGNATURE_TAIL >> sh);
110  if (match == *sp) {
111  if (sig_cnt_ >= sizeof(usbdev_stream_sig_t) - 1U) {
112  // Note: at this point we could use the signature words to identify
113  // the Endianness of the sender and thus reverse the other fields
114  // if necessary.
115 
116  // Basic sanity check of signature values before we accept it;
117  // some of the checking must be stream-type dependent, so this is
118  // not a complete guarantee of validity.
119  uint8_t stream = sig->stream & USBDevice::kUsbdevStreamFlagID;
120  if (sig->num_bytes > 0U && sig->num_bytes < 0x10000000U &&
121  stream < STREAMS_MAX) {
122  if (verbose_) {
123  std::cout << PrefixID() << "Signature accepted" << std::endl;
124  }
125  sig_recvd_ = kSigStateReceived;
126  } else {
127  std::cout << PrefixID() << "Signature rejected" << std::endl;
128  // Report the signature to explain why it's been rejected.
129  SigReport(*sig);
130  discard = true;
131  }
132  } else {
133  sig_cnt_++;
134  }
135  } else {
136  discard = true;
137  }
138  } break;
139 
140  default:
141  // Note: should not be called once we have a valid stream signature.
142  assert(!"Invalid/undefined sig_recvd state");
143  break;
144  }
145 
146  if (discard) {
147  // Mismatch; discard the checked data, and resume from the beginning.
148  dropped += sig_cnt_ + 1U;
149  sig_recvd_ = kSigStateStart;
150  sig_cnt_ = 0U;
151  }
152  // Advance to next byte.
153  sp++;
154  } while (--nrecv > 0U && sig_recvd_ != kSigStateReceived);
155 
156  return dropped;
157 }
158 
159 void USBDevStream::SigProcess(const usbdev_stream_sig_t &sig) {
160  // Stream IDentifier and flags.
161  uint8_t stream = sig.stream & USBDevice::kUsbdevStreamFlagID;
162  bool retrieve = ((sig.stream & USBDevice::kUsbdevStreamFlagRetrieve) != 0U);
163  bool check = ((sig.stream & USBDevice::kUsbdevStreamFlagCheck) != 0U);
164  bool send = ((sig.stream & USBDevice::kUsbdevStreamFlagSend) != 0U);
165  SetProperties(stream, retrieve, check, send);
166 
167  // If this is the start of the sequence then we also have the number of bytes
168  // to be transferred.
169  if (!(sig.seq_hi | sig.seq_lo)) {
170  transfer_bytes_ = sig.num_bytes;
171  }
172 }
173 
174 void USBDevStream::SigReport(const usbdev_stream_sig_t &sig) {
175  // Sequence number; important for unreliable packet-based streams
176  // (Isochronous streams).
177  uint16_t seq = sig_read8(offsetof(usbdev_stream_sig_t, seq_lo)) |
178  (sig_read8(offsetof(usbdev_stream_sig_t, seq_hi)) << 8);
179 
180  // Stream IDentifier and flags.
181  uint8_t stream = sig.stream & USBDevice::kUsbdevStreamFlagID;
182  bool retrieve = ((sig.stream & USBDevice::kUsbdevStreamFlagRetrieve) != 0U);
183  bool check = ((sig.stream & USBDevice::kUsbdevStreamFlagCheck) != 0U);
184  bool send = ((sig.stream & USBDevice::kUsbdevStreamFlagSend) != 0U);
185 
186  printf("Signature detected: stream #%u LFSR 0x%02x bytes 0x%x\n", stream,
187  sig.init_lfsr, sig.num_bytes);
188  printf(" - retrieve %c check %c send %c\n", retrieve ? 'Y' : 'N',
189  check ? 'Y' : 'N', send ? 'Y' : 'N');
190  printf(" - seq #%u\n", seq);
191 }
192 
193 // Return an indication of whether this stream has completed its transfer.
196 }
197 
198 bool USBDevStream::ProvisionSpace(uint8_t **space, uint32_t len) {
199  uint32_t space_bytes = SpaceAvailable(space);
200  if (space_bytes >= len) {
201  return true;
202  }
203 
204  // If we're failing to allocate sufficient space right at the end of the
205  // buffer, wrap sooner.
206  if (buf_.rd_idx <= buf_.wr_idx && (kBufferSize - buf_.wr_idx) < len &&
207  buf_.rd_idx > len) {
208  buf_.end_idx = buf_.wr_idx;
209  buf_.wr_idx = 0U;
210  // Return pointer to the start of the free space.
211  if (space) {
212  *space = buf_.data;
213  }
214  return true;
215  }
216 
217  return false;
218 }
219 
220 uint32_t USBDevStream::SpaceAvailable(uint8_t **space) {
221  uint32_t space_bytes = kBufferSize - buf_.wr_idx;
222  uint8_t *start = &buf_.data[buf_.wr_idx];
223 
224  if (buf_.rd_idx > buf_.wr_idx) {
225  // All space is contiguous.
226  space_bytes = (buf_.rd_idx - 1U) - buf_.wr_idx;
227  } else if (!buf_.rd_idx) {
228  // Leave one unused byte at the end of the circular buffer.
229  space_bytes--;
230  }
231 
232  // Return pointer to the start of the free space.
233  if (space) {
234  *space = start;
235  }
236  return space_bytes;
237 }
238 
239 bool USBDevStream::AddData(const uint8_t *data, uint32_t len) {
240  // Ascertain the amount of space available.
241  uint32_t space_bytes = SpaceAvailable(nullptr);
242  if (space_bytes < len) {
243  return false;
244  }
245 
246  // Size of first contiguous chunk.
247  uint32_t chunk = kBufferSize - buf_.wr_idx;
248  if (len < chunk) {
249  chunk = len;
250  }
251  if (data) {
252  // Data is not already present in the buffer.
253  memcpy(&buf_.data[buf_.wr_idx], data, chunk);
254  data += chunk;
255  }
256  buf_.wr_idx += chunk;
257  if (buf_.wr_idx > buf_.end_idx) {
258  buf_.end_idx = buf_.wr_idx;
259  }
260 
261  // Write index wraparound.
262  if (buf_.wr_idx >= kBufferSize) {
263  buf_.wr_idx = 0U;
264  }
265 
266  return true;
267 }
268 
269 void USBDevStream::GenerateData(uint8_t *dp, uint32_t len) {
270  // Generate a stream of bytes _as if_ we'd received them correctly from
271  // the device
272  uint8_t next_lfsr = tst_lfsr_;
273  for (unsigned idx = 0U; idx < len; idx++) {
274  dp[idx] = next_lfsr;
275  next_lfsr = LFSR_ADVANCE(next_lfsr);
276  }
277 }
278 
279 bool USBDevStream::ProcessData(uint8_t *dp, uint32_t len) {
280  bool ok = true;
281 
282  if (len > 0U) {
283  // Record the time of the first data reception.
284  if (!received) {
285  start_time = time_us();
286  received = true;
287  }
288 
289  if (verbose_) {
290  std::cout << "S" << ID()
291  << (cfg.retrieve ? ": Received " : ": Generated ") << len
292  << " byte(s)" << std::endl;
293  }
294 
295  // We can just check and overwrite the input data in-situ.
296  const uint8_t *sp = dp;
297  for (uint32_t idx = 0U; idx < len; idx++) {
298  uint8_t expected = tst_lfsr_;
299  uint8_t recvd = sp[idx];
300 
301  // Check whether the received byte is as expected.
302  if (retrieve_ && check_) {
303  if (recvd != expected) {
304  printf("S%u: Mismatched data from device 0x%02x, expected 0x%02x\n",
305  id_, recvd, expected);
306  ok = false;
307  }
308  }
309 
310  // Simply XOR the two LFSR-generated streams together.
311  dp[idx] = recvd ^ dpi_lfsr_;
312  if (verbose_) {
313  printf("S%u: 0x%02x <- 0x%02x ^ 0x%02x\n", id_, dp[idx], recvd,
314  dpi_lfsr_);
315  }
316 
317  // Advance our LFSRs.
318  tst_lfsr_ = LFSR_ADVANCE(tst_lfsr_);
319  dpi_lfsr_ = LFSR_ADVANCE(dpi_lfsr_);
320  }
321 
322  // Update the buffer writing state.
323  bytes_recvd_ += len;
324  }
325 
326  return ok;
327 }
328 
329 uint32_t USBDevStream::DataAvailable(uint8_t **data) {
330  // Read index wraparound.
331  if (buf_.rd_idx >= buf_.end_idx && buf_.wr_idx > 0U &&
332  buf_.wr_idx < buf_.rd_idx) {
333  buf_.rd_idx = 0U;
334  }
335 
336  uint32_t data_bytes = buf_.end_idx - buf_.rd_idx;
337  uint8_t *start = &buf_.data[buf_.rd_idx];
338 
339  if (buf_.wr_idx >= buf_.rd_idx) {
340  data_bytes = buf_.wr_idx - buf_.rd_idx;
341  }
342  if (data) {
343  *data = start;
344  }
345  return data_bytes;
346 }
347 
348 bool USBDevStream::DiscardData(uint32_t len) {
349  // Ascertain the amount of data available.
350  uint32_t data_bytes = DataAvailable(nullptr);
351  if (data_bytes < len) {
352  return false;
353  }
354 
355  // Adjust buffer state to discard the specified number of bytes.
356  uint32_t chunk = buf_.end_idx - buf_.rd_idx;
357  assert(len <= chunk);
358  if (len < chunk || (len == chunk && buf_.wr_idx >= buf_.end_idx)) {
359  buf_.rd_idx += len;
360  } else {
361  buf_.rd_idx = len - chunk;
362  }
363 
364  return true;
365 }
366 
367 bool USBDevStream::ConsumeData(uint32_t len) {
368  if (!DiscardData(len)) {
369  return false;
370  }
371  // Update the count of transmitted bytes.
372  bytes_sent_ += len;
373  return true;
374 }
375 
377  // Check that the write index is valid before we advance the read index to
378  // clear the buffer.
379  assert(buf_.wr_idx < kBufferSize);
380  assert(buf_.wr_idx <= buf_.end_idx);
381  buf_.rd_idx = buf_.wr_idx;
382 }
383 
384 // Service the given data stream; this base class implementation just provides
385 // some generic progress reporting.
387  if (verbose_) {
388  printf("S%u : rd_idx 0x%x wr_idx 0x%x\n", id_, buf_.rd_idx, buf_.wr_idx);
389  }
390  return true;
391 }