1 #include "src/util.h"
2
3 #include <dirent.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6
7 #include <algorithm>
8 #include <cstring>
9 #include <sstream>
10 #include <thread>
11
12 #include <application.h>
13
14 #include "nugget_tools.h"
15 #include "nugget/app/protoapi/control.pb.h"
16 #include "nugget/app/protoapi/header.pb.h"
17
18 #ifndef CONFIG_NO_UART
19 #include "src/lib/inc/crc_16.h"
20 #endif // CONFIG_NO_UART
21
22 #ifdef ANDROID
23 #define FLAGS_util_use_ahdlc false
24 #define FLAGS_util_print_uart false
25 #else
26 #include "gflags/gflags.h"
27
28 DEFINE_bool(util_use_ahdlc, false, "Use aHDLC over UART instead of SPI.");
29 DEFINE_bool(util_print_uart, false, "Print the output of citadel UART.");
30 DEFINE_string(util_verbosity, "ERROR", "One of SILENT, CRITICAL, ERROR, WARNING, or INFO.");
31 #endif // ANDROID
32
33 using nugget::app::protoapi::APImessageID;
34 using nugget::app::protoapi::ControlRequest;
35 using nugget::app::protoapi::ControlRequestType;
36 using nugget::app::protoapi::Notice;
37 using std::chrono::duration;
38 using std::chrono::duration_cast;
39 using std::chrono::high_resolution_clock;
40 using std::chrono::microseconds;
41
42 namespace test_harness {
43 namespace {
44
GetVerbosityFromFlag()45 int GetVerbosityFromFlag() {
46 #ifdef ANDROID
47 return TestHarness::ERROR;
48 #else
49 std::string upper_case_flag;
50 upper_case_flag.reserve(FLAGS_util_verbosity.size());
51 std::transform(FLAGS_util_verbosity.begin(), FLAGS_util_verbosity.end(),
52 std::back_inserter(upper_case_flag), toupper);
53
54 if (upper_case_flag == "SILENT")
55 return TestHarness::SILENT;
56 if (upper_case_flag == "CRITICAL")
57 return TestHarness::CRITICAL;
58 if (upper_case_flag == "WARNING")
59 return TestHarness::WARNING;
60 if (upper_case_flag == "INFO")
61 return TestHarness::INFO;
62
63 // Default to ERROR.
64 return TestHarness::ERROR;
65 #endif // ANDROID
66 }
67
68 #ifndef ANDROID
find_uart(int verbosity)69 string find_uart(int verbosity) {
70 constexpr char dir_path[] = "/dev/";
71 auto dir = opendir(dir_path);
72 if (!dir) {
73 return "";
74 }
75
76 string manual_serial_no = nugget_tools::GetCitadelUSBSerialNo();
77 const char prefix[] = "ttyUltraTarget_";
78
79 string return_value = "";
80 if (manual_serial_no.empty()) {
81 const size_t prefix_length = sizeof(prefix) / sizeof(prefix[0]) - 1;
82 while (auto listing = readdir(dir)) {
83 // The following is always true so it is not checked:
84 // sizeof(listing->d_name) >= sizeof(prefix)
85 if (std::equal(prefix, prefix + prefix_length, listing->d_name)) {
86 return_value = string(dir_path) + listing->d_name;
87 break;
88 }
89 }
90 } else {
91 return_value = string(dir_path) + prefix + manual_serial_no;
92 }
93
94 if (verbosity >= TestHarness::VerbosityLevels::INFO) {
95 if (return_value.empty()) {
96 std::cout << "UltraDebug UART not found" << std::endl;
97 } else {
98 std::cout << "USING: " << return_value << std::endl;
99 }
100 }
101
102 closedir(dir);
103 return return_value;
104 }
105 #endif // ANDROID
106
107 } // namespace
108
MakeUnique()109 std::unique_ptr<TestHarness> TestHarness::MakeUnique() {
110 return std::unique_ptr<TestHarness>(new TestHarness());
111 }
112
TestHarness()113 TestHarness::TestHarness() : verbosity(GetVerbosityFromFlag()),
114 output_buffer(PROTO_BUFFER_MAX_LEN, 0),
115 input_buffer(PROTO_BUFFER_MAX_LEN, 0), tty_fd(-1) {
116 #ifdef CONFIG_NO_UART
117 Init(nullptr);
118 #else
119 string path = find_uart(verbosity);
120 Init(path.c_str());
121 #endif // CONFIG_NO_UART
122 }
123
TestHarness(const char * path)124 TestHarness::TestHarness(const char* path) :
125 verbosity(ERROR), output_buffer(PROTO_BUFFER_MAX_LEN, 0),
126 input_buffer(PROTO_BUFFER_MAX_LEN, 0), tty_fd(-1) {
127 Init(path);
128 }
129
~TestHarness()130 TestHarness::~TestHarness() {
131 #ifndef CONFIG_NO_UART
132 if (verbosity >= INFO) {
133 std::cout << "CLOSING TEST HARNESS" << std::endl;
134 }
135 if (ttyState()) {
136 auto temp = tty_fd;
137 tty_fd = -1;
138 close(temp);
139 }
140 if (print_uart_worker) {
141 print_uart_worker->join();
142 print_uart_worker = nullptr;
143 }
144 #endif // CONFIG_NO_UART
145
146 if (client) {
147 client->Close();
148 client = unique_ptr<nos::NuggetClientInterface >();
149 }
150 }
151
ttyState() const152 bool TestHarness::ttyState() const {
153 return tty_fd != -1;
154 }
155
getVerbosity() const156 int TestHarness::getVerbosity() const {
157 return verbosity;
158 }
159
setVerbosity(int v)160 int TestHarness::setVerbosity(int v) {
161 int temp = verbosity;
162 verbosity = v;
163 return temp;
164 }
165
flushConsole()166 void TestHarness::flushConsole() {
167 #ifndef CONFIG_NO_UART
168 while (ReadLineUntilBlock().size() > 0) {}
169 #endif // CONFIG_NO_UART
170 }
171
RebootNugget()172 bool TestHarness::RebootNugget() {
173 return nugget_tools::RebootNugget(client.get());
174 }
175
print_bin(std::ostream & out,uint8_t c)176 void print_bin(std::ostream &out, uint8_t c) {
177 if (c == '\\') {
178 out << "\\\\";
179 } else if (isprint(c)) {
180 out << c;
181 } else if (c < 16) {
182 out << "\\x0" << std::hex << (uint32_t) c;
183 } else {
184 out << "\\x" << std::hex << (uint32_t) c;
185 }
186 }
187
SendData(const raw_message & msg)188 int TestHarness::SendData(const raw_message& msg) {
189 #ifdef CONFIG_NO_UART
190 return SendSpi(msg);
191 #else
192 return FLAGS_util_use_ahdlc ? SendAhdlc(msg) : SendSpi(msg);
193 #endif // ANDROID
194 }
195
196 #ifndef CONFIG_NO_UART
SendAhdlc(const raw_message & msg)197 int TestHarness::SendAhdlc(const raw_message& msg) {
198 if (EncodeNewFrame(&encoder) != AHDLC_OK) {
199 return TRANSPORT_ERROR;
200 }
201
202 if (EncodeAddByteToFrameBuffer(&encoder, (uint8_t) (msg.type >> 8))
203 != AHDLC_OK || EncodeAddByteToFrameBuffer(&encoder, (uint8_t) msg.type)
204 != AHDLC_OK) {
205 return TRANSPORT_ERROR;
206 }
207 if (EncodeBuffer(&encoder, msg.data, msg.data_len) != AHDLC_OK) {
208 return TRANSPORT_ERROR;
209 }
210
211 BlockingWrite((const char*) encoder.frame_buffer,
212 encoder.frame_info.buffer_index);
213 return NO_ERROR;
214 }
215 #endif // CONFIG_NO_UART
216
SendSpi(const raw_message & msg)217 int TestHarness::SendSpi(const raw_message& msg) {
218 if (!client) {
219 client = nugget_tools::MakeNuggetClient();
220 client->Open();
221 if (!client->IsOpen()) {
222 FatalError("Unable to connect");
223 }
224 }
225
226 input_buffer.resize(msg.data_len + sizeof(msg.type));
227 input_buffer[0] = msg.type >> 8;
228 input_buffer[1] = (uint8_t) msg.type;
229 std::copy(msg.data, msg.data + msg.data_len, input_buffer.begin() + 2);
230
231 if (verbosity >= INFO) {
232 std::cout << "SPI_TX: ";
233 for (char c : input_buffer) {
234 if (c == '\n') {
235 std::cout << "\nSPI_TX: ";
236 } else {
237 print_bin(std::cout, c);
238 }
239 }
240 std::cout << "\n";
241 std::cout.flush();
242 }
243
244 output_buffer.resize(output_buffer.capacity());
245 return client->CallApp(APP_ID_PROTOBUF, msg.type, input_buffer,
246 &output_buffer);
247 }
248
SendOneofProto(uint16_t type,uint16_t subtype,const google::protobuf::Message & message)249 int TestHarness::SendOneofProto(uint16_t type, uint16_t subtype,
250 const google::protobuf::Message& message) {
251 test_harness::raw_message msg;
252 msg.type = type;
253 int msg_size = message.ByteSize();
254 if (msg_size + 2 > (int) PROTO_BUFFER_MAX_LEN) {
255 return OVERFLOW_ERROR;
256 }
257 msg.data[0] = subtype >> 8;
258 msg.data[1] = (uint8_t) subtype;
259
260 msg.data_len = (uint16_t) (msg_size + 2);
261 if (!message.SerializeToArray(msg.data + 2, msg_size)) {
262 return SERIALIZE_ERROR;
263 }
264
265 auto return_value = SendData(msg);
266 return return_value;
267 }
268
SendProto(uint16_t type,const google::protobuf::Message & message)269 int TestHarness::SendProto(uint16_t type,
270 const google::protobuf::Message& message) {
271 test_harness::raw_message msg;
272 msg.type = type;
273 int msg_size = message.ByteSize();
274 if (msg_size > (int) (PROTO_BUFFER_MAX_LEN - 2)) {
275 return OVERFLOW_ERROR;
276 }
277 msg.data_len = (uint16_t) msg_size;
278 if (!message.SerializeToArray(msg.data, msg.data_len)) {
279 return SERIALIZE_ERROR;
280 }
281
282 auto return_value = SendData(msg);
283 return return_value;
284 }
285
286 #ifndef CONFIG_NO_UART
GetAhdlc(raw_message * msg,microseconds timeout)287 int TestHarness::GetAhdlc(raw_message* msg, microseconds timeout) {
288 if (verbosity >= INFO) {
289 std::cout << "RX: ";
290 }
291 size_t read_count = 0;
292 while (true) {
293 uint8_t read_value;
294 auto start = high_resolution_clock::now();
295 while (read(tty_fd, &read_value, 1) <= 0) {
296 if (timeout >= microseconds(0) &&
297 duration_cast<microseconds>(high_resolution_clock::now() - start) >
298 microseconds(timeout)) {
299 if (verbosity >= INFO) {
300 std::cout << "\n";
301 std::cout.flush();
302 }
303 return TIMEOUT;
304 }
305 }
306 ++read_count;
307
308 ahdlc_op_return return_value =
309 DecodeFrameByte(&decoder, read_value);
310
311 if (verbosity >= INFO) {
312 if (read_value == '\n') {
313 std::cout << "\nRX: ";
314 } else {
315 print_bin(std::cout, read_value);
316 }
317 std::cout.flush();
318 }
319
320 if (read_count > 7) {
321 if (return_value == AHDLC_COMPLETE ||
322 decoder.decoder_state == DECODE_COMPLETE_BAD_CRC) {
323 if (decoder.frame_info.buffer_index < 2) {
324 if (verbosity >= ERROR) {
325 std::cout << "\n";
326 std::cout << "UNDERFLOW ERROR\n";
327 std::cout.flush();
328 }
329 return TRANSPORT_ERROR;
330 }
331
332 msg->type = (decoder.pdu_buffer[0] << 8) | decoder.pdu_buffer[1];
333 msg->data_len = decoder.frame_info.buffer_index - 2;
334 std::copy(decoder.pdu_buffer + 2,
335 decoder.pdu_buffer + decoder.frame_info.buffer_index,
336 msg->data);
337
338 if (verbosity >= INFO) {
339 std::cout << "\n";
340 if (return_value == AHDLC_COMPLETE) {
341 std::cout << "GOOD CRC\n";
342 } else {
343 std::cout << "BAD CRC\n";
344 }
345 std::cout.flush();
346 }
347 return NO_ERROR;
348 } else if (decoder.decoder_state == DECODE_COMPLETE_BAD_CRC) {
349 if (verbosity >= ERROR) {
350 std::cout << "\n";
351 std::cout << "AHDLC BAD CRC\n";
352 std::cout.flush();
353 }
354 return TRANSPORT_ERROR;
355 } else if (decoder.frame_info.buffer_index >= PROTO_BUFFER_MAX_LEN) {
356 if (AhdlcDecoderInit(&decoder, CRC16, NULL) != AHDLC_OK) {
357 FatalError("AhdlcDecoderInit()");
358 }
359 if (verbosity >= ERROR) {
360 std::cout << "\n";
361 std::cout.flush();
362 std::cout << "OVERFLOW ERROR\n";
363 }
364 return OVERFLOW_ERROR;
365 }
366 }
367 }
368 }
369 #endif // CONFIG_NO_UART
370
GetSpi(raw_message * msg,microseconds timeout)371 int TestHarness::GetSpi(raw_message* msg, microseconds timeout) {
372 if (timeout > microseconds(0)) {} // Prevent unused parameter warning.
373 if (output_buffer.size() < 2) {
374 return GENERIC_ERROR;
375 }
376
377 if (verbosity >= INFO) {
378 std::cout << "SPI_RX: ";
379 for (char c : output_buffer) {
380 if (c == '\n') {
381 std::cout << "\nSPI_RX: ";
382 } else {
383 print_bin(std::cout, c);
384 }
385 }
386 std::cout << "\n";
387 std::cout.flush();
388 }
389
390 msg->type = (output_buffer[0] << 8) | output_buffer[1];
391 msg->data_len = output_buffer.size() - sizeof(msg->type);
392 std::copy(output_buffer.begin() + 2, output_buffer.end(), msg->data);
393 output_buffer.resize(0);
394 return NO_ERROR;
395 }
396
GetData(raw_message * msg,microseconds timeout)397 int TestHarness::GetData(raw_message* msg, microseconds timeout) {
398 #ifdef CONFIG_NO_UART
399 return GetSpi(msg, timeout);
400 #else
401 return FLAGS_util_use_ahdlc ? GetAhdlc(msg, timeout) : GetSpi(msg, timeout);
402 #endif // CONFIG_NO_UART
403 }
404
Init(const char * path)405 void TestHarness::Init(const char* path) {
406 if (verbosity >= INFO) {
407 std::cout << "init() start\n";
408 std::cout.flush();
409 }
410
411 #ifndef CONFIG_NO_UART
412 if (FLAGS_util_use_ahdlc) { // AHDLC UART transport.
413 encoder.buffer_len = output_buffer.size();
414 encoder.frame_buffer = output_buffer.data();
415 if (ahdlcEncoderInit(&encoder, CRC16) != AHDLC_OK) {
416 FatalError("ahdlcEncoderInit()");
417 }
418
419 decoder.buffer_len = input_buffer.size();
420 decoder.pdu_buffer = input_buffer.data();
421 if (AhdlcDecoderInit(&decoder, CRC16, NULL) != AHDLC_OK) {
422 FatalError("AhdlcDecoderInit()");
423 }
424 }
425
426 // Setup UART
427 errno = 0;
428 tty_fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
429 if (errno != 0) {
430 perror("ERROR open()");
431 FatalError("Cannot open debug UART to Citadel chip. Is UltraDebug board connected?");
432 }
433 errno = 0;
434
435 if (!isatty(tty_fd)) {
436 FatalError("Path is not a tty");
437 }
438
439 if (tcgetattr(tty_fd, &tty_state)) {
440 perror("ERROR tcgetattr()");
441 FatalError("");
442 }
443
444 if (cfsetospeed(&tty_state, B115200) ||
445 cfsetispeed(&tty_state, B115200)) {
446 perror("ERROR cfsetospeed()");
447 FatalError("");
448 }
449
450 tty_state.c_cc[VMIN] = 0;
451 tty_state.c_cc[VTIME] = 0;
452
453 tty_state.c_iflag = tty_state.c_iflag & ~(IXON | ISTRIP | INPCK | PARMRK |
454 INLCR | ICRNL | BRKINT | IGNBRK);
455 tty_state.c_iflag = 0;
456 tty_state.c_oflag = 0;
457 tty_state.c_lflag = tty_state.c_lflag & ~(ECHO | ECHONL | ICANON | IEXTEN |
458 ISIG);
459 tty_state.c_cflag = (tty_state.c_cflag & ~(CSIZE | PARENB)) | CS8;
460
461 if (tcsetattr(tty_fd, TCSAFLUSH, &tty_state)) {
462 perror("ERROR tcsetattr()");
463 FatalError("");
464 }
465 #else
466 if (path) {} // Prevent the unused variable warning for path.
467 #endif // CONFIG_NO_UART
468
469 // libnos SPI transport is initialized on first use for interoperability.
470
471 if (verbosity >= INFO) {
472 std::cout << "init() finish\n";
473 std::cout.flush();
474 }
475
476 if (FLAGS_util_print_uart) {
477 print_uart_worker = std::unique_ptr<std::thread>(new std::thread(
478 [](TestHarness* harness){
479 if (harness->getVerbosity() >= INFO) {
480 std::cout << "Citadel UART printing enabled!\n";
481 std::cout.flush();
482 }
483 while(harness->ttyState()) {
484 harness->PrintUntilClosed();
485 }
486 if (harness->getVerbosity() >= INFO) {
487 std::cout << "Citadel UART printing disabled!\n";
488 std::cout.flush();
489 }
490 }, this));
491 }
492 }
493
UsingSpi() const494 bool TestHarness::UsingSpi() const {
495 return !FLAGS_util_use_ahdlc;
496 }
497
498 #ifndef CONFIG_NO_UART
SwitchFromConsoleToProtoApi()499 bool TestHarness::SwitchFromConsoleToProtoApi() {
500 if (verbosity >= INFO) {
501 std::cout << "SwitchFromConsoleToProtoApi() start\n";
502 std::cout.flush();
503 }
504
505 if (!ttyState()) { return false; }
506
507 ReadUntil(BYTE_TIME * 1024);
508
509 BlockingWrite("version\n", 1);
510
511 ReadUntil(BYTE_TIME * 1024);
512
513 BlockingWrite("\n", 1);
514
515 while (ReadLineUntilBlock() != "> ") {}
516
517 const char command[] = "protoapi uart on 1\n";
518 BlockingWrite(command, sizeof(command) - 1);
519
520 ReadUntil(BYTE_TIME * 1024);
521
522 if (verbosity >= INFO) {
523 std::cout << "SwitchFromConsoleToProtoApi() finish\n";
524 std::cout.flush();
525 }
526
527 return true;
528 }
529
SwitchFromProtoApiToConsole(raw_message * out_msg)530 bool TestHarness::SwitchFromProtoApiToConsole(raw_message* out_msg) {
531 if (verbosity >= INFO) {
532 std::cout << "SwitchFromProtoApiToConsole() start\n";
533 std::cout.flush();
534 }
535
536 ControlRequest controlRequest;
537 controlRequest.set_type(ControlRequestType::REVERT_TO_CONSOLE);
538 string line;
539 controlRequest.SerializeToString(&line);
540
541 raw_message msg;
542 msg.type = APImessageID::CONTROL_REQUEST;
543
544 std::copy(line.begin(), line.end(), msg.data);
545 msg.data_len = line.size();
546
547 if (SendAhdlc(msg) != error_codes::NO_ERROR) {
548 return false;
549 }
550
551
552 if (GetAhdlc(&msg, 4096 * BYTE_TIME) == NO_ERROR &&
553 msg.type == APImessageID::NOTICE) {
554 Notice message;
555 message.ParseFromArray((char *) msg.data, msg.data_len);
556 if (verbosity >= INFO) {
557 std::cout << message.DebugString() << std::endl;
558 }
559 } else {
560 if (verbosity >= ERROR) {
561 std::cout << "Receive Error" << std::endl;
562 std::cout.flush();
563 }
564 return false;
565 }
566
567 ReadUntil(BYTE_TIME * 4096);
568
569 if (verbosity >= INFO) {
570 std::cout << "SwitchFromProtoApiToConsole() finish\n";
571 std::cout.flush();
572 }
573 if (out_msg) {
574 *out_msg = std::move(msg);
575 }
576 return true;
577 }
578 #endif // CONFIG_NO_UART
579
BlockingWrite(const char * data,size_t len)580 void TestHarness::BlockingWrite(const char* data, size_t len) {
581 if (verbosity >= INFO) {
582 std::cout << "TX: ";
583 for (size_t i = 0; i < len; ++i) {
584 uint8_t value = data[i];
585 if (value == '\n') {
586 std::cout << "\nTX: ";
587 } else {
588 print_bin(std::cout, value);
589 }
590 }
591 std::cout << "\n";
592 std::cout.flush();
593 }
594
595 size_t loc = 0;
596 while (loc < len) {
597 errno = 0;
598 int return_value = write(tty_fd, data + loc, len - loc);
599 if (verbosity >= CRITICAL && errno != 0){
600 perror("ERROR write()");
601 }
602 if (return_value < 0) {
603 if (errno != EWOULDBLOCK && errno != EAGAIN) {
604 FatalError("write(tty_fd,...)");
605 } else {
606 std::this_thread::sleep_for(BYTE_TIME);
607 }
608 } else {
609 loc += return_value;
610 }
611 }
612 }
613
ReadLineUntilBlock()614 string TestHarness::ReadLineUntilBlock() {
615 if (!ttyState()) {
616 return "";
617 }
618
619 string line = "";
620 line.reserve(128);
621 char read_value = ' ';
622 std::stringstream ss;
623
624 auto last_success = high_resolution_clock::now();
625 while (true) {
626 errno = 0;
627 while (read_value != '\n' && read(tty_fd, &read_value, 1) > 0) {
628 last_success = high_resolution_clock::now();
629 print_bin(ss, read_value);
630 line.append(1, read_value);
631 }
632 if (verbosity >= CRITICAL && errno != 0) {
633 perror("ERROR read()");
634 }
635
636 /* If there wasn't anything to read yet, or the end of line is reached
637 * there is no need to continue. */
638 if (read_value == '\n' || line.size() == 0 ||
639 duration_cast<microseconds>(high_resolution_clock::now() -
640 last_success) > 4 * BYTE_TIME) {
641 break;
642 }
643
644 /* Wait for at least one bit time before checking read() again. */
645 std::this_thread::sleep_for(BIT_TIME);
646 }
647
648 if (verbosity >= INFO && line.size() > 0) {
649 std::cout << "RX: " << ss.str() <<"\n";
650 std::cout.flush();
651 }
652 return line;
653 }
654
ReadUntil(microseconds end)655 string TestHarness::ReadUntil(microseconds end) {
656 #ifdef CONFIG_NO_UART
657 std::this_thread::sleep_for(end);
658 return "";
659 #else
660 if (!ttyState()) {
661 return "";
662 }
663
664 char read_value = ' ';
665 bool first = true;
666 std::stringstream ss;
667
668 auto start = high_resolution_clock::now();
669 while (duration_cast<microseconds>(high_resolution_clock::now() -
670 start) < end) {
671 errno = 0;
672 while (read(tty_fd, &read_value, 1) > 0) {
673 ss << read_value;
674 if (verbosity >= INFO) {
675 if (first) {
676 first = false;
677 std::cout << "RX: ";
678 print_bin(std::cout, read_value);
679 } else if (read_value == '\n') {
680 std::cout << "\n";
681 std::cout.flush();
682 std::cout << "RX: ";
683 } else {
684 print_bin(std::cout, read_value);
685 }
686 }
687 }
688 if (verbosity >= CRITICAL && errno != 0) {
689 perror("ERROR read()");
690 }
691
692 /* Wait for at least one bit time before checking read() again. */
693 std::this_thread::sleep_for(BIT_TIME);
694 }
695 if (verbosity >= INFO && !first) {
696 std::cout << "\n";
697 std::cout.flush();
698 }
699
700 return ss.str();
701 #endif // CONFIG_NO_UART
702 }
703
PrintUntilClosed()704 void TestHarness::PrintUntilClosed() {
705 #ifdef CONFIG_NO_UART
706 #else
707 if (!ttyState()) {
708 return;
709 }
710
711 char read_value = ' ';
712 bool first = true;
713 std::stringstream ss("UART: ");
714
715 while (ttyState()) {
716 errno = 0;
717 while (read(tty_fd, &read_value, 1) > 0) {
718 first = false;
719 if (read_value == '\r')
720 continue;
721 if (read_value == '\n') {
722 ss << "\n";
723 std::cout.flush();
724 std::cout << ss.str();
725 std::cout.flush();
726 ss.str("");
727 ss << "UART: ";
728 } else {
729 print_bin(ss, read_value);
730 }
731 }
732 if (verbosity >= CRITICAL && errno != 0 && errno != EAGAIN) {
733 if (errno != EBADF) {
734 perror("ERROR read()");
735 }
736 break;
737 }
738
739 /* Wait for at least one bit time before checking read() again. */
740 std::this_thread::sleep_for(BIT_TIME);
741 }
742 if (!first) {
743 ss << "\n";
744 std::cout.flush();
745 std::cout << ss.str();
746 std::cout.flush();
747 }
748 #endif // CONFIG_NO_UART
749 }
750
751
FatalError(const string & msg)752 void FatalError(const string& msg) {
753 std::cerr << "FATAL ERROR: " << msg << std::endl;
754 exit(1);
755 }
756
error_codes_name(int code)757 const char* error_codes_name(int code) {
758 switch (code) {
759 case error_codes::NO_ERROR:
760 return "NO_ERROR";
761 case error_codes::GENERIC_ERROR:
762 return "GENERIC_ERROR";
763 case error_codes::TIMEOUT:
764 return "TIMEOUT";
765 case error_codes::TRANSPORT_ERROR:
766 return "TRANSPORT_ERROR";
767 case error_codes::OVERFLOW_ERROR:
768 return "OVERFLOW_ERROR";
769 case error_codes::SERIALIZE_ERROR:
770 return "SERIALIZE_ERROR";
771 default:
772 return "unknown";
773 }
774 }
775
776 } // namespace test_harness
777