Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_executable(ceda
src/int.c
src/keyboard.c
src/main.c
src/serial.c
src/sio2.c
src/speaker.c
src/time.c
Expand Down
7 changes: 5 additions & 2 deletions src/ceda.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "limits.h"
#include "macro.h"
#include "module.h"
#include "serial.h"
#include "sio2.h"
#include "speaker.h"
#include "upd8255.h"
Expand All @@ -29,10 +30,11 @@ static CEDAModule mod_video;
static CEDAModule mod_speaker;
static CEDAModule mod_sio2;
static CEDAModule mod_int;
static CEDAModule mod_serial;

static CEDAModule *modules[] = {
&mod_cli, &mod_gui, &mod_bus, &mod_cpu,
&mod_video, &mod_speaker, &mod_int, &mod_sio2,
&mod_cli, &mod_gui, &mod_bus, &mod_cpu, &mod_video,
&mod_speaker, &mod_int, &mod_serial, &mod_sio2,
};

void ceda_init(void) {
Expand All @@ -48,6 +50,7 @@ void ceda_init(void) {
bus_init(&mod_bus);
cpu_init(&mod_cpu);
int_init(&mod_int);
serial_init(&mod_serial);
sio2_init(&mod_sio2);
}

Expand Down
30 changes: 30 additions & 0 deletions src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "floppy.h"
#include "int.h"
#include "macro.h"
#include "serial.h"
#include "time.h"
#include "tokenizer.h"

Expand Down Expand Up @@ -756,6 +757,34 @@ static ceda_string_t *cli_out(const char *arg) {
return NULL;
}

static ceda_string_t *cli_serial(const char *arg) {
char word[LINE_BUFFER_SIZE];
ceda_string_t *msg = ceda_string_new(0);

// skip argv[0]
arg = tokenizer_next_word(word, arg, LINE_BUFFER_SIZE);

// extract command
arg = tokenizer_next_word(word, arg, LINE_BUFFER_SIZE);

if (arg == NULL) {
ceda_string_cpy(msg, USER_BAD_ARG_STR "missing command\n");
return msg;
}

if (strcmp(word, "open") == 0) {
serial_open(0);
} else if (strcmp(word, "close") == 0) {
serial_close();
} else {
ceda_string_cpy(msg, USER_BAD_ARG_STR "expected open or close\n");
return msg;
}

ceda_string_delete(msg);
return NULL;
}

/*
A cli_command_handler_t is a command line handler.
It takes a pointer to the line buffer.
Expand Down Expand Up @@ -793,6 +822,7 @@ static const cli_command cli_commands[] = {
{"mount", "load floppy image in from specified drive (default is 0)",
cli_mount},
{"umount", "unload floppy from specified drive (default is 0)", cli_umount},
{"serial", "open tcp socket to emulate serial port", cli_serial},
{"load", "load binary from file", cli_load},
{"run", "load binary from file and run", cli_run},
{"save", "save memory dump to file", cli_save},
Expand Down
206 changes: 206 additions & 0 deletions src/serial.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#include "serial.h"

#include "fifo.h"
#include "sio2.h"
#include "time.h"

#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>

#define LOG_LEVEL LOG_LVL_INFO
#include "log.h"

#define SERIAL_TCP_PORT (0xCEDB)
#define SERIAL_NETWORK_BUFFER_SIZE 64U

DECLARE_FIFO_TYPE(char, SerialFifo, 64);
static int sockfd = -1;
static int connfd = -1;
static SerialFifo tx_fifo;
static SerialFifo rx_fifo;

static bool serial_getChar(uint8_t *c) {
if (FIFO_ISEMPTY(&rx_fifo))
return false;

*c = (uint8_t)FIFO_POP(&rx_fifo);
return true;
}

static bool serial_putChar(uint8_t c) {
if (FIFO_ISFULL(&tx_fifo))
return false;

LOG_DEBUG("serial: transmitting: %02x (%c)\n", (unsigned int)c,
isprint(c) ? c : ' ');

FIFO_PUSH(&tx_fifo, (char)c);
return true;
}

static void serial_poll(void) {
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;

if (connfd == -1) {
fd_set accept_set;
FD_ZERO(&accept_set);
FD_SET(sockfd, &accept_set);
int ret = select(sockfd + 1, &accept_set, NULL, NULL, &timeout);
if (ret == -1) {
LOG_ERR(
"serial: error during select while accepting new client: %s\n",
strerror(errno));
return;
}
if (ret == 0) // timeout
return;

connfd = accept(sockfd, NULL, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC you never check for return value

Copy link
Collaborator Author

@giomba giomba Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module implements the CEDAModule interface (see src/module.h), which means that this function is polled in the main loop, so the check is performed during next loop iteration, and (if I did not get something wrong) nothing will break.
Anyhow, I'm always printing the "accept client" message, which may actually be wrong. Fixed.

if (connfd != -1) {
LOG_INFO("serial: accept client\n");
}
} else {
int ret = -1;
fd_set read_set;
fd_set write_set;
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(connfd, &read_set);
FD_SET(connfd, &write_set);

ret = select(connfd + 1, &read_set, &write_set, NULL, &timeout);
if (ret == -1) {
LOG_ERR("serial: select error while reading from client: %s\n",
strerror(errno));
close(connfd);
connfd = -1;
return;
}
if (ret == 0) // timeout
return;

// check file descriptors ready for read
if (FD_ISSET(connfd, &read_set)) {
char buffer[SERIAL_NETWORK_BUFFER_SIZE];
const size_t to_receive = MIN((size_t)SERIAL_NETWORK_BUFFER_SIZE,
(size_t)FIFO_FREE(&rx_fifo));
if (to_receive > 0) {
ssize_t ret = recv(connfd, buffer, to_receive, 0);
if (ret == -1) {
LOG_ERR(
"serial: recv error while reading from client: %s\n",
strerror(errno));
LOG_ERR("serial: connection reset\n");
close(connfd);
connfd = -1;
return;
}
if (ret == 0) {
// client disconnection
close(connfd);
connfd = -1;
LOG_INFO("serial: client disconnected\n");
return;
}
// data available
for (ssize_t i = 0; i < ret && !FIFO_ISFULL(&rx_fifo); ++i)
FIFO_PUSH(&rx_fifo, buffer[i]);
}
}

// check file descriptors ready for write
if (FD_ISSET(connfd, &write_set)) {
char buffer[SERIAL_NETWORK_BUFFER_SIZE];
size_t n = 0;
while (n < SERIAL_NETWORK_BUFFER_SIZE && !FIFO_ISEMPTY(&tx_fifo))
buffer[n++] = FIFO_POP(&tx_fifo);
ssize_t ret = send(connfd, buffer, n, 0);

if (ret == -1) {
LOG_ERR("serial: send error while writing to client: %s\n",
strerror(errno));
LOG_ERR("serial: connection reset\n");
close(connfd);
connfd = -1;
return;
}
}
}
}

bool serial_open(uint16_t port) {
if (sockfd >= 0) {
LOG_INFO("serial: port already open\n");
return false;
}

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
LOG_ERR("serial: unable to socket(): %s\n", strerror(errno));
return false;
}

if (port == 0)
port = SERIAL_TCP_PORT;

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){true},
sizeof(int)) != 0) {
LOG_ERR("serial: unable to setsockopt(): %s\n", strerror(errno));
return false;
}

if (bind(sockfd, (const struct sockaddr *)&server_addr,
sizeof(server_addr)) != 0) {
LOG_ERR("serial: unable to bind(): %s\n", strerror(errno));
return false;
}

if (listen(sockfd, 1) != 0) {
LOG_ERR("serial: unable to listen(): %s\n", strerror(errno));
return false;
}

FIFO_INIT(&tx_fifo);
FIFO_INIT(&rx_fifo);

sio2_attachPeripheral(SIO_CHANNEL_A, serial_getChar, serial_putChar);

LOG_INFO("serial: open ok\n");
return true;
}

void serial_close(void) {
sio2_detachPeripheral(SIO_CHANNEL_A);

if (connfd != -1)
close(connfd);

if (sockfd != -1)
close(sockfd);

LOG_INFO("serial: close ok\n");
}

static void serial_cleanup(void) {
serial_close();
}

void serial_init(CEDAModule *mod) {
memset(mod, 0, sizeof(*mod));
mod->init = serial_init;
mod->poll = serial_poll;
mod->cleanup = serial_cleanup;
}
14 changes: 14 additions & 0 deletions src/serial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef CEDA_SERIAL_PORT_H
#define CEDA_SERIAL_PORT_H

#include "module.h"

#include <stdbool.h>
#include <stdint.h>

void serial_init(CEDAModule *mod);

bool serial_open(uint16_t port);
void serial_close(void);

#endif // CEDA_SERIAL_PORT_H
Loading
Loading