Skip to content

Commit b3c7dce

Browse files
committed
MEGA65: UART-via-TCP fixes/enhancements #440
Also adding matrix-mode command "serialtcp" to query status and (re)start connection possible with a new target.
1 parent ecd05eb commit b3c7dce

File tree

5 files changed

+140
-25
lines changed

5 files changed

+140
-25
lines changed

targets/mega65/matrix_mode.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,14 +583,41 @@ static void cmd_shade ( char *p )
583583
}
584584

585585

586-
static void cmd_audio ( char * p )
586+
static void cmd_audio ( char* p )
587587
{
588588
char buffer[4096];
589589
audio65_get_description(buffer, sizeof buffer);
590590
MATRIX("%s", buffer);
591591
}
592592

593593

594+
#ifdef XEMU_HAS_SOCKET_API
595+
#include "serialtcp.h"
596+
static void cmd_serialtcp ( char *p )
597+
{
598+
char param[128], desc[128];
599+
int tx, rx;
600+
const bool running = serialtcp_get_connection_desc(param, sizeof param, desc, sizeof desc, &tx, &rx);
601+
if (*p) {
602+
if (p[0] == '-')
603+
p = param;
604+
if (*p) {
605+
if (serialtcp_restart(p))
606+
MATRIX("ERROR: Could not start/restart");
607+
else {
608+
(void)serialtcp_get_connection_desc(NULL, 0, desc, sizeof desc, NULL, NULL);
609+
MATRIX("OK: Started/restarted to %s", desc);
610+
}
611+
} else {
612+
MATRIX("ERROR: No previous target specification");
613+
}
614+
} else {
615+
MATRIX("STATUS: %s %s (tx=%d,rx=%d)\nUse argument HOST:PORT, or - (previous target) to (re)start connection.", running ? "running: " : "NOT running, previous target was: ", *desc ? desc : "[NO PREVIOUS TARGET]", tx, rx);
616+
}
617+
}
618+
#endif
619+
620+
594621
static void cmd_help ( char *p );
595622

596623

@@ -614,6 +641,9 @@ static const struct command_tab_st {
614641
{ "map", cmd_map, "m" },
615642
{ "shade", cmd_shade, NULL },
616643
{ "audio", cmd_audio, NULL },
644+
# ifdef XEMU_HAS_SOCKET_API
645+
{ "serialtcp", cmd_serialtcp, NULL },
646+
# endif
617647
{ .cmdname = NULL },
618648
};
619649

targets/mega65/mega65.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,12 @@ static void update_emulator ( void )
626626
kbd_trigger_restore_trap();
627627
#ifdef HAS_UARTMON_SUPPORT
628628
uartmon_update();
629+
#endif
630+
#ifdef XEMU_HAS_SOCKET_API
631+
if (XEMU_UNLIKELY(serialtcp_get_connection_error(false))) {
632+
const char *err = serialtcp_get_connection_error(true);
633+
ERROR_WINDOW("SerialTCP failure:\n%s", err);
634+
}
629635
#endif
630636
// Screen updating, final phase
631637
//vic4_close_frame_access();

targets/mega65/serialtcp.c

Lines changed: 97 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
2828
static volatile xemusock_socket_t sock = XS_INVALID_SOCKET;
2929
static volatile bool running = false;
3030
static volatile bool exited = false;
31+
static volatile const char *failed = NULL;
3132
static SDL_Thread *thread_id = NULL;
3233
static Uint8 rx_buffer[BUFFER_SIZE];
3334
static Uint8 tx_buffer[BUFFER_SIZE];
@@ -36,6 +37,8 @@ static SDL_SpinLock lock = 0;
3637
static Uint8 serial_regs[0x10];
3738
static int bitrate_divisor, baudrate;
3839
static int bitrate_divisor_reported = -1;
40+
static char *connection_desc = NULL, *connection_string = NULL;
41+
static volatile int tx_bytes_sum, rx_bytes_sum;
3942

4043

