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