@@ -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 */
3232volatile 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 */
3436uint8_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