Skip to content

Commit fc3038a

Browse files
pedgarciamikrosk
authored andcommitted
NetSIO: socket options for port sharing and proper shutdown
Allow emulator and FujiNet-PC to share port 9997 via SO_REUSEADDR/SO_REUSEPORT. Implement netsio_shutdown() and add re-init guard for emulator restarts.
1 parent 4804056 commit fc3038a

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

‎src/netsio.c‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ static void send_block_to_fujinet(const uint8_t *block, size_t len);
3030

3131
/* Flag to know when netsio is enabled */
3232
volatile int netsio_enabled = 0;
33+
/* Flag to track if NetSIO has been initialized (prevents re-initialization) */
34+
static int netsio_initialized = 0;
3335
/* Holds sync to fujinet-pc incremented number */
3436
uint8_t netsio_sync_num = 0;
3537
/* if we have heard from fujinet-pc or not */
@@ -209,6 +211,14 @@ int netsio_init(uint16_t port) {
209211
pthread_t rx_thread;
210212
int broadcast = 1;
211213

214+
/* Skip re-initialization if already initialized (during emulator restarts) */
215+
if (netsio_initialized) {
216+
#ifdef DEBUG
217+
Log_print("netsio: already initialized, skipping re-initialization");
218+
#endif
219+
return 0;
220+
}
221+
212222
/* create emulator <-> netsio FIFOs */
213223
if (pipe(fds0) < 0)
214224
{
@@ -255,6 +265,25 @@ int netsio_init(uint16_t port) {
255265
#endif
256266
}
257267

268+
/* Enable SO_REUSEADDR to allow binding even if the port is in TIME_WAIT state */
269+
int reuse = 1;
270+
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
271+
{
272+
#ifdef DEBUG
273+
Log_print("netsio setsockopt SO_REUSEADDR failed");
274+
#endif
275+
}
276+
277+
/* On macOS/BSD, SO_REUSEPORT is required to allow multiple processes to receive on same port */
278+
#ifdef SO_REUSEPORT
279+
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)) < 0)
280+
{
281+
#ifdef DEBUG
282+
Log_print("netsio setsockopt SO_REUSEPORT failed");
283+
#endif
284+
}
285+
#endif
286+
258287
/* Bind to the socket on requested port */
259288
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
260289
{
@@ -275,6 +304,12 @@ int netsio_init(uint16_t port) {
275304
}
276305
pthread_detach(rx_thread);
277306

307+
/* Mark as initialized to prevent re-initialization */
308+
netsio_initialized = 1;
309+
#ifdef DEBUG
310+
Log_print("netsio: initialization complete");
311+
#endif
312+
278313
return 0;
279314
}
280315

@@ -731,3 +766,55 @@ static void *fujinet_rx_thread(void *arg) {
731766
}
732767
return NULL;
733768
}
769+
770+
/* Shutdown NetSIO subsystem and clean up resources */
771+
void netsio_shutdown(void) {
772+
#ifdef DEBUG
773+
Log_print("netsio: shutting down...");
774+
#endif
775+
776+
/* Send disconnect notification to FujiNet-PC (if connected) */
777+
if (netsio_enabled) {
778+
uint8_t pkt = NETSIO_DEVICE_DISCONNECTED;
779+
send_to_fujinet(&pkt, 1);
780+
#ifdef DEBUG
781+
Log_print("netsio: sent disconnect notification");
782+
#endif
783+
}
784+
785+
/* Close UDP socket - this will cause fujinet_rx_thread's recvfrom() to fail and exit */
786+
if (sockfd >= 0) {
787+
#ifdef DEBUG
788+
Log_print("netsio: closing socket %d", sockfd);
789+
#endif
790+
close(sockfd);
791+
sockfd = -1;
792+
}
793+
794+
/* Close FIFO pipes */
795+
if (fds0[0] >= 0) {
796+
close(fds0[0]);
797+
fds0[0] = -1;
798+
}
799+
if (fds0[1] >= 0) {
800+
close(fds0[1]);
801+
fds0[1] = -1;
802+
}
803+
804+
/* Reset global state variables */
805+
netsio_enabled = 0;
806+
netsio_initialized = 0; /* Allow re-initialization after shutdown */
807+
netsio_sync_num = 0;
808+
fujinet_known = 0;
809+
netsio_sync_wait = 0;
810+
netsio_cmd_state = 0;
811+
netsio_next_write_size = 0;
812+
813+
/* Clear address info */
814+
memset(&fujinet_addr, 0, sizeof(fujinet_addr));
815+
fujinet_addr_len = sizeof(fujinet_addr);
816+
817+
#ifdef DEBUG
818+
Log_print("netsio: shutdown complete");
819+
#endif
820+
}

0 commit comments

Comments
 (0)