4144
static void close_socket ( void )
@@ -53,6 +56,7 @@ static int the_thread ( void *unused )
5356
{
5457
DEBUGPRINT("SERIALTCP: thread: begin" NL);
5558
Uint8 rx_temp[BUFFER_SIZE];
59+
static const char zero_transfer_error[] = "Cannot read/write, connection closed by peer?";
5660
while (running) {
5761
const int tx_size = SDL_AtomicGet(&tx_fill);
5862
const int rx_size = SDL_AtomicGet(&rx_fill);
@@ -63,29 +67,61 @@ static int the_thread ( void *unused )
6367
}
6468
int xerr, ret_write = -1, ret_read = -1;
6569
const int ret = xemusock_select_1(sock, 1000, what, &xerr);
66-
// TODO: error handling, end of connection / lost connection ...
70+
if (!ret)
71+
continue; // timeout on select
72+
if (ret < 0) {
73+
// XSEINTR is handled already inside xemusock_select_1(), so other errors are kind of fatal ...
74+
failed = xemusock_strerror(xerr);
75+
DEBUGPRINT("SERIALTCP: thread: select() error (%d): %s" NL, xerr, failed);
76+
break;
77+
}
6778
if ((ret & XEMUSOCK_SELECT_W)) {
6879
ret_write = xemusock_send(sock, tx_buffer, tx_size, &xerr);
80+
if (ret_write < 0) {
81+
if (xemusock_should_repeat_from_error(xerr))
82+
continue;
83+
failed = xemusock_strerror(xerr);
84+
DEBUGPRINT("SERIALTCP: thread: send error ret=%d error_code=(%d) error_str=\"%s\"" NL, ret_write, xerr, failed);
85+
break;
86+
}
87+
if (ret_write == 0) {
88+
failed = zero_transfer_error;
89+
break;
90+
}
6991
}
7092
if ((ret & XEMUSOCK_SELECT_R)) {
7193
ret_read = xemusock_recv(sock, rx_temp, BUFFER_SIZE - rx_size, &xerr);
94+
if (ret_read < 0) {
95+
if (xemusock_should_repeat_from_error(xerr))
96+
continue;
97+
failed = xemusock_strerror(xerr);
98+
DEBUGPRINT("SERIALTCP: thread: recv error ret=%d error_code=(%d) error_str=\"%s\"" NL, ret_read, xerr, failed);
99+
break;
100+
}
101+
if (ret_read == 0) {
102+
failed = zero_transfer_error;
103+
break;
104+
}
72105
}
73106
if (ret_read > 0 || ret_write > 0) {
74107
SDL_AtomicLock(&lock);
75108
if (ret_read > 0) {
76109
const int size = SDL_AtomicGet(&rx_fill);
77110
memcpy(rx_buffer + size, rx_temp, ret_read);
78111
SDL_AtomicSet(&rx_fill, size + ret_read);
112+
rx_bytes_sum += ret_read;
79113
}
80114
if (ret_write > 0) {
81115
const int size = SDL_AtomicGet(&tx_fill);
82116
if (size > ret_write)
83117
memmove(tx_buffer, tx_buffer + ret_write, size - ret_write);
84118
SDL_AtomicSet(&tx_fill, size - ret_write);
119+
tx_bytes_sum += ret_write;
85120
}
86121
SDL_AtomicUnlock(&lock);
87122
}
88123
}
124+
running = false;
89125
DEBUGPRINT("SERIALTCP: thread: end" NL);
90126
close_socket();
91127
exited = true;
@@ -102,63 +138,103 @@ int serialtcp_init ( const char *connection )
102138
ERROR_WINDOW("%s cannot init connection as it's already on-going!", error_prefix);
103139
return -1;
104140
}
105-
unsigned int ip, port;
106-
const char *errmsg = xemusock_parse_string_connection_parameters(connection, &ip, &port);
141+
// Network init must go first, as xemusock_parse_string_connection_parameters() use socket API already.
142+
// It makes a difference on Windows, where winsock initialization must be done first.
143+
const char *errmsg = xemusock_init();
107144
if (errmsg) {
108-
ERROR_WINDOW("%s parsing: %s", error_prefix, errmsg);
109-
return -1;
110-
}
111-
if (port < 1 || port >= 0x10000 || !ip) {
112-
ERROR_WINDOW("%s parsing: bad IP and/or port: %s", error_prefix, connection);
145+
ERROR_WINDOW("%s network init problem:\n%s", error_prefix, errmsg);
113146
return -1;
114147
}
115-
errmsg = xemusock_init();
148+
unsigned int ip, port;
149+
errmsg = xemusock_parse_string_connection_parameters(connection, &ip, &port);
116150
if (errmsg) {
117-
ERROR_WINDOW("%s network init problem: %s", error_prefix, errmsg);
151+
ERROR_WINDOW("%s target parsing:\n%s", error_prefix, errmsg);
118152
return -1;
119153
}
120154
int xerr;
121155
sock = xemusock_create_for_inet(XEMUSOCK_TCP, XEMUSOCK_BLOCKING, &xerr);
122156
if (sock == XS_INVALID_SOCKET) {
123-
ERROR_WINDOW("%s cannot create TCP socket: %s", error_prefix, xemusock_strerror(xerr));
157+
ERROR_WINDOW("%s cannot create TCP socket:\n%s", error_prefix, xemusock_strerror(xerr));
124158
return -1;
125159
}
126160
struct sockaddr_in sock_st;
127161
xemusock_fill_servaddr_for_inet_ip_netlong(&sock_st, ip, port);
128162
if (xemusock_connect(sock, &sock_st, &xerr)) {
129-
ERROR_WINDOW("%s cannot connect to TCP target: %s", error_prefix, xemusock_strerror(xerr));
163+
ERROR_WINDOW("%s cannot connect to TCP target:\n%s", error_prefix, xemusock_strerror(xerr));
130164
close_socket();
131165
return -1;
132166
}
133167
if (xemusock_setsockopt_keepalive(sock, &xerr))
134-
ERROR_WINDOW("%s warning, could not set KEEPALIVE: %s", error_prefix, xemusock_strerror(xerr));
168+
ERROR_WINDOW("%s warning, could not set KEEPALIVE:\n%s", error_prefix, xemusock_strerror(xerr));
135169
if (xemusock_set_nonblocking(sock, 1, &xerr)) {
136-
ERROR_WINDOW("%s cannot set unblock for TCP target: %s", error_prefix, xemusock_strerror(xerr));
170+
ERROR_WINDOW("%s cannot set unblock for TCP target:\n%s", error_prefix, xemusock_strerror(xerr));
137171
close_socket();
138172
return -1;
139173
}
140174
// Reset data structures
141175
SDL_AtomicSet(&rx_fill, 0);
142176
SDL_AtomicSet(&tx_fill, 0);
143177
SDL_AtomicUnlock(&lock);
144-
running = true;
145-
exited = false;
146178
memset(serial_regs, 0, sizeof serial_regs);
179+
rx_bytes_sum = 0;
180+
tx_bytes_sum = 0;
147181
bitrate_divisor = 0;
148182
baudrate = 19200;
183+
const unsigned int ip_native = ntohl(ip);
184+
char ip_string[16];
185+
snprintf(ip_string, sizeof ip_string, "%u.%u.%u.%u", (ip_native >> 24) & 0xFF, (ip_native >> 16) & 0xFF, (ip_native >> 8) & 0xFF, ip_native & 0xFF);
186+
free(connection_string);
187+
connection_string = xemu_strdup(connection);
188+
if (strncmp(ip_string, connection, strlen(ip_string))) {
189+
const int size = strlen(connection) + strlen(ip_string) + 10;
190+
connection_desc = xemu_realloc(connection_desc, size);
191+
snprintf(connection_desc, size, "%s (%s)", connection, ip_string);
192+
} else {
193+
connection_desc = xemu_realloc(connection_desc, strlen(connection) + 1);
194+
strcpy(connection_desc, connection);
195+
}
196+
exited = false;
197+
running = true;
198+
failed = NULL;
149199
// Start thread
200+
DEBUGPRINT("SERIALTCP: starting connection thread to %s" NL, connection_desc);
150201
thread_id = SDL_CreateThread(the_thread, "Xemu-SerialTCP", NULL);
151202
if (!thread_id) {
152203
running = false;
153204
close_socket();
154-
ERROR_WINDOW("%s cannot create thread: %s", error_prefix, SDL_GetError());
205+
ERROR_WINDOW("%s cannot create thread:\n%s", error_prefix, SDL_GetError());
155206
return -1;
156207
}
157-
OSD(-1, -1, "SerialTCP connection established to\n%s", connection);
208+
OSD(-1, -1, "SerialTCP connection established to\n%s", connection_desc);
158209
return 0;
159210
}
160211

