• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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