Software APIs
usb_testutils_streams.c
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 //
5 // USB streaming utility code
6 //
7 // This test code supports a set of concurrent uni/bidirectional streams using
8 // the Bulk Transfer type, such that all data is delivered reliably to/from the
9 // USB host.
10 //
11 // The data itself is pseudo-randomly generated by the sender and,
12 // independently, by the receiving code to check that the data has been
13 // propagated unmodified and without data loss, corruption, replication etc.
14 
15 #include "sw/device/lib/testing/usb_testutils_streams.h"
16 
18 #include "sw/device/lib/testing/test_framework/check.h"
19 #include "sw/device/lib/testing/usb_testutils_controlep.h"
20 #include "sw/device/lib/testing/usb_testutils_diags.h"
21 
22 #define MODULE_ID MAKE_MODULE_ID('u', 't', 's')
23 
24 // Maximum length of the configuration descriptor.
25 #define CFG_DSCR_LEN_MAX \
26  (USB_CFG_DSCR_LEN + \
27  USBUTILS_STREAMS_MAX * (USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN))
28 
29 /**
30  * Read method to be employed
31  */
32 static const enum {
33  kReadMethodNone = 0u, // Just discard the data; do not read it from usbdev
34  kReadMethodStandard, // Use standard dif_usbdev_buffer_read() function
35  kReadMethodFaster // Faster implementation
36 } read_method = kReadMethodStandard;
37 
38 /**
39  * Write method to be employed
40  */
41 static const enum {
42  kWriteMethodStandard = 1u, // Use standard dif_usbdev_buffer_write() function
43  kWriteMethodFaster // Faster implementation
44 } write_method = kWriteMethodStandard;
45 
46 /**
47  * Diagnostic logging; expensive
48  */
49 static bool log_traffic = false;
50 
51 // Determine the length of the next packet in the data stream, _including_
52 // any required signature.
53 static uint8_t packet_length(const usbdev_stream_t *s, uint32_t bytes_done,
54  uint8_t *bufsz_lfsr) {
55  // For non-Isochronous streams the packet size is also constrained by the
56  // total size of the transfer. For Isochronous streams we just keep
57  // transmitting to account for the possibility of packet loss.
58  uint8_t bytes_left = USBDEV_MAX_PACKET_SIZE;
59 
60  if (s->xfr_type != kUsbTransferTypeIsochronous) {
61  if (bytes_done < s->transfer_bytes &&
62  s->transfer_bytes - bytes_done < bytes_left) {
63  bytes_left = (uint8_t)(s->transfer_bytes - bytes_done);
64  }
65  }
66 
67  // Length of packet
68  unsigned num_bytes;
69 
70  if (s->flags & kUsbdevStreamFlagMaxPackets) {
71  num_bytes = bytes_left;
72  } else {
73  // Vary the amount of data sent per buffer
74  if (s->tx.sig_required) {
75  const unsigned sig_bytes = sizeof(usbdev_stream_sig_t);
76  switch (s->xfr_type) {
77  case kUsbTransferTypeIsochronous: {
78  // For Isochronous streams, and the first packet of other transfers,
79  // we must constrain the minimum packet size
80  const unsigned max_bytes = USBDEV_MAX_PACKET_SIZE - sig_bytes;
81  num_bytes = sig_bytes + (*bufsz_lfsr % (max_bytes + 1u));
82  } break;
83  default:
84  num_bytes = sig_bytes;
85  break;
86  }
87  } else {
88  num_bytes = *bufsz_lfsr % (bytes_left + 1u);
89  }
90  *bufsz_lfsr = LFSR_ADVANCE(*bufsz_lfsr);
91  }
92 
93  return (uint8_t)num_bytes;
94 }
95 
96 // Dump a sequence of bytes as hexadecimal and ASCII for diagnostic purposes
97 static void buffer_dump(const uint8_t *data, size_t n) {
98  base_hexdump_fmt_t fmt = {
99  .bytes_per_word = 1,
100  .words_per_line = 0x20u,
101  .alphabet = &kBaseHexdumpDefaultFmtAlphabet,
102  };
103 
104  base_hexdump_with(fmt, (char *)data, n);
105 }
106 
107 // Create a stream signature buffer
108 static uint8_t buffer_sig_create(usb_testutils_streams_ctx_t *ctx,
110  // Number of bytes left to be transmitted
111  // - for non-Isochronous streams this is just the total number of bytes to be
112  // transmitted, and there's a single signature at the start of the stream
113  // - for Isochronous this provideds a down count, and it will wrap around
114  // upon reaching zero, in the event of packet loss and prolonged
115  // transmission
116  uint32_t tx_left = s->transfer_bytes - (s->tx.bytes % s->transfer_bytes);
117 
118  usbdev_stream_sig_t sig;
119  sig.head_sig = USBDEV_STREAM_SIGNATURE_HEAD;
120  sig.init_lfsr = s->tx.lfsr;
121  sig.stream = s->id | (uint8_t)s->flags;
122  sig.seq_lo = (uint8_t)s->tx.seq;
123  sig.seq_hi = (uint8_t)(s->tx.seq >> 8);
124  sig.num_bytes = tx_left;
125  sig.tail_sig = USBDEV_STREAM_SIGNATURE_TAIL;
126 
127  // Advance sequence counter
128  s->tx.seq++;
129 
130  if (s->verbose && log_traffic) {
131  buffer_dump((uint8_t *)&sig, sizeof(sig));
132  }
133 
134  size_t bytes_written;
135  switch (write_method) {
136 #if USBUTILS_MEM_FASTER
137  case kWriteMethodFaster:
138  // TODO: integrate faster transfers to/from USB device packet buffer
139  // memory.
141 #endif
142  default:
143  CHECK_DIF_OK(dif_usbdev_buffer_write(
144  ctx->usbdev->dev, buf, (uint8_t *)&sig, sizeof(sig), &bytes_written));
145  CHECK(bytes_written == sizeof(sig));
146  break;
147  }
148 
149  // Note: stream signature is not included in the count of bytes transferred
150  // so we do not advance tx_bytes
151  return (uint8_t)bytes_written;
152 }
153 
154 // Check a stream signature
155 //
156 // Note: Only Isochronous streams are expected to receive signatures; in order
157 // to keep the device and host ends synchronized in the presence of packet-
158 // dropping, a signature occurs at the start of each packet. Each signature
159 // includes a packet sequence number and the intiial value of the sender's LFSR
160 static bool buffer_sig_check(usb_testutils_streams_ctx_t *ctx,
161  usbdev_stream_t *s, const usbdev_stream_sig_t *sig,
162  uint8_t len) {
163  // Validate the signature
164  if (sig->head_sig != USBDEV_STREAM_SIGNATURE_HEAD ||
165  sig->tail_sig != USBDEV_STREAM_SIGNATURE_TAIL ||
166  // Returned to the correct stream?
167  (sig->stream & 0xfU) != s->id) {
168  LOG_INFO("buffer_sig_check failed: ");
169  return false;
170  }
171 
172  // Sequence number not regressed?
173  uint16_t seq = (uint16_t)((sig->seq_hi << 8) | sig->seq_lo);
174  if (seq < s->rx_seq) {
175  LOG_INFO("buffer_sig_check: sequence number regressed from 0x%x to 0x%x",
176  s->rx_seq, seq);
177  return false;
178  }
179 
180  // Current LFSR state and sequence number
181  uint16_t rx_seq = s->rx_seq;
182  uint8_t rx_lfsr = s->rx_lfsr;
183  uint8_t rxtx_lfsr = s->rxtx_lfsr;
184 
185  // Skip past any packets that appear to have been dropped; this is
186  // permissible for Isochronous transfers which prioritize service/real-time
187  // delivery over reliable transmission.
188  if (rx_seq < seq) {
189  do {
190  // Determine the length of the missing packet
191  uint8_t len = packet_length(s, s->rx_bytes, &s->rx_buf_size);
192  len -= sizeof(*sig);
193  // Advance the LFSR states to account for the missing packet
194  while (len-- > 0U) {
195  rxtx_lfsr = LFSR_ADVANCE(rxtx_lfsr);
196  }
197  // The updated host-side LFSR has been supplied in the packet signature
198  rx_seq++;
199  } while (rx_seq < seq);
200 
201  // Since we do not know where the packet(s) were dropped, we must set our
202  // knowledge of the host-side LFSR to the value indicated in this packet.
203  rx_lfsr = sig->init_lfsr;
204  }
205 
206  // Our expectation of the host/DPI LFSR should now match the value that it has
207  // supplied in the modified packet.
208  if (rx_lfsr != sig->init_lfsr) {
209  LOG_INFO(
210  "buffer_sig_check: S%u unexpected host-side LFSR value (0x%x but "
211  "expected 0x%x)",
212  s->id, sig->init_lfsr, rx_lfsr);
213  return false;
214  }
215 
216  // For 'rx_buf_size' to track the 'tx_buf_size' used when the packets were
217  // created and transmitted, we must also ascertain the expected length of the
218  // packet that we have just received; we thus check that the length of this
219  // packet has not changed either.
220  uint8_t exp_len = packet_length(s, s->rx_bytes, &s->rx_buf_size);
221  if (len != exp_len) {
222  LOG_INFO(
223  "buffer_sig_check: S%u unexpected packet length (0x%x but expected "
224  "0x%x)",
225  s->id, len, exp_len);
226  return false;
227  }
228 
229  // Expected sequence number of next packet
230  s->rx_seq = rx_seq + 1U;
231  // Update the LFSR states
232  s->rxtx_lfsr = rxtx_lfsr;
233  s->rx_lfsr = rx_lfsr;
234 
235  return true;
236 }
237 
238 // Fill a buffer with LFSR-generated data
239 static void buffer_fill(usb_testutils_streams_ctx_t *ctx, usbdev_stream_t *s,
240  dif_usbdev_buffer_t *buf, uint8_t num_bytes) {
241  alignas(uint32_t) uint8_t data[USBDEV_MAX_PACKET_SIZE];
242 
243  CHECK(num_bytes <= buf->remaining_bytes);
244  CHECK(num_bytes <= sizeof(data));
245 
246  if (s->generating) {
247  // Emit LFSR-generated byte stream; keep this brief so that we can
248  // reduce our latency in responding to USB events (usb_testutils employs
249  // polling at present)
250  uint8_t lfsr = s->tx.lfsr;
251 
252  const uint8_t *edp = &data[num_bytes];
253  uint8_t *dp = data;
254  while (dp < edp) {
255  *dp++ = lfsr;
256  lfsr = LFSR_ADVANCE(lfsr);
257  }
258 
259  // Update the LFSR for the next packet
260  s->tx.lfsr = lfsr;
261  } else {
262  // Undefined buffer contents; useful for profiling IN throughput on
263  // CW310, because the CPU load at 10MHz can be an appreciable slowdown
264  }
265 
266  if (s->verbose && log_traffic) {
267  buffer_dump(data, num_bytes);
268  }
269 
270  size_t bytes_written;
271  switch (write_method) {
272 #if USBUTILS_MEM_FASTER
273  case kWriteMethodFaster:
274  // TODO: integrate faster transfers to/from USB device packet buffer
275  // memory.
277 #endif
278  default:
279  CHECK_DIF_OK(dif_usbdev_buffer_write(ctx->usbdev->dev, buf, data,
280  num_bytes, &bytes_written));
281  CHECK(bytes_written == num_bytes);
282  break;
283  }
284  s->tx.bytes += bytes_written;
285 }
286 
287 // Check the contents of a received buffer
288 static void buffer_check(usb_testutils_streams_ctx_t *ctx, usbdev_stream_t *s,
289  dif_usbdev_rx_packet_info_t packet_info,
290  dif_usbdev_buffer_t buf) {
291  usb_testutils_ctx_t *usbdev = ctx->usbdev;
292  uint8_t len = packet_info.length;
293 
294  if (len > 0) {
295  alignas(uint32_t) uint8_t data[USBDEV_MAX_PACKET_SIZE];
296 
297  CHECK(len <= sizeof(data));
298 
299  // Notes: the buffer being read here is USBDEV memory accessed as MMIO, so
300  // only the DIF accesses it directly. when we consume the final bytes
301  // from the read buffer, it is automatically returned to the buffer
302  // pool.
303 
304  size_t bytes_read;
305  switch (read_method) {
306 #if USBUTILS_MEM_FASTER
307  // Faster read performance using custom routine
308  case kReadMethodFaster:
309  // TODO: faster read method not yet integrated, defaulting to standard
311 #endif
312  default:
313  CHECK_DIF_OK(dif_usbdev_buffer_read(usbdev->dev, usbdev->buffer_pool,
314  &buf, data, len, &bytes_read));
315  break;
316  }
317  CHECK(bytes_read == len);
318 
319  if (s->verbose && log_traffic) {
320  buffer_dump(data, bytes_read);
321  }
322 
323  // Byte offset of LFSR-generated byte stream
324  size_t offset = 0U;
325 
326  switch (s->xfr_type) {
327  case kUsbTransferTypeIsochronous: {
328  // Check the packet signature, advance the LFSRs and drop through to
329  // check the remainder of the packet
330  const usbdev_stream_sig_t *sig = (usbdev_stream_sig_t *)data;
331  bool ok = buffer_sig_check(ctx, s, sig, len);
332  CHECK(ok, "S%u: Received packet invalid", s->id);
333 
334  offset = sizeof(*sig);
335  } // no break
337  case kUsbTransferTypeInterrupt:
339  case kUsbTransferTypeBulk: {
340  // Check received data against expected LFSR-generated byte stream;
341  // keep this brief so that we can reduce our latency in responding to
342  // USB events (usb_testutils employs polling at present)
343  uint8_t rxtx_lfsr = s->rxtx_lfsr;
344  uint8_t rx_lfsr = s->rx_lfsr;
345 
346  const uint8_t *esp = &data[bytes_read];
347  const uint8_t *sp = &data[offset];
348  while (sp < esp) {
349  // Received data should be the XOR of two LFSR-generated PRND streams
350  // - ours on the transmission side, and that of the DPI model
351  uint8_t expected = rxtx_lfsr ^ rx_lfsr;
352  CHECK(expected == *sp,
353  "S%u: Unexpected received data 0x%02x : (LFSRs 0x%02x 0x%02x)",
354  s->id, *sp, rxtx_lfsr, rx_lfsr);
355 
356  rxtx_lfsr = LFSR_ADVANCE(rxtx_lfsr);
357  rx_lfsr = LFSR_ADVANCE(rx_lfsr);
358  sp++;
359  }
360 
361  // Update the LFSRs for the next packet
362  s->rxtx_lfsr = rxtx_lfsr;
363  s->rx_lfsr = rx_lfsr;
364 
365  // Update the count of LFSR bytes received
366  s->rx_bytes += bytes_read - offset;
367  } break;
368 
369  default:
370  CHECK(s->xfr_type == kUsbTransferTypeControl);
371  break;
372  }
373  } else {
374  // In the event that we've received a zero-length data packet, we still
375  // must return the buffer to the pool
376  CHECK_DIF_OK(
377  dif_usbdev_buffer_return(usbdev->dev, usbdev->buffer_pool, &buf));
378  }
379 }
380 
381 // Callback for successful buffer transmission
382 static status_t strm_tx_done(void *cb_v, usb_testutils_xfr_result_t result) {
383  // Pointer to callback context.
385  // Streaming context and per-stream context.
386  usb_testutils_streams_ctx_t *ctx = cb->ctx;
387  usbdev_stream_t *s = cb->s;
388 
389  // If we do not have at least one queued buffer then something has gone wrong
390  // and this callback is inappropriate
391  uint8_t tx_ep = s->tx_ep;
392  uint8_t nqueued = ctx->tx_bufs_queued[tx_ep];
393 
394  if (s->verbose) {
395  LOG_INFO("strm_tx_done called. %u (%u total) buffers(s) are queued",
396  nqueued, ctx->tx_queued_total);
397  }
398 
399  TRY_CHECK(nqueued > 0);
400 
401  // Note: since buffer transmission and completion signalling both occur within
402  // the foreground code (polling, not interrupt-driven) there is no issue of
403  // potential races here
404 
405  if (nqueued > 0) {
406  // Advance the 'committed transmission' state for this stream
407  s->tx_cmt = ctx->tx_bufs[tx_ep][0u].tx;
408 
409  // Shuffle the buffer descriptions, without using memmove
410  for (unsigned idx = 1u; idx < nqueued; idx++) {
411  ctx->tx_bufs[tx_ep][idx - 1u] = ctx->tx_bufs[tx_ep][idx];
412  }
413 
414  // Is there another buffer ready to be transmitted?
415  ctx->tx_queued_total--;
416  ctx->tx_bufs_queued[tx_ep] = --nqueued;
417 
418  if (nqueued) {
419  usb_testutils_ctx_t *usbdev = ctx->usbdev;
420  TRY(dif_usbdev_send(usbdev->dev, tx_ep, &ctx->tx_bufs[tx_ep][0u].buf));
421  }
422  }
423  return OK_STATUS();
424 }
425 
426 // Callback for buffer reception
427 static status_t strm_rx(void *cb_v, dif_usbdev_rx_packet_info_t packet_info,
428  dif_usbdev_buffer_t buf) {
429  // Pointer to callback context.
431  // Streaming context and per-stream context.
432  usb_testutils_streams_ctx_t *ctx = cb->ctx;
433  usbdev_stream_t *s = cb->s;
434 
435  TRY_CHECK(packet_info.endpoint == s->rx_ep);
436 
437  // We do not expect to receive SETUP packets to this endpoint
438  TRY_CHECK(!packet_info.is_setup);
439 
440  if (s->verbose) {
441  LOG_INFO("Stream %u: Received buffer of %u bytes(s)", s->id,
442  packet_info.length);
443  }
444 
445  if (s->sending && s->generating) {
446  buffer_check(ctx, s, packet_info, buf);
447  } else {
448  // Note: this is useful for profiling the OUT performance on CW310
449  usb_testutils_ctx_t *usbdev = ctx->usbdev;
450 
451  if (read_method != kReadMethodNone && packet_info.length > 0) {
452  alignas(uint32_t) uint8_t data[USBDEV_MAX_PACKET_SIZE];
453  size_t len = packet_info.length;
454  size_t bytes_read;
455 
456  switch (read_method) {
457 #if USBUTILS_MEM_FASTER
458  // Faster read performance using custom routine
459  case kReadMethodFaster:
460  // TODO: faster read method not yet integrated, defaulting to standard
462 #endif
463  // Use the standard interface
464  default:
465  TRY(dif_usbdev_buffer_read(usbdev->dev, usbdev->buffer_pool, &buf,
466  data, len, &bytes_read));
467  break;
468  }
469  } else {
470  // Just discard the data, without reading it; peak OUT performance
471  TRY(dif_usbdev_buffer_return(usbdev->dev, usbdev->buffer_pool, &buf));
472  }
473 
474  s->rx_bytes += packet_info.length;
475  }
476 
477  return OK_STATUS();
478 }
479 
480 // Set the count of already-initialized streams, and apportion the available
481 // tx buffers among the streams.
482 bool usb_testutils_streams_count_set(usb_testutils_streams_ctx_t *ctx,
483  unsigned nstreams) {
484  // Too many streams?
485  if (nstreams > USBUTILS_STREAMS_MAX) {
486  return false;
487  }
488 
489  // Decide how many buffers each endpoint may queue up for transmission;
490  // we must ensure that there are buffers available for reception, and we
491  // do not want any endpoint to starve another
492  for (unsigned s = 0U; s < nstreams; s++) {
493  // This is slightly overspending the available buffers, leaving the
494  // endpoints to vie for the final few buffers, so it's important that
495  // we limit the total number of buffers across all endpoints too
496  unsigned ep = ctx->streams[s].tx_ep;
497  ctx->tx_bufs_queued[ep] = 0U;
498  ctx->tx_bufs_limit[ep] =
499  (uint8_t)((USBUTILS_STREAMS_TXBUF_MAX + nstreams - 1) / nstreams);
500  }
501  ctx->tx_queued_total = 0U;
502 
503  // Remember the stream count
504  ctx->nstreams = (uint8_t)nstreams;
505 
506  return true;
507 }
508 
509 // Returns an indication of whether a stream has completed its data transfer
510 bool usb_testutils_stream_completed(const usb_testutils_streams_ctx_t *ctx,
511  uint8_t id) {
512  // Locate the stream context information
513  const usbdev_stream_t *s = &ctx->streams[id];
514 
515  return (s->tx.bytes >= s->transfer_bytes) &&
516  (s->rx_bytes >= s->transfer_bytes);
517 }
518 
519 // Initialize a stream, preparing it for use
520 status_t usb_testutils_stream_init(usb_testutils_streams_ctx_t *ctx, uint8_t id,
521  usb_testutils_transfer_type_t xfr_type,
522  uint8_t ep_in, uint8_t ep_out,
523  uint32_t transfer_bytes,
524  usbdev_stream_flags_t flags, bool verbose) {
525  // Locate the stream context information
526  TRY_CHECK(id < USBUTILS_STREAMS_MAX);
527  usbdev_stream_t *s = &ctx->streams[id];
528 
529  // Remember the stream IDentifier and flags
530  s->id = id;
531  s->flags = flags;
532 
533  // Remember the stream transfer type
534  s->xfr_type = xfr_type;
535 
536  // Remember whether verbose reporting is required
537  s->verbose = verbose;
538 
539  // Extract a couple of convenient flags; from our perspective
540  s->sending = ((flags & kUsbdevStreamFlagRetrieve) != 0U);
541  s->generating = ((flags & kUsbdevStreamFlagCheck) != 0U);
542 
543  // Not yet sent stream signature
544  s->tx.sig_required = true;
545 
546  // Initialize the transfer state
547  s->tx.bytes = 0u;
548  s->rx_bytes = 0u;
549  s->transfer_bytes = transfer_bytes;
550 
551  // Sequence number to be used for first packet
552  s->tx.seq = 0u;
553  s->rx_seq = s->tx.seq;
554 
555  // Initialize the LFSR state for transmission and reception sides
556  // - we use a simple LFSR to generate a PRND stream to transmit to the USBPI
557  // - the USBDPI XORs the received data with another LFSR-generated stream of
558  // its own, and transmits the result back to us
559  // - to check the returned data, our reception code mimics both LFSRs
560  s->tx.lfsr = USBTST_LFSR_SEED(id);
561  s->rxtx_lfsr = s->tx.lfsr;
562  s->rx_lfsr = USBDPI_LFSR_SEED(id);
563 
564  // Packet size randomization
565  s->tx.buf_size = BUFSZ_LFSR_SEED(id);
566  s->rx_buf_size = s->tx.buf_size;
567 
568  s->rx_ep = ep_out;
569  s->tx_ep = ep_in;
570 
571  // Retain the current transmission state as the latest 'committed transmission
572  // state'
573  s->tx_cmt = s->tx;
574 
575  // Callback context pointer; permits access to both the stream-specific state
576  // and the enclosing streaming context.
577  usbdev_stream_cb_ctx_t *cb = &ctx->cb[id];
578  cb->ctx = ctx;
579  cb->s = s;
580 
581  // Set up the endpoint for IN transfers (TO host)
582  CHECK_STATUS_OK(usb_testutils_in_endpoint_setup(
583  ctx->usbdev, ep_in, xfr_type, cb, strm_tx_done, NULL, NULL));
584 
585  // Set up the endpoint for OUT transfers (FROM host)
586  CHECK_STATUS_OK(usb_testutils_out_endpoint_setup(
587  ctx->usbdev, ep_out, xfr_type, kUsbdevOutStream, cb, strm_rx, NULL));
588 
589  return OK_STATUS();
590 }
591 
592 // Service the given stream, preparing and/or sending any data that we can;
593 // data reception is handled via callbacks and requires no attention here
594 status_t usb_testutils_stream_service(usb_testutils_streams_ctx_t *ctx,
595  uint8_t id) {
596  // Locate the stream context information
597  TRY_CHECK(id < USBUTILS_STREAMS_MAX);
598  usbdev_stream_t *s = &ctx->streams[id];
599 
600  // Generate output data as soon as possible and make it available for
601  // collection by the host
602 
603  uint8_t tx_ep = s->tx_ep;
604  uint8_t nqueued = ctx->tx_bufs_queued[tx_ep];
605 
606  // Isochronous streams never cease transmission because packets are expected
607  // to get dropped; let the reception side decided test completion
608  bool tx_more = (s->xfr_type == kUsbTransferTypeIsochronous) ||
609  (s->tx.bytes < s->transfer_bytes);
610 
611  if (tx_more && // More bytes to transfer?
612  nqueued < ctx->tx_bufs_limit[tx_ep] && // Endpoint allowed buffer?
613  ctx->tx_queued_total <
614  USBUTILS_STREAMS_TXBUF_MAX) { // Total buffers not exceeded?
616 
617  // See whether we can populate another buffer yet
619  ctx->usbdev->dev, ctx->usbdev->buffer_pool, &buf);
620  if (dif_result == kDifOk) {
621  // This is just for reporting the number of buffers presented to the
622  // USB device, as a progress indicator
623  static unsigned bufs_sent = 0u;
624  uint8_t bytes_added = 0u;
625  uint8_t num_bytes;
626 
627  // Decide upon the packet length, _including_ any signature bytes.
628  num_bytes = packet_length(s, s->tx.bytes, &s->tx.buf_size);
629 
630  // Construct a signature to send to the host-side software,
631  // identifying the stream and its properties?
632  if (s->tx.sig_required) {
633  bytes_added = buffer_sig_create(ctx, s, &buf);
634  // If we're 'not sending' then we still send the stream signature once
635  // but only the signature; it allows the host/DPI model to receive the
636  // stream flags and discover that no further data is to be expected.
637  if (!s->sending) {
638  s->tx.bytes = s->transfer_bytes;
639  }
640  // Signature required for each packet of an Isochronous stream, but only
641  // at the stream start for other transfer types
642  if (!s->sending || s->xfr_type != kUsbTransferTypeIsochronous) {
643  // First packet is just the stream signature
644  s->tx.sig_required = false;
645  }
646  }
647 
648  // How many LFSR-generated bytes are to be included?
649  if (bytes_added < num_bytes) {
650  buffer_fill(ctx, s, &buf, (uint8_t)(num_bytes - bytes_added));
651  }
652 
653  // Remember the buffer and the current transmission state until we're
654  // informed that it has been successfully transmitted
655  //
656  // Note: since the 'tx_done' callback occurs from foreground code that
657  // is polling, there is no issue of interrupt races here
658  ctx->tx_bufs[tx_ep][nqueued].buf = buf;
659  ctx->tx_bufs[tx_ep][nqueued].tx = s->tx;
660  ctx->tx_bufs_queued[tx_ep] = ++nqueued;
661  ctx->tx_queued_total++;
662 
663  // Can we present this buffer for transmission yet?
664  if (nqueued <= 1U) {
665  TRY(dif_usbdev_send(ctx->usbdev->dev, tx_ep, &buf));
666  }
667 
668  if (s->verbose) {
669  LOG_INFO(
670  "Stream %u: %uth buffer (of 0x%x byte(s)) awaiting transmission",
671  s->id, bufs_sent, num_bytes);
672  }
673  bufs_sent++;
674  } else {
675  // If we have no more buffers available right now, continue polling...
676  CHECK(dif_result == kDifUnavailable);
677  }
678  }
679  return OK_STATUS();
680 }
681 
682 status_t usb_testutils_streams_init(
683  usb_testutils_streams_ctx_t *ctx, unsigned nstreams,
684  const usb_testutils_transfer_type_t xfr_types[], uint32_t num_bytes,
685  usbdev_stream_flags_t flags, bool verbose) {
686  TRY_CHECK(nstreams <= USBUTILS_STREAMS_MAX);
687  TRY_CHECK(nstreams <= UINT8_MAX);
688 
689  // Initialize the state of each stream
690  for (uint8_t id = 0U; id < nstreams; id++) {
691  // Which endpoint are we using for the IN transfers to the host?
692  const uint8_t ep_in = id + 1U;
693  // Which endpoint are we using for the OUT transfers from the host?
694  const uint8_t ep_out = id + 1U;
695  TRY(usb_testutils_stream_init(ctx, id, xfr_types[id], ep_in, ep_out,
696  num_bytes, flags, verbose));
697  }
698 
699  // Remember the stream count and apportion the available tx buffers
700  TRY_CHECK(usb_testutils_streams_count_set(ctx, nstreams));
701 
702  return OK_STATUS();
703 }
704 
705 status_t usb_testutils_streams_typed_init(
706  usb_testutils_streams_ctx_t *ctx, uint8_t *cfg, uint16_t len,
707  unsigned nstreams, const usb_testutils_transfer_type_t xfr_types[],
708  uint32_t num_bytes, usbdev_stream_flags_t flags, bool verbose,
709  uint32_t *types) {
710  TRY_CHECK(nstreams <= USBUTILS_STREAMS_MAX);
711 
712  // Does the caller require a bitmap of the transfer type(s) collected?
713  if (types) {
714  *types = 0U;
715  }
716 
717  // Total length of the configuration descriptor; validate caller buffer.
718  size_t cfg_len = USB_CFG_DSCR_LEN +
719  nstreams * (USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN);
720  TRY_CHECK(cfg && cfg_len <= len);
721 
722  // Configuration Descriptor header.
723  uint8_t head[CFG_DSCR_LEN_MAX] = {
724  USB_CFG_DSCR_HEAD((uint16_t)cfg_len, (uint8_t)nstreams)};
725  memcpy(cfg, head, USB_CFG_DSCR_LEN);
726  cfg += USB_CFG_DSCR_LEN;
727 
728  // Followed by programmatically-generated list of interface descriptors.
729  for (uint8_t id = 0U; id < nstreams; id++) {
730  usb_testutils_transfer_type_t xfr_type = xfr_types[id];
731  // Return to the caller the transfer type of each stream in turn.
732  if (types) {
733  *types |= xfr_type << (id * 2U);
734  }
735 
736  uint8_t ep_in = (uint8_t)(id + 1U);
737  uint8_t ep_out = (uint8_t)(id + 1U);
738  TRY(usb_testutils_stream_init(ctx, id, xfr_type, ep_in, ep_out, num_bytes,
739  flags, verbose));
740 
741  // Isochronous and Interrupt endpoints require a bInterval value of 1.
742  uint8_t bInterval = (xfr_type == kUsbTransferTypeIsochronous ||
743  xfr_type == kUsbTransferTypeInterrupt);
744 
745  // Description of a single interface.
746  uint8_t int_dscr[USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN] = {
747  VEND_INTERFACE_DSCR(id, 2, 0x50, 1),
748  USB_EP_DSCR(0, ep_out, (uint8_t)xfr_type, USBDEV_MAX_PACKET_SIZE,
749  bInterval),
750  USB_EP_DSCR(1, ep_in, (uint8_t)xfr_type, USBDEV_MAX_PACKET_SIZE,
751  bInterval),
752  };
753 
754  // Append interface descriptor to the configuration descriptor.
755  memcpy(cfg, int_dscr, sizeof(int_dscr));
756  cfg += sizeof(int_dscr);
757 
758  if (verbose) {
759  /**
760  * Indexed by usb_testutils_transfer_type_t
761  */
762  static const char *xfr_name[] = {
763  "Control",
764  "Isochronous",
765  "Bulk",
766  "Interrupt",
767  };
768  TRY_CHECK(xfr_type < ARRAYSIZE(xfr_name));
769  LOG_INFO("S%u: IN %u:OUT %u : %s - 0x%x bytes flags 0x%x", id, ep_in,
770  ep_out, xfr_name[xfr_type], num_bytes, flags);
771  }
772  }
773 
774  // Remember the stream count and apportion the available tx buffers
775  TRY_CHECK(usb_testutils_streams_count_set(ctx, nstreams));
776 
777  return OK_STATUS();
778 }
779 
780 status_t usb_testutils_streams_service(usb_testutils_streams_ctx_t *ctx) {
781  TRY_CHECK(ctx->nstreams <= UINT8_MAX);
782 
783  for (uint8_t id = 0U; id < ctx->nstreams; id++) {
784  TRY(usb_testutils_stream_service(ctx, id));
785 
786  // We must keep polling regularly in order to handle detection of packet
787  // transmission as well as perform packet reception and checking
788  CHECK_STATUS_OK(usb_testutils_poll(ctx->usbdev));
789  }
790  return OK_STATUS();
791 }
792 
793 status_t usb_testutils_streams_suspend(usb_testutils_streams_ctx_t *ctx,
794  uint8_t *buf, unsigned size,
795  unsigned *used) {
796  // Validate arguments.
797  if (!ctx || !buf || !used ||
798  size < 1U + ctx->nstreams * sizeof(usbdev_stream_t)) {
799  return INVALID_ARGUMENT();
800  }
801 
802  // Store the stream count
803  uint8_t *dp = buf;
804  *dp++ = ctx->nstreams;
805 
806  // Store the state of each stream
807  for (uint8_t id = 0U; id < ctx->nstreams; id++) {
808  // We can just save the entire stream context as-is
809  const usbdev_stream_t *s = &ctx->streams[id];
810  memcpy(dp, s, sizeof(*s));
811  dp += sizeof(*s);
812  if (s->verbose) {
813  LOG_INFO("S%u: suspend seq %u, lfsr %x <- commit seq %u, lfsr %x", id,
814  s->tx.seq, s->tx.lfsr, s->tx_cmt.seq, s->tx_cmt.lfsr);
815  }
816  }
817 
818  *used = (unsigned)(dp - buf);
819 
820  // Note: we rely upon all packet reception having occurred, such that there
821  // are no residual OUT data packets in the USB device. There is necessarily a
822  // period of at least 4ms of Suspending/Suspended state before we save the
823  // state for Sleep.
824  // In practice it is likely to be much longer than that.
825 
826  return OK_STATUS();
827 }
828 
829 status_t usb_testutils_streams_resume(usb_testutils_streams_ctx_t *ctx,
830  const uint8_t *data, unsigned len) {
831  // Validate arguments.
832  if (!ctx || !data || len < 1U) {
833  return INVALID_ARGUMENT();
834  }
835 
836  // We expect only to be resuming immediately after initialization, and with no
837  // buffers already in use.
838  if (ctx->tx_queued_total) {
839  return FAILED_PRECONDITION();
840  }
841 
842  // Read the stream count and then check that we have enough supplied data.
843  const uint8_t *dp = data;
844  unsigned nstreams = *dp++;
845  if (len < 1U + nstreams * sizeof(usbdev_stream_t)) {
846  return INVALID_ARGUMENT();
847  }
848 
849  for (uint8_t id = 0U; id < nstreams; id++) {
850  // Load the entire stream context as-is...
851  usbdev_stream_t *s = &ctx->streams[id];
852  memcpy(&s->id, dp, sizeof(*s));
853  if (s->verbose) {
854  LOG_INFO("S%u: resume seq %u, lfsr %x <- commit seq %u, lfsr %x", id,
855  s->tx.seq, s->tx.lfsr, s->tx_cmt.seq, s->tx_cmt.lfsr);
856  }
857  // ...but now we need to rewind the state to the latest 'commit' point,
858  // reflecting the most recent point of data transmission at the USB device,
859  // rather than the point to which the packet creation had run ahead.
860  s->tx = s->tx_cmt;
861  dp += sizeof(*s);
862  }
863 
864  // Remember the stream count and apportion the available tx buffers
865  TRY_CHECK(usb_testutils_streams_count_set(ctx, nstreams));
866 
867  return OK_STATUS();
868 }
869 
870 status_t usb_testutils_stream_status(usb_testutils_streams_ctx_t *ctx,
871  uint8_t id, uint32_t *num_bytes,
872  uint32_t *tx_bytes, uint32_t *rx_bytes) {
873  // Check the stream IDentifier.
874  if (id >= ctx->nstreams) {
875  return INVALID_ARGUMENT();
876  }
877 
878  // Return the requested information.
879  const usbdev_stream_t *s = &ctx->streams[id];
880  if (num_bytes) {
881  *num_bytes = s->transfer_bytes;
882  }
883  if (tx_bytes) {
884  *tx_bytes = s->tx.bytes;
885  }
886  if (rx_bytes) {
887  *rx_bytes = s->rx_bytes;
888  }
889 
890  return OK_STATUS();
891 }
892 
893 bool usb_testutils_streams_completed(const usb_testutils_streams_ctx_t *ctx) {
894  // See whether any streams still have more work to do
895  for (uint8_t id = 0; id < ctx->nstreams; id++) {
896  if (!usb_testutils_stream_completed(ctx, id)) {
897  return false;
898  }
899  }
900  return true;
901 }