161212

213+
bool serialtcp_get_connection_desc ( char *param, const unsigned int param_size, char *desc, const unsigned int desc_size, int *tx, int *rx )
214+
{
215+
if (param)
216+
snprintf(param, param_size, "%s", connection_string ? connection_string : "");
217+
if (desc)
218+
snprintf(desc, desc_size, "%s", connection_desc ? connection_desc : "");
219+
if (tx)
220+
*tx = tx_bytes_sum;
221+
if (rx)
222+
*rx = rx_bytes_sum;
223+
return running;
224+
}
225+
226+
227+
const char *serialtcp_get_connection_error ( const bool remove_error )
228+
{
229+
if (running)
230+
return NULL;
231+
const char *err = (const char *)failed;
232+
if (remove_error)
233+
failed = NULL;
234+
return err;
235+
}
236+
237+
162238
int serialtcp_shutdown ( void )
163239
{
164240
if (!running) {
@@ -189,7 +265,7 @@ int serialtcp_shutdown ( void )
189265
int serialtcp_restart ( const char *connection )
190266
{
191267
if (!connection || !*connection) {
192-
ERROR_WINDOW("Cannot restart SerialTCP: no target specification");
268+
ERROR_WINDOW("Cannot restart SerialTCP:\nno target specification");
193269
return -1;
194270
}
195271
DEBUGPRINT("SERIALTCP: restarting connection ..." NL);
@@ -209,7 +285,7 @@ static int send_byte ( const Uint8 byte )
209285
tx_buffer[size] = byte;
210286
SDL_AtomicSet(&tx_fill, size + 1);
211287
SDL_AtomicUnlock(&lock);
212-
DEBUGPRINT("SERIALTCP: byte sent: $%02X" NL, byte);
288+
// DEBUGPRINT("SERIALTCP: byte sent: $%02X" NL, byte);
213289
return 0;
214290
}
215291

@@ -230,7 +306,7 @@ static int recv_byte ( const bool also_remove )
230306
SDL_AtomicSet(&rx_fill, size - 1);
231307
}
232308
SDL_AtomicUnlock(&lock);
233-
DEBUGPRINT("SERIALTCP: byte %s $%02X" NL, also_remove ? "removed" : "received", byte);
309+
// DEBUGPRINT("SERIALTCP: byte %s $%02X" NL, also_remove ? "removed" : "received", byte);
234310
return byte;
235311
}
236312

targets/mega65/serialtcp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
2323
extern int serialtcp_init ( const char *connection );
2424
extern int serialtcp_shutdown ( void );
2525
extern int serialtcp_restart ( const char *connection );
26+
extern bool serialtcp_get_connection_desc ( char *param, const unsigned int param_size, char *desc, const unsigned int desc_size, int *tx, int *rx );
27+
extern const char *serialtcp_get_connection_error ( const bool remove_error );
2628

2729
extern void serialtcp_write_reg ( const int reg, const Uint8 data );
2830
extern Uint8 serialtcp_read_reg ( const int reg );

xemu/emutools_socketapi.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,11 @@ const char *xemusock_parse_string_connection_parameters ( const char *str, unsig
219219
}
220220
}
221221
if (s_port) {
222-
*port = atoi(s_port);
223-
if (*port && (*port < 1 || *port > 65535)) {
222+
char *endptr;
223+
*port = strtol(s_port, &endptr, 10);
224+
if (*s_port == '\0' || (*endptr != '\0') || (*port < 1 || *port > 65535)) {
224225
free(s);
225-
return "Bad port";
226+
return "Bad port number";
226227
}
227228
}
228229
free(s);

0 commit comments

Comments
 (0)