rewrote DC11 to use comm
This commit is contained in:
parent
12f3f59620
commit
7ae8bc4bcc
7 changed files with 91 additions and 444 deletions
|
@ -22,6 +22,9 @@
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
|
#include "comm_esp32_hardwareserial.h"
|
||||||
|
#include "comm_tcp_socket.h"
|
||||||
#if defined(SHA2017)
|
#if defined(SHA2017)
|
||||||
#include "console_shabadge.h"
|
#include "console_shabadge.h"
|
||||||
#else
|
#else
|
||||||
|
@ -204,7 +207,7 @@ void start_network(console *const c)
|
||||||
dc11_loaded = true;
|
dc11_loaded = true;
|
||||||
|
|
||||||
Serial.println(F("* Adding DC11"));
|
Serial.println(F("* Adding DC11"));
|
||||||
dc11 *dc11_ = new dc11(1100, b);
|
std::vector<comm *> comm_interfaces;
|
||||||
|
|
||||||
#if !defined(BUILD_FOR_RP2040) && defined(TTY_SERIAL_RX)
|
#if !defined(BUILD_FOR_RP2040) && defined(TTY_SERIAL_RX)
|
||||||
uint32_t bitrate = load_serial_speed_configuration();
|
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.printf("* Init TTY (on DC11), baudrate: %d bps, RX: %d, TX: %d", bitrate, TTY_SERIAL_RX, TTY_SERIAL_TX);
|
||||||
Serial.println(F(""));
|
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
|
#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_);
|
b->add_DC11(dc11_);
|
||||||
|
|
||||||
Serial.println(F("* Starting (NTP-) clock"));
|
Serial.println(F("* Starting (NTP-) clock"));
|
||||||
|
|
2
comm.h
2
comm.h
|
@ -1,6 +1,8 @@
|
||||||
// (C) 2024 by Folkert van Heusden
|
// (C) 2024 by Folkert van Heusden
|
||||||
// Released under MIT license
|
// Released under MIT license
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "gen.h"
|
#include "gen.h"
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
|
@ -30,12 +30,12 @@ public:
|
||||||
comm_tcp_socket(const int port);
|
comm_tcp_socket(const int port);
|
||||||
virtual ~comm_tcp_socket();
|
virtual ~comm_tcp_socket();
|
||||||
|
|
||||||
virtual bool is_connected() = 0;
|
bool is_connected() override;
|
||||||
|
|
||||||
virtual bool has_data() = 0;
|
bool has_data() override;
|
||||||
virtual uint8_t get_byte() = 0;
|
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()();
|
||||||
};
|
};
|
||||||
|
|
430
dc11.cpp
430
dc11.cpp
|
@ -5,22 +5,8 @@
|
||||||
#if defined(ESP32)
|
#if defined(ESP32)
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(ESP32)
|
|
||||||
#include <lwip/sockets.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <driver/uart.h>
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <winsock2.h>
|
|
||||||
#else
|
|
||||||
#include <poll.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#endif
|
|
||||||
#if IS_POSIX
|
#if IS_POSIX
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#endif
|
#endif
|
||||||
|
@ -41,50 +27,11 @@ constexpr const int serial_line = 3;
|
||||||
|
|
||||||
const char *const dc11_register_names[] { "RCSR", "RBUF", "TSCR", "TBUF" };
|
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 *> & comm_interfaces):
|
||||||
|
b(b),
|
||||||
|
comm_interfaces(comm_interfaces)
|
||||||
{
|
{
|
||||||
uint8_t dont_auth[] = { 0xff, 0xf4, 0x25 };
|
connected.resize(comm_interfaces.size());
|
||||||
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
|
|
||||||
|
|
||||||
// TODO move to begin()
|
// TODO move to begin()
|
||||||
th = new std::thread(std::ref(*this));
|
th = new std::thread(std::ref(*this));
|
||||||
|
@ -100,26 +47,14 @@ dc11::~dc11()
|
||||||
th->join();
|
th->join();
|
||||||
delete th;
|
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
|
void dc11::show_state(console *const cnsl) const
|
||||||
{
|
{
|
||||||
for(int i=0; i<4; i++) {
|
for(int i=0; i<comm_interfaces.size(); i++) {
|
||||||
cnsl->put_string_lf(format("* LINE %d", i + 1));
|
cnsl->put_string_lf(format("* LINE %d", i + 1));
|
||||||
|
|
||||||
|
#if 0 // TODO
|
||||||
if (i == serial_line) {
|
if (i == serial_line) {
|
||||||
cnsl->put_string_lf(format(" Serial thread running: %s", serial_thread_running ? "true": "false" ));
|
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" ));
|
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)
|
if (pfds[dc11_n_lines + i].fd != INVALID_SOCKET)
|
||||||
cnsl->put_string_lf(" Connected to: " + get_endpoint_name(pfds[dc11_n_lines + i].fd));
|
cnsl->put_string_lf(" Connected to: " + get_endpoint_name(pfds[dc11_n_lines + i].fd));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lck(input_lock[i]);
|
std::unique_lock<std::mutex> lck(input_lock[i]);
|
||||||
cnsl->put_string_lf(format(" Characters in buffer: %zu", recv_buffers[i].size()));
|
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; i<dc11_n_lines; i++) {
|
DOLOG(info, false, "DC11 test line %zu", nr);
|
||||||
if (i == serial_line) {
|
|
||||||
DOLOG(info, false, "DC11 serial test line %d", i);
|
|
||||||
|
|
||||||
#if defined(ESP32)
|
comm_interfaces.at(nr)->send_data(reinterpret_cast<const uint8_t *>(txt.c_str()), txt.size());
|
||||||
uart_write_bytes(ESP32_UART, txt.c_str(), txt.size());
|
}
|
||||||
#elif IS_POSIX
|
|
||||||
if (write(serial_fd, txt.c_str(), txt.size()) != ssize_t(txt.size()))
|
|
||||||
DOLOG(warning, false, "DC11 failed to send test string to line %d", i);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if (pfds[dc11_n_lines + i].fd != INVALID_SOCKET) {
|
|
||||||
DOLOG(info, false, "DC11 socket line %d", i);
|
|
||||||
|
|
||||||
if (write(pfds[dc11_n_lines + i].fd, txt.c_str(), txt.size()) != ssize_t(txt.size()))
|
void dc11::test_ports(const std::string & txt) const
|
||||||
DOLOG(warning, false, "DC11 failed to send test string to line %d", i);
|
{
|
||||||
}
|
for(size_t i=0; i<comm_interfaces.size(); i++)
|
||||||
else {
|
test_port(i, txt);
|
||||||
DOLOG(info, false, "DC11 line %d not connected", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dc11::trigger_interrupt(const int line_nr, const bool is_tx)
|
void dc11::trigger_interrupt(const int line_nr, const bool is_tx)
|
||||||
|
@ -175,295 +99,46 @@ void dc11::operator()()
|
||||||
|
|
||||||
DOLOG(info, true, "DC11 thread started");
|
DOLOG(info, true, "DC11 thread started");
|
||||||
|
|
||||||
for(int i=0; i<dc11_n_lines; i++) {
|
|
||||||
// client session
|
|
||||||
pfds[dc11_n_lines + i].fd = INVALID_SOCKET;
|
|
||||||
if (i == serial_line) { // prevent accept() on this socket
|
|
||||||
pfds[i].fd = INVALID_SOCKET;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pfds[dc11_n_lines + i].events = POLLIN;
|
|
||||||
|
|
||||||
// listen on port
|
|
||||||
int port = base_port + i + 1;
|
|
||||||
|
|
||||||
pfds[i].fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
|
|
||||||
int reuse_addr = 1;
|
|
||||||
if (setsockopt(pfds[i].fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse_addr, sizeof(reuse_addr)) == -1) {
|
|
||||||
close(pfds[i].fd);
|
|
||||||
pfds[i].fd = INVALID_SOCKET;
|
|
||||||
|
|
||||||
DOLOG(warning, true, "Cannot set reuseaddress for port %d (DC11)", port);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_nodelay(pfds[i].fd);
|
|
||||||
|
|
||||||
sockaddr_in listen_addr;
|
|
||||||
memset(&listen_addr, 0, sizeof(listen_addr));
|
|
||||||
listen_addr.sin_family = AF_INET;
|
|
||||||
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
listen_addr.sin_port = htons(port);
|
|
||||||
|
|
||||||
if (bind(pfds[i].fd, reinterpret_cast<struct sockaddr *>(&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) {
|
while(!stop_flag) {
|
||||||
#if defined(_WIN32)
|
myusleep(5000); // TODO replace polling
|
||||||
int rc = WSAPoll(pfds, dc11_n_lines * 2, 100);
|
|
||||||
#else
|
|
||||||
int rc = poll(pfds, dc11_n_lines * 2, 100);
|
|
||||||
#endif
|
|
||||||
if (rc == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// accept any new session
|
for(size_t line_nr=0; line_nr<comm_interfaces.size(); line_nr++) {
|
||||||
for(int i=0; i<dc11_n_lines; i++) {
|
// (dis-)connected?
|
||||||
if (pfds[i].revents != POLLIN)
|
bool is_connected = comm_interfaces.at(line_nr)->is_connected();
|
||||||
continue;
|
|
||||||
|
|
||||||
int client_i = dc11_n_lines + i;
|
if (is_connected != connected[line_nr]) {
|
||||||
|
connected[line_nr] = is_connected;
|
||||||
|
|
||||||
// disconnect any existing client session
|
if (is_connected)
|
||||||
// yes, one can ddos with this
|
registers[line_nr * 4 + 0] |= 0160000; // "ERROR", RING INDICATOR, CARRIER TRANSITION
|
||||||
if (pfds[client_i].fd != INVALID_SOCKET) {
|
else
|
||||||
close(pfds[client_i].fd);
|
registers[line_nr * 4 + 0] |= 0120000; // "ERROR", CARRIER TRANSITION
|
||||||
DOLOG(info, false, "Restarting session for port %d", base_port + i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pfds[client_i].fd = accept(pfds[i].fd, nullptr, nullptr);
|
if (is_rx_interrupt_enabled(line_nr))
|
||||||
|
trigger_interrupt(line_nr, false);
|
||||||
if (setup_telnet_session(pfds[client_i].fd) == false) {
|
|
||||||
close(pfds[client_i].fd);
|
|
||||||
pfds[client_i].fd = INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pfds[client_i].fd != INVALID_SOCKET) {
|
|
||||||
set_nodelay(pfds[client_i].fd);
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> 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
|
// receive data
|
||||||
for(int i=dc11_n_lines; i<dc11_n_lines * 2; i++) {
|
bool have_data = false;
|
||||||
if (pfds[i].revents != POLLIN)
|
while(comm_interfaces.at(line_nr)->has_data()) {
|
||||||
continue;
|
uint8_t buffer = comm_interfaces.at(line_nr)->get_byte();
|
||||||
|
|
||||||
char buffer[32] { };
|
|
||||||
int rc_read = read(pfds[i].fd, buffer, sizeof buffer);
|
|
||||||
|
|
||||||
int line_nr = i - dc11_n_lines;
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lck(input_lock[line_nr]);
|
std::unique_lock<std::mutex> lck(input_lock[line_nr]);
|
||||||
|
recv_buffers[line_nr].push_back(char(buffer));
|
||||||
|
|
||||||
if (rc_read <= 0) { // closed or error?
|
have_data = true;
|
||||||
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<rc_read; k++)
|
|
||||||
recv_buffers[line_nr].push_back(buffer[k]);
|
|
||||||
|
|
||||||
|
if (have_data) {
|
||||||
registers[line_nr * 4 + 0] |= 128; // DONE: bit 7
|
registers[line_nr * 4 + 0] |= 128; // DONE: bit 7
|
||||||
}
|
|
||||||
|
|
||||||
if (is_rx_interrupt_enabled(line_nr))
|
if (is_rx_interrupt_enabled(line_nr))
|
||||||
trigger_interrupt(line_nr, false);
|
trigger_interrupt(line_nr, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DOLOG(info, true, "DC11 thread terminating");
|
DOLOG(info, true, "DC11 thread terminating");
|
||||||
|
|
||||||
for(int i=0; i<dc11_n_lines * 2; i++) {
|
|
||||||
if (pfds[i].fd != INVALID_SOCKET)
|
|
||||||
close(pfds[i].fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(ESP32)
|
|
||||||
void dc11_thread_wrapper_serial_handler(void *const c)
|
|
||||||
{
|
|
||||||
dc11 *const d = reinterpret_cast<dc11 *>(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<std::mutex> 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()
|
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] &= ~1; // DTR: bit 0 [RCSR]
|
||||||
registers[line_nr * 4 + 0] &= ~4; // CD : bit 2
|
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] |= 1;
|
||||||
registers[line_nr * 4 + 0] |= 4;
|
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] &= ~2; // CTS: bit 1 [TSCR]
|
||||||
registers[line_nr * 4 + 2] &= ~128; // READY: bit 7
|
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] |= 2;
|
||||||
registers[line_nr * 4 + 2] |= 128;
|
registers[line_nr * 4 + 2] |= 128;
|
||||||
}
|
}
|
||||||
|
@ -588,38 +263,7 @@ void dc11::write_word(const uint16_t addr, const uint16_t v)
|
||||||
else
|
else
|
||||||
TRACE("DC11: transmit %c on line %d", c, line_nr);
|
TRACE("DC11: transmit %c on line %d", c, line_nr);
|
||||||
|
|
||||||
if (line_nr == serial_line) {
|
comm_interfaces.at(line_nr)->send_data(reinterpret_cast<const uint8_t *>(&c), 1);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_tx_interrupt_enabled(line_nr))
|
if (is_tx_interrupt_enabled(line_nr))
|
||||||
trigger_interrupt(line_nr, true);
|
trigger_interrupt(line_nr, true);
|
||||||
|
|
40
dc11.h
40
dc11.h
|
@ -8,14 +8,8 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <winsock2.h>
|
|
||||||
#else
|
|
||||||
#define SOCKET int
|
|
||||||
#define INVALID_SOCKET -1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#include "comm.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "gen.h"
|
#include "gen.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
|
@ -27,7 +21,6 @@
|
||||||
|
|
||||||
class Stream;
|
class Stream;
|
||||||
class bus;
|
class bus;
|
||||||
struct pollfd;
|
|
||||||
|
|
||||||
// 4 interfaces
|
// 4 interfaces
|
||||||
constexpr const int dc11_n_lines = 4;
|
constexpr const int dc11_n_lines = 4;
|
||||||
|
@ -35,33 +28,23 @@ constexpr const int dc11_n_lines = 4;
|
||||||
class dc11: public device
|
class dc11: public device
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int base_port { 1100 };
|
|
||||||
bus *const b { nullptr };
|
bus *const b { nullptr };
|
||||||
uint16_t registers[4 * dc11_n_lines] { 0 };
|
uint16_t registers[4 * dc11_n_lines] { 0 };
|
||||||
std::atomic_bool stop_flag { false };
|
std::atomic_bool stop_flag { false };
|
||||||
std::thread *th { nullptr };
|
std::thread *th { nullptr };
|
||||||
|
|
||||||
// not statically allocated because of compiling problems on arduino
|
std::vector<comm *> comm_interfaces;
|
||||||
#if defined(_WIN32)
|
std::vector<bool > connected;
|
||||||
WSAPOLLFD *pfds { nullptr };
|
|
||||||
#else
|
|
||||||
pollfd *pfds { nullptr };
|
|
||||||
#endif
|
|
||||||
std::vector<char> recv_buffers[dc11_n_lines];
|
std::vector<char> recv_buffers[dc11_n_lines];
|
||||||
mutable std::mutex input_lock[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
|
|
||||||
|
|
||||||
void trigger_interrupt(const int line_nr, const bool is_tx);
|
void trigger_interrupt(const int line_nr, const bool is_tx);
|
||||||
bool is_rx_interrupt_enabled(const int line_nr) const;
|
bool is_rx_interrupt_enabled(const int line_nr) const;
|
||||||
bool is_tx_interrupt_enabled(const int line_nr) const;
|
bool is_tx_interrupt_enabled(const int line_nr) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
dc11(const int base_port, bus *const b);
|
dc11(bus *const b, const std::vector<comm *> & comm_interfaces);
|
||||||
virtual ~dc11();
|
virtual ~dc11();
|
||||||
|
|
||||||
#if IS_POSIX
|
#if IS_POSIX
|
||||||
|
@ -73,15 +56,8 @@ public:
|
||||||
|
|
||||||
void show_state(console *const cnsl) const override;
|
void show_state(console *const cnsl) const override;
|
||||||
|
|
||||||
void test_serial(const std::string & txt) const;
|
void test_port(const size_t port_nr, const std::string & txt) const;
|
||||||
|
void test_ports(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
|
|
||||||
|
|
||||||
uint8_t read_byte(const uint16_t addr) override;
|
uint8_t read_byte(const uint16_t addr) override;
|
||||||
uint16_t read_word(const uint16_t addr) override;
|
uint16_t read_word(const uint16_t addr) override;
|
||||||
|
|
|
@ -982,7 +982,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (parts[0] == "testdc11") {
|
else if (parts[0] == "testdc11") {
|
||||||
b->getDC11()->test_serial(cmd);
|
b->getDC11()->test_ports(cmd);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
22
main.cpp
22
main.cpp
|
@ -11,6 +11,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "comm.h"
|
||||||
|
#include "comm_posix_tty.h"
|
||||||
|
#include "comm_tcp_socket.h"
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
#include "console_ncurses.h"
|
#include "console_ncurses.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -602,13 +605,24 @@ int main(int argc, char *argv[])
|
||||||
cnsl->set_bus(b);
|
cnsl->set_bus(b);
|
||||||
cnsl->begin();
|
cnsl->begin();
|
||||||
|
|
||||||
// TODO
|
//// DC11
|
||||||
dc11 *dc11_ = new dc11(1100, b);
|
constexpr const int bitrate = 38400;
|
||||||
|
|
||||||
|
std::vector<comm *> comm_interfaces;
|
||||||
if (dc11_device.has_value()) {
|
if (dc11_device.has_value()) {
|
||||||
DOLOG(info, false, "Configuring DC11 device for serial port on %s", dc11_device.value().c_str());
|
DOLOG(info, false, "Configuring DC11 device for TTY on %s (%d bps)", dc11_device.value().c_str(), bitrate);
|
||||||
dc11_->set_serial(38400, dc11_device.value());
|
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_);
|
b->add_DC11(dc11_);
|
||||||
|
//
|
||||||
|
|
||||||
tm_11 *tm_11_ = new tm_11(b);
|
tm_11 *tm_11_ = new tm_11(b);
|
||||||
b->add_tm11(tm_11_);
|
b->add_tm11(tm_11_);
|
||||||
|
|
Loading…
Add table
Reference in a new issue