48 #include "stream_test.h"
64 #include "usb_device.h"
66 #include "usbdev_int.h"
67 #include "usbdev_iso.h"
69 #include "usbdev_serial.h"
70 #include "usbdev_utils.h"
80 constexpr uint32_t kTransferBytes = (0x10U << 20);
83 bool received =
false;
86 uint64_t start_time = 0U;
100 static bool GetBool(
const char *p);
103 static void PortNext(
char *next,
size_t n,
const char *curr);
106 bool GetBool(
const char *p) {
107 return (*p ==
'1') || (tolower(*p) ==
'y') || (*p ==
'\r') || (*p ==
'\n') ||
113 bool GetByte(
const char **pp, uint8_t &
byte) {
118 n = (n * 10) + *p++ -
'0';
119 }
while (n < 0x100u && isdigit(*p));
130 bool GetDevice(
const char *p, uint8_t &busNumber, uint8_t &devAddress) {
131 return GetByte(&p, busNumber) && (*p++ ==
':') && GetByte(&p, devAddress) &&
136 void PortNext(
char *next,
size_t n,
const char *curr) {
139 strncpy(next, curr, n);
141 while (*next !=
'\0') {
142 if (isdigit(*next)) {
143 int port = atoi(next);
144 snprintf(next, n,
"%u", (
unsigned)port + 1U);
153 void ReportSyntax(
void) {
156 " stream [-n<streams>][-v<bool>][-c<bool>][-r<bool>][-s<bool>][-t][-z]\n"
157 " [[-d<bus>:<address>] | [--device <bus>:<address>]]\n"
158 " [<input port>[ <output port>]]"
160 " --device programmatically specify a particular USB device by bus\n"
161 " number and device address (see 'lsusb' output).\n"
163 " -c check any retrieved data against expectations\n"
164 " -d specify a particular USB device by bus number"
165 " and device address\n"
166 " -r retrieve data from device\n"
167 " -s send data to device\n"
168 " -t use serial ports (ttyUSBx) in preference to libusb Bulk\n"
169 " Transfer streams for usbdev_stream_test\n"
170 " -v verbose reporting\n"
171 " -z perform suspend-resume signaling throughout the test"
173 " <bool> values may be 0,1,n or y, and they default to 1\n",
177 static int RunTest(
USBDevice *dev,
const char *in_port,
const char *out_port) {
179 char out_name[FILENAME_MAX];
180 char in_name[FILENAME_MAX];
186 for (
unsigned arg = 0U; arg < 4U; arg++) {
187 testArg[arg] = dev->
TestArg(arg);
192 unsigned nstreams = 2U;
194 case USBDevice::kUsbTestNumberStreams:
195 case USBDevice::kUsbTestNumberIso:
196 case USBDevice::kUsbTestNumberMixed:
199 nstreams = testArg[0] & 0xfU;
208 uint32_t transfer_bytes = kTransferBytes;
209 transfer_bytes = (transfer_bytes + nstreams - 1) / nstreams;
211 std::cout <<
" - " << nstreams <<
" stream(s), 0x" << std::hex
212 << transfer_bytes << std::dec <<
" bytes each" << std::endl;
216 for (
unsigned idx = 0U; idx < nstreams; idx++) {
220 case USBDevice::kUsbTestNumberStreams:
229 streamType = USBDevStream::StreamType_Serial;
231 streamType = USBDevStream::StreamType_Bulk;
234 case USBDevice::kUsbTestNumberIso:
235 streamType = USBDevStream::StreamType_Isochronous;
237 case USBDevice::kUsbTestNumberMixed: {
238 uint32_t mixedTypes =
239 (testArg[3] << 16) | (testArg[2] << 8) | testArg[1];
242 switch ((mixedTypes >> (idx * 2)) & 3U) {
244 streamType = USBDevStream::StreamType_Control;
247 streamType = USBDevStream::StreamType_Isochronous;
250 streamType = USBDevStream::StreamType_Bulk;
253 streamType = USBDevStream::StreamType_Interrupt;
259 streamType = USBDevStream::StreamType_Bulk;
267 #if STREAMTEST_LIBUSB
270 switch (streamType) {
271 case USBDevStream::StreamType_Serial: {
276 opened = s->
Open(in_port, out_port);
281 PortNext(out_name,
sizeof(out_name), out_port);
282 PortNext(in_name,
sizeof(in_name), in_port);
289 #if STREAMTEST_LIBUSB
290 case USBDevStream::StreamType_Isochronous: {
295 opened = iso->
Open(idx);
302 case USBDevStream::StreamType_Interrupt:
306 case USBDevStream::StreamType_Bulk: {
311 opened = interrupt->
Open(idx);
313 streams[idx] = interrupt;
319 assert(!
"Unrecognised/invalid stream type");
324 std::cerr <<
"Failed to open stream" << std::endl;
335 std::cout <<
"Streaming..." << std::endl;
338 constexpr uint32_t kRunInterval = 5 * 1000000;
339 constexpr uint32_t kSuspendingInterval = 5 * 1000;
340 constexpr uint32_t kSuspendedInterval = 5 * 1000000;
343 constexpr uint32_t kResumeInterval = 30 * 1000;
344 uint64_t start_time = time_us();
345 uint32_t prev_bytes = 0;
348 uint32_t total_bytes = 0U;
349 uint32_t total_recv = 0U;
350 uint32_t total_sent = 0U;
355 case USBDevice::StateStreaming:
357 for (
unsigned idx = 0U; idx < nstreams; idx++) {
359 if (!streams[idx]->Service()) {
370 if (!streams[idx]->Completed()) {
376 if (cfg.
suspending && elapsed_time(start_time) >= kRunInterval) {
377 std::cout <<
"Waiting to suspend" << std::endl;
379 for (
unsigned idx = 0U; idx < nstreams; idx++) {
380 streams[idx]->
Pause();
386 start_time = time_us();
397 std::cout <<
"Attempting to resume" << std::endl;
400 for (
unsigned idx = 0U; idx < nstreams; idx++) {
403 std::cout <<
"Resuming streaming..." << std::endl;
405 start_time = time_us();
411 case USBDevice::StateSuspending:
412 if (elapsed_time(start_time) >= kSuspendingInterval) {
413 dev->
SetState(USBDevice::StateSuspended);
415 start_time = time_us();
416 std::cout <<
"Suspended" << std::endl;
420 case USBDevice::StateSuspended:
421 if (elapsed_time(start_time) >= kSuspendedInterval) {
424 start_time = time_us();
428 case USBDevice::StateResuming:
429 if (elapsed_time(start_time) >= kResumeInterval) {
430 for (
unsigned idx = 0U; idx < nstreams; idx++) {
434 dev->
SetState(USBDevice::StateStreaming);
436 start_time = time_us();
448 for (
unsigned idx = 0U; idx < nstreams; idx++) {
449 (void)streams[idx]->Stop();
455 if (std::abs((int32_t)total_sent - (int32_t)prev_bytes) >= 0x1000 || done) {
459 uint32_t bytes_left =
460 (total_sent < total_bytes) ? (total_bytes - total_sent) : 0U;
461 std::cout <<
"Bytes received: 0x" << std::hex << total_recv
462 <<
" -- Left to send: 0x" << bytes_left <<
" "
463 << std::dec << std::endl;
464 prev_bytes = total_sent;
468 uint64_t elapsed_time = time_us() - start_time;
471 for (
unsigned idx = 0U; idx < nstreams; idx++) {
472 streams[idx]->
Stop();
478 double elapsed_secs = elapsed_time / 1e6;
479 printf(
"Test completed in %.2lf seconds (%" PRIu64
"us)\n", elapsed_secs,
485 int main(
int argc,
char *argv[]) {
486 const uint16_t kVendorID = 0x18d1u;
487 const uint16_t kProductID = 0x503au;
488 const char *out_port =
nullptr;
489 const char *in_port =
nullptr;
490 uint8_t devAddress = 0u;
491 uint8_t busNumber = 0u;
496 for (
int i = 1; i < argc; i++) {
497 if (argv[i][0] ==
'-') {
498 switch (tolower(argv[i][1])) {
500 cfg.
check = GetBool(&argv[i][2]);
504 if (!GetDevice(&argv[i][2], busNumber, devAddress)) {
505 std::cerr <<
"ERROR: Unrecognised option '" << argv[i] <<
"'"
512 cfg.
retrieve = GetBool(&argv[i][2]);
516 cfg.
send = GetBool(&argv[i][2]);
520 cfg.
serial = GetBool(&argv[i][2]);
523 cfg.
verbose = GetBool(&argv[i][2]);
531 if (!strcmp(&argv[i][2],
"device") && i < argc - 1) {
533 if (GetDevice(argv[++i], busNumber, devAddress)) {
539 std::cerr <<
"ERROR: Unrecognised option '" << argv[i] <<
"'"
544 }
else if (!out_port) {
546 }
else if (!in_port) {
549 std::cerr <<
"ERROR: Parameter '" << argv[i] <<
"' unrecognised"
558 out_port =
"/dev/ttyUSB0";
561 in_port =
"/dev/ttyUSB0";
564 std::cout <<
"USB Streaming Test" << std::endl
565 <<
" (host-side implementation of usbdev streaming tests)"
572 if (!dev.
Init(kVendorID, kProductID, devAddress, busNumber)) {
588 int rc = RunTest(&dev, in_port, out_port);