From 7ae8bc4bccac44b14f0f18c5dc9e376ac7514e1e Mon Sep 17 00:00:00 2001 From: folkert van heusden Date: Thu, 16 May 2024 23:00:58 +0200 Subject: [PATCH] rewrote DC11 to use comm --- ESP32/main.ino | 15 +- comm.h | 2 + comm_tcp_socket.h | 10 +- dc11.cpp | 442 +++++----------------------------------------- dc11.h | 42 +---- debugger.cpp | 2 +- main.cpp | 22 ++- 7 files changed, 91 insertions(+), 444 deletions(-) diff --git a/ESP32/main.ino b/ESP32/main.ino index d95fcd5..1142725 100644 --- a/ESP32/main.ino +++ b/ESP32/main.ino @@ -22,6 +22,9 @@ #include "esp_heap_caps.h" #endif +#include "comm.h" +#include "comm_esp32_hardwareserial.h" +#include "comm_tcp_socket.h" #if defined(SHA2017) #include "console_shabadge.h" #else @@ -204,7 +207,7 @@ void start_network(console *const c) dc11_loaded = true; Serial.println(F("* Adding DC11")); - dc11 *dc11_ = new dc11(1100, b); + std::vector comm_interfaces; #if !defined(BUILD_FOR_RP2040) && defined(TTY_SERIAL_RX) uint32_t bitrate = load_serial_speed_configuration(); @@ -212,8 +215,16 @@ void start_network(console *const c) Serial.printf("* Init TTY (on DC11), baudrate: %d bps, RX: %d, TX: %d", bitrate, TTY_SERIAL_RX, TTY_SERIAL_TX); Serial.println(F("")); - dc11_->set_serial(38400 /* bitrate TODO */, TTY_SERIAL_RX, TTY_SERIAL_TX); + comm_interfaces.push_back(new comm_esp32_hardwareserial(1, TTY_SERIAL_RX, TTY_SERIAL_TX, bitrate)); #endif + + for(size_t i=comm_interfaces.size(); i<4; i++) { + int port = 1100 + i; + comm_interfaces.push_back(new comm_tcp_socket(port)); + DOLOG(info, false, "Configuring DC11 device for TCP socket on port %d", port); + } + + dc11 *dc11_ = new dc11(b, comm_interfaces); b->add_DC11(dc11_); Serial.println(F("* Starting (NTP-) clock")); diff --git a/comm.h b/comm.h index cec9a6f..c721058 100644 --- a/comm.h +++ b/comm.h @@ -1,6 +1,8 @@ // (C) 2024 by Folkert van Heusden // Released under MIT license +#pragma once + #include "gen.h" #include #include diff --git a/comm_tcp_socket.h b/comm_tcp_socket.h index be2e6b7..c61b704 100644 --- a/comm_tcp_socket.h +++ b/comm_tcp_socket.h @@ -30,12 +30,12 @@ public: comm_tcp_socket(const int port); virtual ~comm_tcp_socket(); - virtual bool is_connected() = 0; + bool is_connected() override; - virtual bool has_data() = 0; - virtual uint8_t get_byte() = 0; + bool has_data() override; + uint8_t get_byte() override; - virtual void send_data(const uint8_t *const in, const size_t n) = 0; + void send_data(const uint8_t *const in, const size_t n) override; - void operator()(); + void operator()(); }; diff --git a/dc11.cpp b/dc11.cpp index a0a035c..92a5807 100644 --- a/dc11.cpp +++ b/dc11.cpp @@ -5,22 +5,8 @@ #if defined(ESP32) #include #endif -#if defined(ESP32) -#include -#include -#include -#include -#elif defined(_WIN32) -#include -#include -#else -#include -#include -#include -#endif #if IS_POSIX #include -#include #include #include #endif @@ -41,50 +27,11 @@ constexpr const int serial_line = 3; const char *const dc11_register_names[] { "RCSR", "RBUF", "TSCR", "TBUF" }; -static bool setup_telnet_session(const int fd) +dc11::dc11(bus *const b, const std::vector & comm_interfaces): + b(b), + comm_interfaces(comm_interfaces) { - uint8_t dont_auth[] = { 0xff, 0xf4, 0x25 }; - uint8_t suppress_goahead[] = { 0xff, 0xfb, 0x03 }; - uint8_t dont_linemode[] = { 0xff, 0xfe, 0x22 }; - uint8_t dont_new_env[] = { 0xff, 0xfe, 0x27 }; - uint8_t will_echo[] = { 0xff, 0xfb, 0x01 }; - uint8_t dont_echo[] = { 0xff, 0xfe, 0x01 }; - uint8_t noecho[] = { 0xff, 0xfd, 0x2d }; - // uint8_t charset[] = { 0xff, 0xfb, 0x01 }; - - if (write(fd, dont_auth, sizeof dont_auth) != sizeof dont_auth) - return false; - - if (write(fd, suppress_goahead, sizeof suppress_goahead) != sizeof suppress_goahead) - return false; - - if (write(fd, dont_linemode, sizeof dont_linemode) != sizeof dont_linemode) - return false; - - if (write(fd, dont_new_env, sizeof dont_new_env) != sizeof dont_new_env) - return false; - - if (write(fd, will_echo, sizeof will_echo) != sizeof will_echo) - return false; - - if (write(fd, dont_echo, sizeof dont_echo) != sizeof dont_echo) - return false; - - if (write(fd, noecho, sizeof noecho) != sizeof noecho) - return false; - - return true; -} - -dc11::dc11(const int base_port, bus *const b): - base_port(base_port), - b(b) -{ -#if defined(_WIN32) - pfds = new WSAPOLLFD[dc11_n_lines * 2](); -#else - pfds = new pollfd[dc11_n_lines * 2](); -#endif + connected.resize(comm_interfaces.size()); // TODO move to begin() th = new std::thread(std::ref(*this)); @@ -100,26 +47,14 @@ dc11::~dc11() th->join(); delete th; } - - delete [] pfds; - -#if defined(ESP32) - // won't work due to freertos thread -#elif IS_POSIX - close(serial_fd); - - if (serial_th) { - serial_th->join(); - delete serial_th; - } -#endif } void dc11::show_state(console *const cnsl) const { - for(int i=0; i<4; i++) { + for(int i=0; iput_string_lf(format("* LINE %d", i + 1)); +#if 0 // TODO if (i == serial_line) { cnsl->put_string_lf(format(" Serial thread running: %s", serial_thread_running ? "true": "false" )); cnsl->put_string_lf(format(" Serial enabled: %s", serial_enabled ? "true": "false" )); @@ -128,6 +63,7 @@ void dc11::show_state(console *const cnsl) const if (pfds[dc11_n_lines + i].fd != INVALID_SOCKET) cnsl->put_string_lf(" Connected to: " + get_endpoint_name(pfds[dc11_n_lines + i].fd)); } +#endif std::unique_lock lck(input_lock[i]); cnsl->put_string_lf(format(" Characters in buffer: %zu", recv_buffers[i].size())); @@ -137,29 +73,17 @@ void dc11::show_state(console *const cnsl) const } } -void dc11::test_serial(const std::string & txt) const +void dc11::test_port(const size_t nr, const std::string & txt) const { - for(int i=0; isend_data(reinterpret_cast(txt.c_str()), txt.size()); +} - if (write(pfds[dc11_n_lines + i].fd, txt.c_str(), txt.size()) != ssize_t(txt.size())) - DOLOG(warning, false, "DC11 failed to send test string to line %d", i); - } - else { - DOLOG(info, false, "DC11 line %d not connected", i); - } - } +void dc11::test_ports(const std::string & txt) const +{ + for(size_t i=0; i(&listen_addr), sizeof(listen_addr)) == -1) { - close(pfds[i].fd); - pfds[i].fd = INVALID_SOCKET; - - DOLOG(warning, true, "Cannot bind to port %d (DC11)", port); - continue; - } - - if (listen(pfds[i].fd, SOMAXCONN) == -1) { - close(pfds[i].fd); - pfds[i].fd = INVALID_SOCKET; - - DOLOG(warning, true, "Cannot listen on port %d (DC11)", port); - continue; - } - - pfds[i].events = POLLIN; - } - while(!stop_flag) { -#if defined(_WIN32) - int rc = WSAPoll(pfds, dc11_n_lines * 2, 100); -#else - int rc = poll(pfds, dc11_n_lines * 2, 100); -#endif - if (rc == 0) - continue; + myusleep(5000); // TODO replace polling - // accept any new session - for(int i=0; iis_connected(); - int client_i = dc11_n_lines + i; + if (is_connected != connected[line_nr]) { + connected[line_nr] = is_connected; - // disconnect any existing client session - // yes, one can ddos with this - if (pfds[client_i].fd != INVALID_SOCKET) { - close(pfds[client_i].fd); - DOLOG(info, false, "Restarting session for port %d", base_port + i + 1); + if (is_connected) + registers[line_nr * 4 + 0] |= 0160000; // "ERROR", RING INDICATOR, CARRIER TRANSITION + else + registers[line_nr * 4 + 0] |= 0120000; // "ERROR", CARRIER TRANSITION + + if (is_rx_interrupt_enabled(line_nr)) + trigger_interrupt(line_nr, false); } - pfds[client_i].fd = accept(pfds[i].fd, nullptr, nullptr); + // receive data + bool have_data = false; + while(comm_interfaces.at(line_nr)->has_data()) { + uint8_t buffer = comm_interfaces.at(line_nr)->get_byte(); - if (setup_telnet_session(pfds[client_i].fd) == false) { - close(pfds[client_i].fd); - pfds[client_i].fd = INVALID_SOCKET; + std::unique_lock lck(input_lock[line_nr]); + recv_buffers[line_nr].push_back(char(buffer)); + + have_data = true; } - if (pfds[client_i].fd != INVALID_SOCKET) { - set_nodelay(pfds[client_i].fd); - - std::unique_lock lck(input_lock[i]); - - registers[i * 4 + 0] |= 0160000; // "ERROR", RING INDICATOR, CARRIER TRANSITION - if (is_rx_interrupt_enabled(i)) - trigger_interrupt(i, false); - } - } - - // receive data - for(int i=dc11_n_lines; i lck(input_lock[line_nr]); - - if (rc_read <= 0) { // closed or error? - DOLOG(info, false, "Failed reading from port %d", i - dc11_n_lines + 1); - - registers[line_nr * 4 + 0] |= 0140000; // "ERROR", CARRIER TRANSITION - - close(pfds[i].fd); - pfds[i].fd = INVALID_SOCKET; - } - else { - for(int k=0; k(c); - - d->serial_handler(); - - vTaskSuspend(nullptr); -} - -void dc11::set_serial(const int bitrate, const int rx, const int tx) -{ - if (serial_thread_running) { - DOLOG(info, true, "DC11: serial port already configured"); - return; - } - - Serial.printf("Tick period: %d\r\n", portTICK_PERIOD_MS); - - serial_thread_running = true; - - // Configure UART parameters - static uart_config_t uart_config = { - .baud_rate = bitrate, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - .rx_flow_ctrl_thresh = 122, - }; - ESP_ERROR_CHECK(uart_param_config(ESP32_UART, &uart_config)); - - ESP_ERROR_CHECK(uart_set_pin(ESP32_UART, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); - - // Setup UART buffered IO with event queue - const int uart_buffer_size = 1024 * 2; - static QueueHandle_t uart_queue; - // Install UART driver using an event queue here - ESP_ERROR_CHECK(uart_driver_install(ESP32_UART, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0)); - - const char msg[] = "Press enter to connect\r\n"; - uart_write_bytes(ESP32_UART, msg, sizeof(msg) - 1); - - xTaskCreate(&dc11_thread_wrapper_serial_handler, "dc11_tty", 3072, this, 1, nullptr); -} -#elif IS_POSIX -void dc11::set_serial(const int bitrate, const std::string & device) -{ - serial_fd = open(device.c_str(), O_RDWR); - if (serial_fd == -1) { - DOLOG(warning, false, "DC11 failed to access %s: %s", device.c_str(), strerror(errno)); - return; // TODO error handling - } - - serial_thread_running = true; - - // from https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c - termios tty { }; - if (tcgetattr(serial_fd, &tty) == -1) { - DOLOG(warning, false, "DC11 tcgetattr failed: %s", strerror(errno)); - close(serial_fd); - return; - } - - cfsetospeed(&tty, bitrate); - cfsetispeed(&tty, bitrate); - - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars - // disable IGNBRK for mismatched speed tests; otherwise receive break - // as \000 chars - tty.c_iflag &= ~IGNBRK; // disable break processing - tty.c_lflag = 0; // no signaling chars, no echo, - // no canonical processing - tty.c_oflag = 0; // no remapping, no delays - tty.c_cc[VMIN] = 0; // read doesn't block - tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout - - tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl - - tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, - // enable reading - tty.c_cflag &= ~(PARENB | PARODD); // shut off parity - tty.c_cflag &= ~CSTOPB; - tty.c_cflag &= ~CRTSCTS; - - if (tcsetattr(serial_fd, TCSANOW, &tty) == -1) { - DOLOG(warning, false, "DC11 tcsetattr failed: %s", strerror(errno)); - close(serial_fd); - return; - } - - serial_th = new std::thread(&dc11::serial_handler, this); -} -#endif - -void dc11::serial_handler() -{ - set_thread_name("kek:dc11-serial"); - - TRACE("DC11: serial handler thread started"); - -#if IS_POSIX - pollfd fds[] = { { serial_fd, POLLIN, 0 } }; -#endif - - while(!stop_flag) { - char c = 0; -#if defined(ESP32) - yield(); - - size_t n_available = 0; - ESP_ERROR_CHECK(uart_get_buffered_data_len(ESP32_UART, &n_available)); - if (n_available == 0) { - vTaskDelay(4 / portTICK_PERIOD_MS); - continue; - } - - if (uart_read_bytes(ESP32_UART, &c, 1, 100) == 0) - continue; -#elif IS_POSIX - int rc_poll = poll(fds, 1, 100); - if (rc_poll == -1) { - DOLOG(warning, false, "DC11 poll failed: %s", strerror(errno)); - break; - } - if (rc_poll == 0) - continue; - - int rc_read = read(serial_fd, &c, 1); - if (rc_read <= 0) { - DOLOG(warning, false, "DC11 read on %d failed: %s", serial_fd, strerror(errno)); - break; - } -#endif - - std::unique_lock lck(input_lock[serial_line]); - - recv_buffers[serial_line].push_back(c); - - if (serial_enabled == false && is_rx_interrupt_enabled(serial_line)) { - DOLOG(debug, false, "DC11: enabling serial connection"); - - serial_enabled = true; - - // first key press enables the port - registers[serial_line * 4 + 0] |= 0160000; // "ERROR", RING INDICATOR, CARRIER TRANSITION - } - else { - TRACE("DC11: key %d pressed", c); - - registers[serial_line * 4 + 0] |= 128; // DONE: bit 7 - } - - if (is_rx_interrupt_enabled(serial_line)) - trigger_interrupt(serial_line, false); - } - - TRACE("DC11: serial handler thread terminating"); } void dc11::reset() @@ -505,7 +180,7 @@ uint16_t dc11::read_word(const uint16_t addr) registers[line_nr * 4 + 0] &= ~1; // DTR: bit 0 [RCSR] registers[line_nr * 4 + 0] &= ~4; // CD : bit 2 - if (pfds[line_nr + dc11_n_lines].fd != INVALID_SOCKET || line_nr == serial_line) { + if (comm_interfaces.at(line_nr)->is_connected()) { registers[line_nr * 4 + 0] |= 1; registers[line_nr * 4 + 0] |= 4; } @@ -541,7 +216,7 @@ uint16_t dc11::read_word(const uint16_t addr) registers[line_nr * 4 + 2] &= ~2; // CTS: bit 1 [TSCR] registers[line_nr * 4 + 2] &= ~128; // READY: bit 7 - if (pfds[line_nr + dc11_n_lines].fd != INVALID_SOCKET || line_nr == serial_line) { + if (comm_interfaces.at(line_nr)->is_connected()) { registers[line_nr * 4 + 2] |= 2; registers[line_nr * 4 + 2] |= 128; } @@ -588,38 +263,7 @@ void dc11::write_word(const uint16_t addr, const uint16_t v) else TRACE("DC11: transmit %c on line %d", c, line_nr); - if (line_nr == serial_line) { - if (serial_thread_running) { -#if defined(ESP32) - uart_write_bytes(ESP32_UART, &c, 1); -#elif IS_POSIX - if (write(serial_fd, &c, 1) != 1) { - DOLOG(warning, false, "DC11 failed to send %d to (fd %d) serial port: %s", c, serial_fd, strerror(errno)); - // TODO error handling - } -#endif - } - else { - TRACE("DC11 serial line 4 not connected, yet output %d", c); - } - - if (is_tx_interrupt_enabled(line_nr)) - trigger_interrupt(line_nr, true); - return; - } - - SOCKET fd = pfds[dc11_n_lines + line_nr].fd; - - if (fd != INVALID_SOCKET && write(fd, &c, 1) != 1) { - DOLOG(info, false, "DC11 line %d disconnected\n", line_nr + 1); - - registers[line_nr * 4 + 0] |= 0140000; // "ERROR", CARRIER TRANSITION -#if IS_POSIX - assert(fd != serial_fd); -#endif - close(fd); - pfds[dc11_n_lines + line_nr].fd = INVALID_SOCKET; - } + comm_interfaces.at(line_nr)->send_data(reinterpret_cast(&c), 1); if (is_tx_interrupt_enabled(line_nr)) trigger_interrupt(line_nr, true); diff --git a/dc11.h b/dc11.h index 0ccc12e..0b6f9ac 100644 --- a/dc11.h +++ b/dc11.h @@ -8,14 +8,8 @@ #include #include #include -#if defined(_WIN32) -#include -#include -#else -#define SOCKET int -#define INVALID_SOCKET -1 -#endif +#include "comm.h" #include "device.h" #include "gen.h" #include "bus.h" @@ -27,7 +21,6 @@ class Stream; class bus; -struct pollfd; // 4 interfaces constexpr const int dc11_n_lines = 4; @@ -35,33 +28,23 @@ constexpr const int dc11_n_lines = 4; class dc11: public device { private: - int base_port { 1100 }; bus *const b { nullptr }; uint16_t registers[4 * dc11_n_lines] { 0 }; std::atomic_bool stop_flag { false }; std::thread *th { nullptr }; - // not statically allocated because of compiling problems on arduino -#if defined(_WIN32) - WSAPOLLFD *pfds { nullptr }; -#else - pollfd *pfds { nullptr }; -#endif - std::vector recv_buffers[dc11_n_lines]; - mutable std::mutex input_lock[dc11_n_lines]; - std::atomic_bool serial_thread_running { false }; - bool serial_enabled { false }; -#if IS_POSIX - std::thread *serial_th { nullptr }; - int serial_fd { -1 }; -#endif + std::vector comm_interfaces; + std::vector connected; + + std::vector recv_buffers[dc11_n_lines]; + mutable std::mutex input_lock [dc11_n_lines]; void trigger_interrupt(const int line_nr, const bool is_tx); bool is_rx_interrupt_enabled(const int line_nr) const; bool is_tx_interrupt_enabled(const int line_nr) const; public: - dc11(const int base_port, bus *const b); + dc11(bus *const b, const std::vector & comm_interfaces); virtual ~dc11(); #if IS_POSIX @@ -73,15 +56,8 @@ public: void show_state(console *const cnsl) const override; - void test_serial(const std::string & txt) const; - -#if defined(ESP32) - void set_serial(const int bitrate, const int rx, const int tx); - void serial_handler(); -#elif IS_POSIX - void set_serial(const int bitrate, const std::string & device_name); - void serial_handler(); -#endif + void test_port(const size_t port_nr, const std::string & txt) const; + void test_ports(const std::string & txt) const; uint8_t read_byte(const uint16_t addr) override; uint16_t read_word(const uint16_t addr) override; diff --git a/debugger.cpp b/debugger.cpp index 3277e27..f9245f0 100644 --- a/debugger.cpp +++ b/debugger.cpp @@ -982,7 +982,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto continue; } else if (parts[0] == "testdc11") { - b->getDC11()->test_serial(cmd); + b->getDC11()->test_ports(cmd); continue; } diff --git a/main.cpp b/main.cpp index 6166586..8debc5a 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,9 @@ #include #include "error.h" +#include "comm.h" +#include "comm_posix_tty.h" +#include "comm_tcp_socket.h" #if !defined(_WIN32) #include "console_ncurses.h" #endif @@ -602,13 +605,24 @@ int main(int argc, char *argv[]) cnsl->set_bus(b); cnsl->begin(); - // TODO - dc11 *dc11_ = new dc11(1100, b); + //// DC11 + constexpr const int bitrate = 38400; + + std::vector comm_interfaces; if (dc11_device.has_value()) { - DOLOG(info, false, "Configuring DC11 device for serial port on %s", dc11_device.value().c_str()); - dc11_->set_serial(38400, dc11_device.value()); + DOLOG(info, false, "Configuring DC11 device for TTY on %s (%d bps)", dc11_device.value().c_str(), bitrate); + comm_interfaces.push_back(new comm_posix_tty(dc11_device.value(), bitrate)); } + + for(size_t i=comm_interfaces.size(); i<4; i++) { + int port = 1100 + i; + comm_interfaces.push_back(new comm_tcp_socket(port)); + DOLOG(info, false, "Configuring DC11 device for TCP socket on port %d", port); + } + + dc11 *dc11_ = new dc11(b, comm_interfaces); b->add_DC11(dc11_); + // tm_11 *tm_11_ = new tm_11(b); b->add_tm11(tm_11_);