Home | History | Annotate | Download | only in src
      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 
     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
     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 
    109 std::unique_ptr<TestHarness> TestHarness::MakeUnique() {
    110   return std::unique_ptr<TestHarness>(new TestHarness());
    111 }
    112 
    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 
    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 
    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 
    152 bool TestHarness::ttyState() const {
    153   return tty_fd != -1;
    154 }
    155 
    156 int TestHarness::getVerbosity() const {
    157   return verbosity;
    158 }
    159 
    160 int TestHarness::setVerbosity(int v) {
    161   int temp = verbosity;
    162   verbosity = v;
    163   return temp;
    164 }
    165 
    166 void TestHarness::flushConsole() {
    167 #ifndef CONFIG_NO_UART
    168   while (ReadLineUntilBlock().size() > 0) {}
    169 #endif  // CONFIG_NO_UART
    170 }
    171 
    172 bool TestHarness::RebootNugget() {
    173   return nugget_tools::RebootNugget(client.get());
    174 }
    175 
    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 
    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
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    494 bool TestHarness::UsingSpi() const {
    495   return !FLAGS_util_use_ahdlc;
    496 }
    497 
    498 #ifndef CONFIG_NO_UART
    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 
    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 
    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 
    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 
    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 
    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 
    752 void FatalError(const string& msg) {
    753   std::cerr << "FATAL ERROR: " << msg << std::endl;
    754   exit(1);
    755 }
    756 
    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