diff --git a/BasiliskII/src/Windows/Makefile.in b/BasiliskII/src/Windows/Makefile.in index c5505e3d..5bb0acfa 100755 --- a/BasiliskII/src/Windows/Makefile.in +++ b/BasiliskII/src/Windows/Makefile.in @@ -21,16 +21,26 @@ WANT_GTK = @WANT_GTK@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ +SLIRP_CFLAGS = @SLIRP_CFLAGS@ +SLIRP_SRCS = \ + ../slirp/bootp.c ../slirp/ip_output.c ../slirp/tcp_input.c \ + ../slirp/cksum.c ../slirp/mbuf.c ../slirp/tcp_output.c \ + ../slirp/debug.c ../slirp/misc.c ../slirp/tcp_subr.c \ + ../slirp/if.c ../slirp/sbuf.c ../slirp/tcp_timer.c \ + ../slirp/ip_icmp.c ../slirp/slirp.c ../slirp/tftp.c \ + ../slirp/ip_input.c ../slirp/socket.c ../slirp/udp.c +SLIRP_OBJS = $(SLIRP_SRCS:../slirp/%.c=$(OBJ_DIR)/slirp-%.o) + LN_S = @LN_S@ WINDRES = @WINDRES@ CC = @CC@ CXX = @CXX@ CFLAGS = @CFLAGS@ $(SDL_CFLAGS) CXXFLAGS = @CXXFLAGS@ $(SDL_CFLAGS) -CPPFLAGS = @CPPFLAGS@ -I../include -I. @CPUINCLUDES@ +CPPFLAGS = @CPPFLAGS@ -I../include -I. @CPUINCLUDES@ -I../slirp DEFS = @DEFS@ @DEFINES@ LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ +LIBS = @LIBS@ -lwsock32 -liphlpapi CPUSRCS = @CPUSRCS@ HOST_CC = gcc @@ -58,7 +68,7 @@ SRCS = ../main.cpp main_windows.cpp ../prefs.cpp ../prefs_items.cpp prefs_window ../extfs.cpp extfs_windows.cpp ../user_strings.cpp user_strings_windows.cpp \ vm_alloc.cpp sigsegv.cpp posix_emu.cpp util_windows.cpp kernel_windows.cpp \ ../dummy/prefs_editor_dummy.cpp BasiliskII.rc \ - $(CDENABLESRCS) $(ROUTERSRCS) $(CPUSRCS) + $(CDENABLESRCS) $(ROUTERSRCS) $(CPUSRCS) $(SLIRP_OBJS) UI_SRCS = ../prefs.cpp prefs_windows.cpp prefs_editor_gtk.cpp xpram_windows.cpp \ ../prefs_items.cpp ../user_strings.cpp user_strings_windows.cpp util_windows.cpp \ @@ -130,6 +140,8 @@ $(OBJ_DIR)/%.ho : %.c $(HOST_CC) $(CPPFLAGS) $(DEFS) $(HOST_CFLAGS) -c $< -o $@ $(OBJ_DIR)/%.ho : %.cpp $(HOST_CXX) $(CPPFLAGS) $(DEFS) $(HOST_CXXFLAGS) -c $< -o $@ +$(OBJ_DIR)/slirp-%.o : ../slirp/%.c + $(CC) $(CPPFLAGS) $(DEFS) $(CFLAGS) $(SLIRP_CFLAGS) -c $< -o $@ $(OBJ_DIR)/%.o : %.c $(CC) $(CPPFLAGS) $(DEFS) $(CFLAGS) -c $< -o $@ $(OBJ_DIR)/%.o : %.cpp diff --git a/BasiliskII/src/Windows/configure.ac b/BasiliskII/src/Windows/configure.ac index bfc8415c..cf696c4a 100755 --- a/BasiliskII/src/Windows/configure.ac +++ b/BasiliskII/src/Windows/configure.ac @@ -105,6 +105,9 @@ AC_CHECK_TYPES(loff_t) AC_CHECK_TYPES(caddr_t) AC_TYPE_SIZE_T +dnl Checks for library functions. +AC_CHECK_FUNCS(strdup strerror) + dnl Define a macro that translates a yesno-variable into a C macro definition dnl to be put into the config.h file dnl $1 -- the macro to define @@ -259,6 +262,19 @@ AC_EGREP_CPP(xyes, #endif ], [AC_MSG_RESULT(yes); HAVE_GCC30=yes], AC_MSG_RESULT(no)) +dnl Add -fno-strict-aliasing for slirp sources +if [[ "x$HAVE_GCC30" = "xyes" ]]; then + SAVED_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-strict-aliasing" + AC_CACHE_CHECK([whether the compiler supports -fno-strict-aliasing], + ac_cv_gcc_no_strict_aliasing, [ + AC_TRY_COMPILE([],[], + [ac_cv_gcc_no_strict_aliasing=yes; AC_SUBST(SLIRP_CFLAGS, "-fno-strict-aliasing")], + [ac_cv_gcc_no_strict_aliasing=no]) + ]) + CFLAGS="$SAVED_CFLAGS" +fi + dnl Select appropriate CPU source and REGPARAM define. ASM_OPTIMIZATIONS=none CPUSRCS="cpuemu1.cpp cpuemu2.cpp cpuemu3.cpp cpuemu4.cpp cpuemu5.cpp cpuemu6.cpp cpuemu7.cpp cpuemu8.cpp" diff --git a/BasiliskII/src/Windows/ether_windows.cpp b/BasiliskII/src/Windows/ether_windows.cpp index f1f0054e..3800fc87 100755 --- a/BasiliskII/src/Windows/ether_windows.cpp +++ b/BasiliskII/src/Windows/ether_windows.cpp @@ -1,1233 +1,1321 @@ -/* - * ether_windows.cpp - Ethernet device driver - * - * Basilisk II (C) 1997-2005 Christian Bauer - * - * Windows platform specific code copyright (C) Lauri Pesonen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include - -#include "sysdeps.h" -#include "cpu_emulation.h" -#include "main.h" -#include "macos_util.h" -#include "prefs.h" -#include "user_strings.h" -#include "ether.h" -#include "ether_defs.h" -#include "b2ether/multiopt.h" -#include "b2ether/inc/b2ether_hl.h" -#include "ether_windows.h" -#include "router/router.h" -#include "kernel_windows.h" - - -#define DEBUG 0 -#define MONITOR 0 - -#if DEBUG -#pragma optimize("",off) -#endif - -#include "debug.h" - - -// Ethernet device types -enum { - NET_IF_B2ETHER, - NET_IF_ROUTER, - NET_IF_FAKE, -}; - -// Options -bool ether_use_permanent = true; -static int16 ether_multi_mode = ETHER_MULTICAST_MAC; - -// Global variables -HANDLE ether_th; -unsigned int ether_tid; -HANDLE ether_th1; -HANDLE ether_th2; -static int net_if_type = -1; // Ethernet device type -#ifdef SHEEPSHAVER -static bool net_open = false; // Flag: initialization succeeded, network device open -uint8 ether_addr[6]; // Our Ethernet address -#endif - -// These are protected by queue_csection -// Controls transfer for read thread to feed thread -static CRITICAL_SECTION queue_csection; -typedef struct _win_queue_t { - uint8 *buf; - int sz; -} win_queue_t; -#define MAX_QUEUE_ITEMS 1024 -static win_queue_t queue[MAX_QUEUE_ITEMS]; -static int queue_head = 0; -static int queue_inx = 0; -static bool wait_request = true; - - - -// Read thread protected packet pool -static CRITICAL_SECTION fetch_csection; -// Some people use pools as large as 64. -#define PACKET_POOL_COUNT 10 -static LPPACKET packets[PACKET_POOL_COUNT]; -static bool wait_request2 = false; - - - -// Write thread packet queue -static CRITICAL_SECTION send_csection; -static LPPACKET send_queue = 0; - - -// Write thread free packet pool -static CRITICAL_SECTION wpool_csection; -static LPPACKET write_packet_pool = 0; - - - -// Try to deal with echos. Protected by fetch_csection. -// The code should be moved to the driver. No need to lift -// the echo packets to the application level. -// MAX_ECHO must be a power of two. -#define MAX_ECHO (1<<2) -static int echo_count = 0; -typedef uint8 echo_t[1514]; -static echo_t pending_packet[MAX_ECHO]; -static int pending_packet_sz[MAX_ECHO]; - - -// List of attached protocols -struct NetProtocol { - NetProtocol *next; - uint16 type; - uint32 handler; -}; - -static NetProtocol *prot_list = NULL; - - -static LPADAPTER fd = 0; -static bool thread_active = false; -static bool thread_active_1 = false; -static bool thread_active_2 = false; -static bool thread_active_3 = false; -static HANDLE int_ack = 0; -static HANDLE int_sig = 0; -static HANDLE int_sig2 = 0; -static HANDLE int_send_now = 0; - -// Prototypes -static WINAPI unsigned int ether_thread_feed_int(void *arg); -static WINAPI unsigned int ether_thread_get_packets_nt(void *arg); -static WINAPI unsigned int ether_thread_write_packets(void *arg); -static void init_queue(void); -static void final_queue(void); -static bool allocate_read_packets(void); -static void free_read_packets(void); -static void free_write_packets(void); -static int16 ether_do_add_multicast(uint8 *addr); -static int16 ether_do_del_multicast(uint8 *addr); -static int16 ether_do_write(uint32 arg); -static void ether_do_interrupt(void); - - -/* - * Find protocol in list - */ - -static NetProtocol *find_protocol(uint16 type) -{ - // All 802.2 types are the same - if (type <= 1500) - type = 0; - - // Search list (we could use hashing here but there are usually only three - // handlers installed: 0x0000 for AppleTalk and 0x0800/0x0806 for TCP/IP) - NetProtocol *p = prot_list; - while (p) { - if (p->type == type) - return p; - p = p->next; - } - return NULL; -} - - -/* - * Initialization - */ - -bool ether_init(void) -{ - char str[256]; - - // Do nothing if no Ethernet device specified - const char *name = PrefsFindString("ether"); - if (name == NULL) - return false; - - ether_multi_mode = PrefsFindInt32("ethermulticastmode"); - ether_use_permanent = PrefsFindBool("etherpermanentaddress"); - - // Determine Ethernet device type - net_if_type = -1; - if (strcmp(name, "router") == 0) - net_if_type = NET_IF_ROUTER; - else - net_if_type = NET_IF_B2ETHER; - - // Initialize NAT-Router - if (net_if_type == NET_IF_ROUTER) { - if (!router_init()) - net_if_type = NET_IF_FAKE; - } - - // Open ethernet device - const char *dev_name; - switch (net_if_type) { - case NET_IF_B2ETHER: - dev_name = PrefsFindString("etherguid"); - break; - } - if (net_if_type == NET_IF_B2ETHER) { - if (dev_name == NULL) { - WarningAlert("No ethernet device GUID specified. Ethernet is not available."); - goto open_error; - } - - fd = PacketOpenAdapter( dev_name, ether_multi_mode ); - if (!fd) { - sprintf(str, "Could not open ethernet adapter %s.", dev_name); - WarningAlert(str); - goto open_error; - } - - // Get Ethernet address - if(!PacketGetMAC(fd,ether_addr,ether_use_permanent)) { - sprintf(str, "Could not get hardware address of device %s. Ethernet is not available.", dev_name); - WarningAlert(str); - goto open_error; - } - D(bug("Real ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); - - const char *ether_fake_address; - ether_fake_address = PrefsFindString("etherfakeaddress"); - if(ether_fake_address && strlen(ether_fake_address) == 12) { - char sm[10]; - strcpy( sm, "0x00" ); - for( int i=0; i<6; i++ ) { - sm[2] = ether_fake_address[i*2]; - sm[3] = ether_fake_address[i*2+1]; - ether_addr[i] = (uint8)strtoul(sm,0,0); - } - D(bug("Fake ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); - } - } - else { - memcpy( ether_addr, router_mac_addr, 6 ); - D(bug("Fake ethernet address (same as router) %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); - } - - // Start packet reception thread - int_ack = CreateSemaphore( 0, 0, 1, NULL); - if(!int_ack) { - WarningAlert("WARNING: Cannot create int_ack semaphore"); - goto open_error; - } - - // nonsignaled - int_sig = CreateSemaphore( 0, 0, 1, NULL); - if(!int_sig) { - WarningAlert("WARNING: Cannot create int_sig semaphore"); - goto open_error; - } - - int_sig2 = CreateSemaphore( 0, 0, 1, NULL); - if(!int_sig2) { - WarningAlert("WARNING: Cannot create int_sig2 semaphore"); - goto open_error; - } - - int_send_now = CreateSemaphore( 0, 0, 1, NULL); - if(!int_send_now) { - WarningAlert("WARNING: Cannot create int_send_now semaphore"); - goto open_error; - } - - init_queue(); - - if(!allocate_read_packets()) goto open_error; - - // No need to enter wait state if we can avoid it. - // These all terminate fast. - - if(pfnInitializeCriticalSectionAndSpinCount) { - pfnInitializeCriticalSectionAndSpinCount( &fetch_csection, 5000 ); - } else { - InitializeCriticalSection( &fetch_csection ); - } - if(pfnInitializeCriticalSectionAndSpinCount) { - pfnInitializeCriticalSectionAndSpinCount( &queue_csection, 5000 ); - } else { - InitializeCriticalSection( &queue_csection ); - } - if(pfnInitializeCriticalSectionAndSpinCount) { - pfnInitializeCriticalSectionAndSpinCount( &send_csection, 5000 ); - } else { - InitializeCriticalSection( &send_csection ); - } - if(pfnInitializeCriticalSectionAndSpinCount) { - pfnInitializeCriticalSectionAndSpinCount( &wpool_csection, 5000 ); - } else { - InitializeCriticalSection( &wpool_csection ); - } - - ether_th = (HANDLE)_beginthreadex( 0, 0, ether_thread_feed_int, 0, 0, ðer_tid ); - if (!ether_th) { - D(bug("Failed to create ethernet thread\n")); - goto open_error; - } - thread_active = true; - - unsigned int dummy; - ether_th2 = (HANDLE)_beginthreadex( 0, 0, ether_thread_get_packets_nt, 0, 0, &dummy ); - ether_th1 = (HANDLE)_beginthreadex( 0, 0, ether_thread_write_packets, 0, 0, &dummy ); - - // Everything OK - return true; - - open_error: - if (thread_active) { - TerminateThread(ether_th,0); - ether_th = 0; - if (int_ack) - CloseHandle(int_ack); - int_ack = 0; - if(int_sig) - CloseHandle(int_sig); - int_sig = 0; - if(int_sig2) - CloseHandle(int_sig2); - int_sig2 = 0; - if(int_send_now) - CloseHandle(int_send_now); - int_send_now = 0; - thread_active = false; - } - if(net_if_type == NET_IF_B2ETHER) { - PacketCloseAdapter(fd); - } - fd = 0; - return false; -} - - -/* - * Deinitialization - */ - -void ether_exit(void) -{ - D(bug("EtherExit\n")); - - // Stop reception thread - thread_active = false; - - if(int_ack) ReleaseSemaphore(int_ack,1,NULL); - if(int_sig) ReleaseSemaphore(int_sig,1,NULL); - if(int_sig2) ReleaseSemaphore(int_sig2,1,NULL); - if(int_send_now) ReleaseSemaphore(int_send_now,1,NULL); - - D(bug("CancelIO if needed\n")); - if (fd && fd->hFile && pfnCancelIo) - pfnCancelIo(fd->hFile); - - // Wait max 2 secs to shut down pending io. After that, kill them. - D(bug("Wait delay\n")); - for( int i=0; i<10; i++ ) { - if(!thread_active_1 && !thread_active_2 && !thread_active_3) break; - Sleep(200); - } - - if(thread_active_1) { - D(bug("Ether killing ether_th1\n")); - if(ether_th1) TerminateThread(ether_th1,0); - thread_active_1 = false; - } - if(thread_active_2) { - D(bug("Ether killing ether_th2\n")); - if(ether_th2) TerminateThread(ether_th2,0); - thread_active_2 = false; - } - if(thread_active_3) { - D(bug("Ether killing thread\n")); - if(ether_th) TerminateThread(ether_th,0); - thread_active_3 = false; - } - - ether_th1 = 0; - ether_th2 = 0; - ether_th = 0; - - D(bug("Closing semaphores\n")); - if(int_ack) { - CloseHandle(int_ack); - int_ack = 0; - } - if(int_sig) { - CloseHandle(int_sig); - int_sig = 0; - } - if(int_sig2) { - CloseHandle(int_sig2); - int_sig2 = 0; - } - if(int_send_now) { - CloseHandle(int_send_now); - int_send_now = 0; - } - - // Close ethernet device - if(fd) { - PacketCloseAdapter(fd); - fd = 0; - } - - // Remove all protocols - D(bug("Removing protocols\n")); - NetProtocol *p = prot_list; - while (p) { - NetProtocol *next = p->next; - delete p; - p = next; - } - prot_list = 0; - - D(bug("Deleting sections\n")); - DeleteCriticalSection( &fetch_csection ); - DeleteCriticalSection( &queue_csection ); - DeleteCriticalSection( &send_csection ); - DeleteCriticalSection( &wpool_csection ); - - D(bug("Freeing read packets\n")); - free_read_packets(); - - D(bug("Freeing write packets\n")); - free_write_packets(); - - D(bug("Finalizing queue\n")); - final_queue(); - - if (net_if_type == NET_IF_ROUTER) { - D(bug("Stopping router\n")); - router_final(); - } - - D(bug("EtherExit done\n")); -} - - -/* - * Glue around low-level implementation - */ - -#ifdef SHEEPSHAVER -// Error codes -enum { - eMultiErr = -91, - eLenErr = -92, - lapProtErr = -94, - excessCollsns = -95 -}; - -// Initialize ethernet -void EtherInit(void) -{ - net_open = false; - - // Do nothing if the user disabled the network - if (PrefsFindBool("nonet")) - return; - - net_open = ether_init(); -} - -// Exit ethernet -void EtherExit(void) -{ - ether_exit(); - net_open = false; -} - -// Get ethernet hardware address -void AO_get_ethernet_address(uint32 arg) -{ - uint8 *addr = Mac2HostAddr(arg); - if (net_open) - OTCopy48BitAddress(ether_addr, addr); - else { - addr[0] = 0x12; - addr[1] = 0x34; - addr[2] = 0x56; - addr[3] = 0x78; - addr[4] = 0x9a; - addr[5] = 0xbc; - } - D(bug("AO_get_ethernet_address: got address %02x%02x%02x%02x%02x%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])); -} - -// Add multicast address -void AO_enable_multicast(uint32 addr) -{ - if (net_open) - ether_do_add_multicast(Mac2HostAddr(addr)); -} - -// Disable multicast address -void AO_disable_multicast(uint32 addr) -{ - if (net_open) - ether_do_del_multicast(Mac2HostAddr(addr)); -} - -// Transmit one packet -void AO_transmit_packet(uint32 mp) -{ - if (net_open) { - switch (ether_do_write(mp)) { - case noErr: - num_tx_packets++; - break; - case excessCollsns: - num_tx_buffer_full++; - break; - } - } -} - -// Copy packet data from message block to linear buffer -static inline int ether_arg_to_buffer(uint32 mp, uint8 *p) -{ - return ether_msgb_to_buffer(mp, p); -} - -// Ethernet interrupt -void EtherIRQ(void) -{ - D(bug("EtherIRQ\n")); - num_ether_irq++; - - OTEnterInterrupt(); - ether_do_interrupt(); - OTLeaveInterrupt(); - - // Acknowledge interrupt to reception thread - D(bug(" EtherIRQ done\n")); - ReleaseSemaphore(int_ack,1,NULL); -} -#else -// Add multicast address -int16 ether_add_multicast(uint32 pb) -{ - return ether_do_add_multicast(Mac2HostAddr(pb + eMultiAddr)); -} - -// Disable multicast address -int16 ether_del_multicast(uint32 pb) -{ - return ether_do_del_multicast(Mac2HostAddr(pb + eMultiAddr)); -} - -// Transmit one packet -int16 ether_write(uint32 wds) -{ - return ether_do_write(wds); -} - -// Copy packet data from WDS to linear buffer -static inline int ether_arg_to_buffer(uint32 wds, uint8 *p) -{ - return ether_wds_to_buffer(wds, p); -} - -// Dispatch packet to protocol handler -static void ether_dispatch_packet(uint32 packet, uint32 length) -{ - // Get packet type - uint16 type = ReadMacInt16(packet + 12); - - // Look for protocol - NetProtocol *prot = find_protocol(type); - if (prot == NULL) - return; - - // No default handler - if (prot->handler == 0) - return; - - // Copy header to RHA - Mac2Mac_memcpy(ether_data + ed_RHA, packet, 14); - D(bug(" header %08lx%04lx %08lx%04lx %04lx\n", ReadMacInt32(ether_data + ed_RHA), ReadMacInt16(ether_data + ed_RHA + 4), ReadMacInt32(ether_data + ed_RHA + 6), ReadMacInt16(ether_data + ed_RHA + 10), ReadMacInt16(ether_data + ed_RHA + 12))); - - // Call protocol handler - M68kRegisters r; - r.d[0] = type; // Packet type - r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket) - r.a[0] = packet + 14; // Pointer to packet (Mac address, for ReadPacket) - r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA - r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines - D(bug(" calling protocol handler %08lx, type %08lx, length %08lx, data %08lx, rha %08lx, read_packet %08lx\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4])); - Execute68k(prot->handler, &r); -} - -// Ethernet interrupt -void EtherInterrupt(void) -{ - D(bug("EtherIRQ\n")); - ether_do_interrupt(); - - // Acknowledge interrupt to reception thread - D(bug(" EtherIRQ done\n")); - ReleaseSemaphore(int_ack,1,NULL); -} -#endif - - -/* - * Reset - */ - -void ether_reset(void) -{ - D(bug("EtherReset\n")); - - // Remove all protocols - NetProtocol *p = prot_list; - while (p) { - NetProtocol *next = p->next; - delete p; - p = next; - } - prot_list = NULL; -} - - -/* - * Add multicast address - */ - -static int16 ether_do_add_multicast(uint8 *addr) -{ - D(bug("ether_add_multicast\n")); - - // We wouldn't need to do this - // if(ether_multi_mode != ETHER_MULTICAST_MAC) return noErr; - - switch (net_if_type) { - case NET_IF_B2ETHER: - if (!PacketAddMulticast( fd, addr)) { - D(bug("WARNING: couldn't enable multicast address\n")); - return eMultiErr; - } - default: - D(bug("ether_add_multicast: noErr\n")); - return noErr; - } -} - - -/* - * Delete multicast address - */ - -int16 ether_do_del_multicast(uint8 *addr) -{ - D(bug("ether_del_multicast\n")); - - // We wouldn't need to do this - // if(ether_multi_mode != ETHER_MULTICAST_MAC) return noErr; - - switch (net_if_type) { - case NET_IF_B2ETHER: - if (!PacketDelMulticast( fd, addr)) { - D(bug("WARNING: couldn't disable multicast address\n")); - return eMultiErr; - } - default: - return noErr; - } -} - - -/* - * Attach protocol handler - */ - -int16 ether_attach_ph(uint16 type, uint32 handler) -{ - D(bug("ether_attach_ph type=0x%x, handler=0x%x\n",(int)type,handler)); - - // Already attached? - NetProtocol *p = find_protocol(type); - if (p != NULL) { - D(bug("ether_attach_ph: lapProtErr\n")); - return lapProtErr; - } else { - // No, create and attach - p = new NetProtocol; - p->next = prot_list; - p->type = type; - p->handler = handler; - prot_list = p; - D(bug("ether_attach_ph: noErr\n")); - return noErr; - } -} - - -/* - * Detach protocol handler - */ - -int16 ether_detach_ph(uint16 type) -{ - D(bug("ether_detach_ph type=%08lx\n",(int)type)); - - NetProtocol *p = find_protocol(type); - if (p != NULL) { - NetProtocol *previous = 0; - NetProtocol *q = prot_list; - while(q) { - if (q == p) { - if(previous) { - previous->next = q->next; - } else { - prot_list = q->next; - } - delete p; - return noErr; - } - previous = q; - q = q->next; - } - } - return lapProtErr; -} - -#if MONITOR -static void dump_packet( uint8 *packet, int length ) -{ - char buf[1000], sm[10]; - - *buf = 0; - - if(length > 256) length = 256; - - for (int i=0; inext = 0; - if(send_queue) { - LPPACKET p = send_queue; - // The queue is short. It would be larger overhead to double-link it. - while(p->next) p = p->next; - p->next = Packet; - } else { - send_queue = Packet; - } - LeaveCriticalSection( &send_csection ); -} - -static LPPACKET get_send_head( void ) -{ - LPPACKET Packet = 0; - - EnterCriticalSection( &send_csection ); - if(send_queue) { - Packet = send_queue; - send_queue = send_queue->next; - } - LeaveCriticalSection( &send_csection ); - - return Packet; -} - -static int get_write_packet_pool_sz( void ) -{ - LPPACKET t = write_packet_pool; - int sz = 0; - - while(t) { - t = t->next; - sz++; - } - return(sz); -} - -static void free_write_packets( void ) -{ - LPPACKET next; - int i = 0; - while(write_packet_pool) { - next = write_packet_pool->next; - D(bug("Freeing write packet %ld\n",++i)); - PacketFreePacket(write_packet_pool); - write_packet_pool = next; - } -} - -void recycle_write_packet( LPPACKET Packet ) -{ - EnterCriticalSection( &wpool_csection ); - Packet->next = write_packet_pool; - write_packet_pool = Packet; - D(bug("Pool size after recycling = %ld\n",get_write_packet_pool_sz())); - LeaveCriticalSection( &wpool_csection ); -} - -static LPPACKET get_write_packet( UINT len ) -{ - LPPACKET Packet = 0; - - EnterCriticalSection( &wpool_csection ); - if(write_packet_pool) { - Packet = write_packet_pool; - write_packet_pool = write_packet_pool->next; - Packet->OverLapped.Offset = 0; - Packet->OverLapped.OffsetHigh = 0; - Packet->Length = len; - Packet->BytesReceived = 0; - Packet->bIoComplete = FALSE; - Packet->free = TRUE; - Packet->next = 0; - // actually an auto-reset event. - if(Packet->OverLapped.hEvent) ResetEvent(Packet->OverLapped.hEvent); - } else { - Packet = PacketAllocatePacket(fd,len); - } - - D(bug("Pool size after get wr packet = %ld\n",get_write_packet_pool_sz())); - - LeaveCriticalSection( &wpool_csection ); - - return Packet; -} - -static unsigned int ether_thread_write_packets(void *arg) -{ - LPPACKET Packet; - - thread_active_1 = true; - - D(bug("ether_thread_write_packets start\n")); - - while(thread_active) { - // must be alertable, otherwise write completion is never called - WaitForSingleObjectEx(int_send_now,INFINITE,TRUE); - while( thread_active && (Packet = get_send_head()) != 0 ) { - switch (net_if_type) { - case NET_IF_ROUTER: - if(router_write_packet((uint8 *)Packet->Buffer, Packet->Length)) { - Packet->bIoComplete = TRUE; - recycle_write_packet(Packet); - } - break; - case NET_IF_FAKE: - Packet->bIoComplete = TRUE; - recycle_write_packet(Packet); - break; - case NET_IF_B2ETHER: - if(!PacketSendPacket( fd, Packet, FALSE, TRUE )) { - // already recycled if async - } - break; - } - } - } - - D(bug("ether_thread_write_packets exit\n")); - - thread_active_1 = false; - - return(0); -} - -static BOOL write_packet( uint8 *packet, int len ) -{ - LPPACKET Packet; - - D(bug("write_packet\n")); - - Packet = get_write_packet(len); - if(Packet) { - memcpy( Packet->Buffer, packet, len ); - - EnterCriticalSection( &fetch_csection ); - pending_packet_sz[echo_count] = min(sizeof(pending_packet),len); - memcpy( pending_packet[echo_count], packet, pending_packet_sz[echo_count] ); - echo_count = (echo_count+1) & (~(MAX_ECHO-1)); - LeaveCriticalSection( &fetch_csection ); - - insert_send_queue( Packet ); - - ReleaseSemaphore(int_send_now,1,NULL); - return(TRUE); - } else { - return(FALSE); - } -} - -static int16 ether_do_write(uint32 arg) -{ - D(bug("ether_write\n")); - - // Copy packet to buffer - uint8 packet[1514], *p = packet; - int len = ether_arg_to_buffer(arg, p); - - if(len > 1514) { - D(bug("illegal packet length: %d\n",len)); - return eLenErr; - } else { -#if MONITOR - bug("Sending Ethernet packet (%d bytes):\n",(int)len); - dump_packet( packet, len ); -#endif - } - - // Transmit packet - if (!write_packet(packet, len)) { - D(bug("WARNING: couldn't transmit packet\n")); - return excessCollsns; - } else { - // It's up to the protocol drivers to do the error checking. Even if the - // i/o completion routine returns ok, there can be errors, so there is - // no point to wait for write completion and try to make some sense of the - // possible error codes. - return noErr; - } -} - - -static void init_queue(void) -{ - queue_inx = 0; - queue_head = 0; - - for( int i=0; i 0) { - D(bug("ethernet queue full, packet dropped\n")); - } else { - if(sz > 1514) sz = 1514; - queue[queue_inx].sz = sz; - memcpy( queue[queue_inx].buf, buf, sz ); - queue_inx++; - if(queue_inx >= MAX_QUEUE_ITEMS) queue_inx = 0; - if(wait_request) { - wait_request = false; - ReleaseSemaphore(int_sig,1,NULL); - } - } - LeaveCriticalSection( &queue_csection ); -} - -static int dequeue_packet( uint8 *buf ) -{ - int sz; - - if(!thread_active) return(0); - - EnterCriticalSection( &queue_csection ); - sz = queue[queue_head].sz; - if(sz > 0) { - memcpy( buf, queue[queue_head].buf, sz ); - queue[queue_head].sz = 0; - queue_head++; - if(queue_head >= MAX_QUEUE_ITEMS) queue_head = 0; - } - LeaveCriticalSection( &queue_csection ); - return(sz); -} - -static void trigger_queue(void) -{ - EnterCriticalSection( &queue_csection ); - if( queue[queue_head].sz > 0 ) { - D(bug(" packet received, triggering Ethernet interrupt\n")); - SetInterruptFlag(INTFLAG_ETHER); - TriggerInterrupt(); - // of course can't wait here. - } - LeaveCriticalSection( &queue_csection ); -} - -static bool set_wait_request(void) -{ - bool result; - EnterCriticalSection( &queue_csection ); - if(queue[queue_head].sz) { - result = true; - } else { - result = false; - wait_request = true; - } - LeaveCriticalSection( &queue_csection ); - return(result); -} - - -/* - * Packet reception threads - */ - -VOID CALLBACK packet_read_completion( - DWORD dwErrorCode, - DWORD dwNumberOfBytesTransfered, - LPOVERLAPPED lpOverlapped - ) -{ - EnterCriticalSection( &fetch_csection ); - - LPPACKET lpPacket = CONTAINING_RECORD(lpOverlapped,PACKET,OverLapped); - - D(bug("packet_read_completion bytes=%d, error code=%d\n",dwNumberOfBytesTransfered,dwErrorCode)); - - if(thread_active && !dwErrorCode) { - int count = min(dwNumberOfBytesTransfered,1514); - if(count) { - int j = echo_count; - for(int i=MAX_ECHO; i; i--) { - j--; - if(j < 0) j = MAX_ECHO-1; - if(count == pending_packet_sz[j] && - memcmp(pending_packet[j],lpPacket->Buffer,count) == 0) - { - D(bug("packet_read_completion discarding own packet.\n")); - dwNumberOfBytesTransfered = 0; - - j = (j+1) & (~(MAX_ECHO-1)); - if(j != echo_count) { - D(bug("Wow, this fix made some good after all...\n")); - } - - break; - } - } - if(dwNumberOfBytesTransfered) { - if(net_if_type != NET_IF_ROUTER || !router_read_packet((uint8 *)lpPacket->Buffer, dwNumberOfBytesTransfered)) { - enqueue_packet( (LPBYTE)lpPacket->Buffer, dwNumberOfBytesTransfered ); - } - } - } - } - - // actually an auto-reset event. - if(lpPacket->OverLapped.hEvent) ResetEvent(lpPacket->OverLapped.hEvent); - - lpPacket->free = TRUE; - lpPacket->bIoComplete = TRUE; - - if(wait_request2) { - wait_request2 = false; - ReleaseSemaphore(int_sig2,1,NULL); - } - - LeaveCriticalSection( &fetch_csection ); -} - -static BOOL has_no_completed_io(void) -{ - BOOL result = TRUE; - - EnterCriticalSection( &fetch_csection ); - - for( int i=0; ibIoComplete) { - result = FALSE; - break; - } - } - if(result) wait_request2 = true; - - LeaveCriticalSection( &fetch_csection ); - return(result); -} - -static bool allocate_read_packets(void) -{ - for( int i=0; ifree) { - packets[i]->free = FALSE; - if(PacketReceivePacket(fd,packets[i],FALSE)) { - if(packets[i]->bIoComplete) { - D(bug("Early io completion...\n")); - packet_read_completion( - ERROR_SUCCESS, - packets[i]->BytesReceived, - &packets[i]->OverLapped - ); - } - } else { - packets[i]->free = TRUE; - } - } - } - } - - if(thread_active && has_no_completed_io()) { - D(bug("Waiting for int_sig2\n")); - // "problem": awakens twice in a row. Fix if you increase the pool size. - WaitForSingleObjectEx(int_sig2,INFINITE,TRUE); - } - } - - D(bug("ether_thread_get_packets_nt exit\n")); - - thread_active_2 = false; - - return 0; -} - -static unsigned int ether_thread_feed_int(void *arg) -{ - bool looping; - - thread_active_3 = true; - - D(bug("ether_thread_feed_int start\n")); - - while(thread_active) { - D(bug("Waiting for int_sig\n")); - WaitForSingleObject(int_sig,INFINITE); - // Looping this way to avoid a race condition. - D(bug("Triggering\n")); - looping = true; - while(thread_active && looping) { - trigger_queue(); - // Wait for interrupt acknowledge by EtherInterrupt() - WaitForSingleObject(int_ack,INFINITE); - if(thread_active) looping = set_wait_request(); - } - D(bug("Queue empty.\n")); - } - - D(bug("ether_thread_feed_int exit\n")); - - thread_active_3 = false; - - return 0; -} - - -/* - * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers - */ - -static void ether_do_interrupt(void) -{ - // Call protocol handler for received packets - EthernetPacket ether_packet; - uint32 packet = ether_packet.addr(); - ssize_t length; - for (;;) { - - // Read packet from Ethernet device - length = dequeue_packet(Mac2HostAddr(packet)); - if (length < 14) - break; - -#if MONITOR - bug("Receiving Ethernet packet (%d bytes):\n",(int)length); - dump_packet( Mac2HostAddr(packet), length ); -#endif - - // Dispatch packet - ether_dispatch_packet(packet, length); - } -} - -#if DEBUG -#pragma optimize("",on) -#endif +/* + * ether_windows.cpp - Ethernet device driver + * + * Basilisk II (C) 1997-2005 Christian Bauer + * + * Windows platform specific code copyright (C) Lauri Pesonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "main.h" +#include "macos_util.h" +#include "prefs.h" +#include "user_strings.h" +#include "ether.h" +#include "ether_defs.h" +#include "b2ether/multiopt.h" +#include "b2ether/inc/b2ether_hl.h" +#include "ether_windows.h" +#include "router/router.h" +#include "kernel_windows.h" +#include "libslirp.h" + +// Define to let the slirp library determine the right timeout for select() +#define USE_SLIRP_TIMEOUT 1 + + +#define DEBUG 0 +#define MONITOR 0 + +#if DEBUG +#pragma optimize("",off) +#endif + +#include "debug.h" + + +// Ethernet device types +enum { + NET_IF_B2ETHER, + NET_IF_ROUTER, + NET_IF_SLIRP, + NET_IF_FAKE, +}; + +// Options +bool ether_use_permanent = true; +static int16 ether_multi_mode = ETHER_MULTICAST_MAC; + +// Global variables +HANDLE ether_th; +unsigned int ether_tid; +HANDLE ether_th1; +HANDLE ether_th2; +static int net_if_type = -1; // Ethernet device type +#ifdef SHEEPSHAVER +static bool net_open = false; // Flag: initialization succeeded, network device open +uint8 ether_addr[6]; // Our Ethernet address +#endif + +// These are protected by queue_csection +// Controls transfer for read thread to feed thread +static CRITICAL_SECTION queue_csection; +typedef struct _win_queue_t { + uint8 *buf; + int sz; +} win_queue_t; +#define MAX_QUEUE_ITEMS 1024 +static win_queue_t queue[MAX_QUEUE_ITEMS]; +static int queue_head = 0; +static int queue_inx = 0; +static bool wait_request = true; + + + +// Read thread protected packet pool +static CRITICAL_SECTION fetch_csection; +// Some people use pools as large as 64. +#define PACKET_POOL_COUNT 10 +static LPPACKET packets[PACKET_POOL_COUNT]; +static bool wait_request2 = false; + + + +// Write thread packet queue +static CRITICAL_SECTION send_csection; +static LPPACKET send_queue = 0; + + +// Write thread free packet pool +static CRITICAL_SECTION wpool_csection; +static LPPACKET write_packet_pool = 0; + + + +// Try to deal with echos. Protected by fetch_csection. +// The code should be moved to the driver. No need to lift +// the echo packets to the application level. +// MAX_ECHO must be a power of two. +#define MAX_ECHO (1<<2) +static int echo_count = 0; +typedef uint8 echo_t[1514]; +static echo_t pending_packet[MAX_ECHO]; +static int pending_packet_sz[MAX_ECHO]; + + +// List of attached protocols +struct NetProtocol { + NetProtocol *next; + uint16 type; + uint32 handler; +}; + +static NetProtocol *prot_list = NULL; + + +static LPADAPTER fd = 0; +static bool thread_active = false; +static bool thread_active_1 = false; +static bool thread_active_2 = false; +static bool thread_active_3 = false; +static HANDLE int_ack = 0; +static HANDLE int_sig = 0; +static HANDLE int_sig2 = 0; +static HANDLE int_send_now = 0; + +// Prototypes +static WINAPI unsigned int slirp_receive_func(void *arg); +static WINAPI unsigned int ether_thread_feed_int(void *arg); +static WINAPI unsigned int ether_thread_get_packets_nt(void *arg); +static WINAPI unsigned int ether_thread_write_packets(void *arg); +static void init_queue(void); +static void final_queue(void); +static bool allocate_read_packets(void); +static void free_read_packets(void); +static void free_write_packets(void); +static int16 ether_do_add_multicast(uint8 *addr); +static int16 ether_do_del_multicast(uint8 *addr); +static int16 ether_do_write(uint32 arg); +static void ether_do_interrupt(void); + + +/* + * Find protocol in list + */ + +static NetProtocol *find_protocol(uint16 type) +{ + // All 802.2 types are the same + if (type <= 1500) + type = 0; + + // Search list (we could use hashing here but there are usually only three + // handlers installed: 0x0000 for AppleTalk and 0x0800/0x0806 for TCP/IP) + NetProtocol *p = prot_list; + while (p) { + if (p->type == type) + return p; + p = p->next; + } + return NULL; +} + + +/* + * Initialization + */ + +bool ether_init(void) +{ + char str[256]; + + // Do nothing if no Ethernet device specified + const char *name = PrefsFindString("ether"); + if (name == NULL) + return false; + + ether_multi_mode = PrefsFindInt32("ethermulticastmode"); + ether_use_permanent = PrefsFindBool("etherpermanentaddress"); + + // Determine Ethernet device type + net_if_type = -1; + if (strcmp(name, "router") == 0) + net_if_type = NET_IF_ROUTER; + else if (strcmp(name, "slirp") == 0) + net_if_type = NET_IF_SLIRP; + else + net_if_type = NET_IF_B2ETHER; + + // Initialize NAT-Router + if (net_if_type == NET_IF_ROUTER) { + if (!router_init()) + net_if_type = NET_IF_FAKE; + } + + // Initialize slirp library + if (net_if_type == NET_IF_SLIRP) { + if (slirp_init() < 0) { + sprintf(str, GetString(STR_SLIRP_NO_DNS_FOUND_WARN)); + WarningAlert(str); + return false; + } + } + + // Open ethernet device + const char *dev_name; + switch (net_if_type) { + case NET_IF_B2ETHER: + dev_name = PrefsFindString("etherguid"); + break; + } + if (net_if_type == NET_IF_B2ETHER) { + if (dev_name == NULL) { + WarningAlert("No ethernet device GUID specified. Ethernet is not available."); + goto open_error; + } + + fd = PacketOpenAdapter( dev_name, ether_multi_mode ); + if (!fd) { + sprintf(str, "Could not open ethernet adapter %s.", dev_name); + WarningAlert(str); + goto open_error; + } + + // Get Ethernet address + if(!PacketGetMAC(fd,ether_addr,ether_use_permanent)) { + sprintf(str, "Could not get hardware address of device %s. Ethernet is not available.", dev_name); + WarningAlert(str); + goto open_error; + } + D(bug("Real ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); + + const char *ether_fake_address; + ether_fake_address = PrefsFindString("etherfakeaddress"); + if(ether_fake_address && strlen(ether_fake_address) == 12) { + char sm[10]; + strcpy( sm, "0x00" ); + for( int i=0; i<6; i++ ) { + sm[2] = ether_fake_address[i*2]; + sm[3] = ether_fake_address[i*2+1]; + ether_addr[i] = (uint8)strtoul(sm,0,0); + } + D(bug("Fake ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); + } + } + else if (net_if_type == NET_IF_SLIRP) { + ether_addr[0] = 0x52; + ether_addr[1] = 0x54; + ether_addr[2] = 0x00; + ether_addr[3] = 0x12; + ether_addr[4] = 0x34; + ether_addr[5] = 0x56; + D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); + } + else { + memcpy( ether_addr, router_mac_addr, 6 ); + D(bug("Fake ethernet address (same as router) %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); + } + + // Start packet reception thread + int_ack = CreateSemaphore( 0, 0, 1, NULL); + if(!int_ack) { + WarningAlert("WARNING: Cannot create int_ack semaphore"); + goto open_error; + } + + // nonsignaled + int_sig = CreateSemaphore( 0, 0, 1, NULL); + if(!int_sig) { + WarningAlert("WARNING: Cannot create int_sig semaphore"); + goto open_error; + } + + int_sig2 = CreateSemaphore( 0, 0, 1, NULL); + if(!int_sig2) { + WarningAlert("WARNING: Cannot create int_sig2 semaphore"); + goto open_error; + } + + int_send_now = CreateSemaphore( 0, 0, 1, NULL); + if(!int_send_now) { + WarningAlert("WARNING: Cannot create int_send_now semaphore"); + goto open_error; + } + + init_queue(); + + if(!allocate_read_packets()) goto open_error; + + // No need to enter wait state if we can avoid it. + // These all terminate fast. + + if(pfnInitializeCriticalSectionAndSpinCount) { + pfnInitializeCriticalSectionAndSpinCount( &fetch_csection, 5000 ); + } else { + InitializeCriticalSection( &fetch_csection ); + } + if(pfnInitializeCriticalSectionAndSpinCount) { + pfnInitializeCriticalSectionAndSpinCount( &queue_csection, 5000 ); + } else { + InitializeCriticalSection( &queue_csection ); + } + if(pfnInitializeCriticalSectionAndSpinCount) { + pfnInitializeCriticalSectionAndSpinCount( &send_csection, 5000 ); + } else { + InitializeCriticalSection( &send_csection ); + } + if(pfnInitializeCriticalSectionAndSpinCount) { + pfnInitializeCriticalSectionAndSpinCount( &wpool_csection, 5000 ); + } else { + InitializeCriticalSection( &wpool_csection ); + } + + ether_th = (HANDLE)_beginthreadex( 0, 0, ether_thread_feed_int, 0, 0, ðer_tid ); + if (!ether_th) { + D(bug("Failed to create ethernet thread\n")); + goto open_error; + } + thread_active = true; + + unsigned int dummy; + ether_th2 = (HANDLE)_beginthreadex( 0, 0, + net_if_type == NET_IF_SLIRP ? slirp_receive_func : ether_thread_get_packets_nt, + 0, 0, &dummy ); + ether_th1 = (HANDLE)_beginthreadex( 0, 0, ether_thread_write_packets, 0, 0, &dummy ); + + // Everything OK + return true; + + open_error: + if (thread_active) { + TerminateThread(ether_th,0); + ether_th = 0; + if (int_ack) + CloseHandle(int_ack); + int_ack = 0; + if(int_sig) + CloseHandle(int_sig); + int_sig = 0; + if(int_sig2) + CloseHandle(int_sig2); + int_sig2 = 0; + if(int_send_now) + CloseHandle(int_send_now); + int_send_now = 0; + thread_active = false; + } + if(net_if_type == NET_IF_B2ETHER) { + PacketCloseAdapter(fd); + } + fd = 0; + return false; +} + + +/* + * Deinitialization + */ + +void ether_exit(void) +{ + D(bug("EtherExit\n")); + + // Stop reception thread + thread_active = false; + + if(int_ack) ReleaseSemaphore(int_ack,1,NULL); + if(int_sig) ReleaseSemaphore(int_sig,1,NULL); + if(int_sig2) ReleaseSemaphore(int_sig2,1,NULL); + if(int_send_now) ReleaseSemaphore(int_send_now,1,NULL); + + D(bug("CancelIO if needed\n")); + if (fd && fd->hFile && pfnCancelIo) + pfnCancelIo(fd->hFile); + + // Wait max 2 secs to shut down pending io. After that, kill them. + D(bug("Wait delay\n")); + for( int i=0; i<10; i++ ) { + if(!thread_active_1 && !thread_active_2 && !thread_active_3) break; + Sleep(200); + } + + if(thread_active_1) { + D(bug("Ether killing ether_th1\n")); + if(ether_th1) TerminateThread(ether_th1,0); + thread_active_1 = false; + } + if(thread_active_2) { + D(bug("Ether killing ether_th2\n")); + if(ether_th2) TerminateThread(ether_th2,0); + thread_active_2 = false; + } + if(thread_active_3) { + D(bug("Ether killing thread\n")); + if(ether_th) TerminateThread(ether_th,0); + thread_active_3 = false; + } + + ether_th1 = 0; + ether_th2 = 0; + ether_th = 0; + + D(bug("Closing semaphores\n")); + if(int_ack) { + CloseHandle(int_ack); + int_ack = 0; + } + if(int_sig) { + CloseHandle(int_sig); + int_sig = 0; + } + if(int_sig2) { + CloseHandle(int_sig2); + int_sig2 = 0; + } + if(int_send_now) { + CloseHandle(int_send_now); + int_send_now = 0; + } + + // Close ethernet device + if(fd) { + PacketCloseAdapter(fd); + fd = 0; + } + + // Remove all protocols + D(bug("Removing protocols\n")); + NetProtocol *p = prot_list; + while (p) { + NetProtocol *next = p->next; + delete p; + p = next; + } + prot_list = 0; + + D(bug("Deleting sections\n")); + DeleteCriticalSection( &fetch_csection ); + DeleteCriticalSection( &queue_csection ); + DeleteCriticalSection( &send_csection ); + DeleteCriticalSection( &wpool_csection ); + + D(bug("Freeing read packets\n")); + free_read_packets(); + + D(bug("Freeing write packets\n")); + free_write_packets(); + + D(bug("Finalizing queue\n")); + final_queue(); + + if (net_if_type == NET_IF_ROUTER) { + D(bug("Stopping router\n")); + router_final(); + } + + D(bug("EtherExit done\n")); +} + + +/* + * Glue around low-level implementation + */ + +#ifdef SHEEPSHAVER +// Error codes +enum { + eMultiErr = -91, + eLenErr = -92, + lapProtErr = -94, + excessCollsns = -95 +}; + +// Initialize ethernet +void EtherInit(void) +{ + net_open = false; + + // Do nothing if the user disabled the network + if (PrefsFindBool("nonet")) + return; + + net_open = ether_init(); +} + +// Exit ethernet +void EtherExit(void) +{ + ether_exit(); + net_open = false; +} + +// Get ethernet hardware address +void AO_get_ethernet_address(uint32 arg) +{ + uint8 *addr = Mac2HostAddr(arg); + if (net_open) + OTCopy48BitAddress(ether_addr, addr); + else { + addr[0] = 0x12; + addr[1] = 0x34; + addr[2] = 0x56; + addr[3] = 0x78; + addr[4] = 0x9a; + addr[5] = 0xbc; + } + D(bug("AO_get_ethernet_address: got address %02x%02x%02x%02x%02x%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])); +} + +// Add multicast address +void AO_enable_multicast(uint32 addr) +{ + if (net_open) + ether_do_add_multicast(Mac2HostAddr(addr)); +} + +// Disable multicast address +void AO_disable_multicast(uint32 addr) +{ + if (net_open) + ether_do_del_multicast(Mac2HostAddr(addr)); +} + +// Transmit one packet +void AO_transmit_packet(uint32 mp) +{ + if (net_open) { + switch (ether_do_write(mp)) { + case noErr: + num_tx_packets++; + break; + case excessCollsns: + num_tx_buffer_full++; + break; + } + } +} + +// Copy packet data from message block to linear buffer +static inline int ether_arg_to_buffer(uint32 mp, uint8 *p) +{ + return ether_msgb_to_buffer(mp, p); +} + +// Ethernet interrupt +void EtherIRQ(void) +{ + D(bug("EtherIRQ\n")); + num_ether_irq++; + + OTEnterInterrupt(); + ether_do_interrupt(); + OTLeaveInterrupt(); + + // Acknowledge interrupt to reception thread + D(bug(" EtherIRQ done\n")); + ReleaseSemaphore(int_ack,1,NULL); +} +#else +// Add multicast address +int16 ether_add_multicast(uint32 pb) +{ + return ether_do_add_multicast(Mac2HostAddr(pb + eMultiAddr)); +} + +// Disable multicast address +int16 ether_del_multicast(uint32 pb) +{ + return ether_do_del_multicast(Mac2HostAddr(pb + eMultiAddr)); +} + +// Transmit one packet +int16 ether_write(uint32 wds) +{ + return ether_do_write(wds); +} + +// Copy packet data from WDS to linear buffer +static inline int ether_arg_to_buffer(uint32 wds, uint8 *p) +{ + return ether_wds_to_buffer(wds, p); +} + +// Dispatch packet to protocol handler +static void ether_dispatch_packet(uint32 packet, uint32 length) +{ + // Get packet type + uint16 type = ReadMacInt16(packet + 12); + + // Look for protocol + NetProtocol *prot = find_protocol(type); + if (prot == NULL) + return; + + // No default handler + if (prot->handler == 0) + return; + + // Copy header to RHA + Mac2Mac_memcpy(ether_data + ed_RHA, packet, 14); + D(bug(" header %08lx%04lx %08lx%04lx %04lx\n", ReadMacInt32(ether_data + ed_RHA), ReadMacInt16(ether_data + ed_RHA + 4), ReadMacInt32(ether_data + ed_RHA + 6), ReadMacInt16(ether_data + ed_RHA + 10), ReadMacInt16(ether_data + ed_RHA + 12))); + + // Call protocol handler + M68kRegisters r; + r.d[0] = type; // Packet type + r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket) + r.a[0] = packet + 14; // Pointer to packet (Mac address, for ReadPacket) + r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA + r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines + D(bug(" calling protocol handler %08lx, type %08lx, length %08lx, data %08lx, rha %08lx, read_packet %08lx\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4])); + Execute68k(prot->handler, &r); +} + +// Ethernet interrupt +void EtherInterrupt(void) +{ + D(bug("EtherIRQ\n")); + ether_do_interrupt(); + + // Acknowledge interrupt to reception thread + D(bug(" EtherIRQ done\n")); + ReleaseSemaphore(int_ack,1,NULL); +} +#endif + + +/* + * Reset + */ + +void ether_reset(void) +{ + D(bug("EtherReset\n")); + + // Remove all protocols + NetProtocol *p = prot_list; + while (p) { + NetProtocol *next = p->next; + delete p; + p = next; + } + prot_list = NULL; +} + + +/* + * Add multicast address + */ + +static int16 ether_do_add_multicast(uint8 *addr) +{ + D(bug("ether_add_multicast\n")); + + // We wouldn't need to do this + // if(ether_multi_mode != ETHER_MULTICAST_MAC) return noErr; + + switch (net_if_type) { + case NET_IF_B2ETHER: + if (!PacketAddMulticast( fd, addr)) { + D(bug("WARNING: couldn't enable multicast address\n")); + return eMultiErr; + } + default: + D(bug("ether_add_multicast: noErr\n")); + return noErr; + } +} + + +/* + * Delete multicast address + */ + +int16 ether_do_del_multicast(uint8 *addr) +{ + D(bug("ether_del_multicast\n")); + + // We wouldn't need to do this + // if(ether_multi_mode != ETHER_MULTICAST_MAC) return noErr; + + switch (net_if_type) { + case NET_IF_B2ETHER: + if (!PacketDelMulticast( fd, addr)) { + D(bug("WARNING: couldn't disable multicast address\n")); + return eMultiErr; + } + default: + return noErr; + } +} + + +/* + * Attach protocol handler + */ + +int16 ether_attach_ph(uint16 type, uint32 handler) +{ + D(bug("ether_attach_ph type=0x%x, handler=0x%x\n",(int)type,handler)); + + // Already attached? + NetProtocol *p = find_protocol(type); + if (p != NULL) { + D(bug("ether_attach_ph: lapProtErr\n")); + return lapProtErr; + } else { + // No, create and attach + p = new NetProtocol; + p->next = prot_list; + p->type = type; + p->handler = handler; + prot_list = p; + D(bug("ether_attach_ph: noErr\n")); + return noErr; + } +} + + +/* + * Detach protocol handler + */ + +int16 ether_detach_ph(uint16 type) +{ + D(bug("ether_detach_ph type=%08lx\n",(int)type)); + + NetProtocol *p = find_protocol(type); + if (p != NULL) { + NetProtocol *previous = 0; + NetProtocol *q = prot_list; + while(q) { + if (q == p) { + if(previous) { + previous->next = q->next; + } else { + prot_list = q->next; + } + delete p; + return noErr; + } + previous = q; + q = q->next; + } + } + return lapProtErr; +} + +#if MONITOR +static void dump_packet( uint8 *packet, int length ) +{ + char buf[1000], sm[10]; + + *buf = 0; + + if(length > 256) length = 256; + + for (int i=0; inext = 0; + if(send_queue) { + LPPACKET p = send_queue; + // The queue is short. It would be larger overhead to double-link it. + while(p->next) p = p->next; + p->next = Packet; + } else { + send_queue = Packet; + } + LeaveCriticalSection( &send_csection ); +} + +static LPPACKET get_send_head( void ) +{ + LPPACKET Packet = 0; + + EnterCriticalSection( &send_csection ); + if(send_queue) { + Packet = send_queue; + send_queue = send_queue->next; + } + LeaveCriticalSection( &send_csection ); + + return Packet; +} + +static int get_write_packet_pool_sz( void ) +{ + LPPACKET t = write_packet_pool; + int sz = 0; + + while(t) { + t = t->next; + sz++; + } + return(sz); +} + +static void free_write_packets( void ) +{ + LPPACKET next; + int i = 0; + while(write_packet_pool) { + next = write_packet_pool->next; + D(bug("Freeing write packet %ld\n",++i)); + PacketFreePacket(write_packet_pool); + write_packet_pool = next; + } +} + +void recycle_write_packet( LPPACKET Packet ) +{ + EnterCriticalSection( &wpool_csection ); + Packet->next = write_packet_pool; + write_packet_pool = Packet; + D(bug("Pool size after recycling = %ld\n",get_write_packet_pool_sz())); + LeaveCriticalSection( &wpool_csection ); +} + +static LPPACKET get_write_packet( UINT len ) +{ + LPPACKET Packet = 0; + + EnterCriticalSection( &wpool_csection ); + if(write_packet_pool) { + Packet = write_packet_pool; + write_packet_pool = write_packet_pool->next; + Packet->OverLapped.Offset = 0; + Packet->OverLapped.OffsetHigh = 0; + Packet->Length = len; + Packet->BytesReceived = 0; + Packet->bIoComplete = FALSE; + Packet->free = TRUE; + Packet->next = 0; + // actually an auto-reset event. + if(Packet->OverLapped.hEvent) ResetEvent(Packet->OverLapped.hEvent); + } else { + Packet = PacketAllocatePacket(fd,len); + } + + D(bug("Pool size after get wr packet = %ld\n",get_write_packet_pool_sz())); + + LeaveCriticalSection( &wpool_csection ); + + return Packet; +} + +static unsigned int ether_thread_write_packets(void *arg) +{ + LPPACKET Packet; + + thread_active_1 = true; + + D(bug("ether_thread_write_packets start\n")); + + while(thread_active) { + // must be alertable, otherwise write completion is never called + WaitForSingleObjectEx(int_send_now,INFINITE,TRUE); + while( thread_active && (Packet = get_send_head()) != 0 ) { + switch (net_if_type) { + case NET_IF_ROUTER: + if(router_write_packet((uint8 *)Packet->Buffer, Packet->Length)) { + Packet->bIoComplete = TRUE; + recycle_write_packet(Packet); + } + break; + case NET_IF_FAKE: + Packet->bIoComplete = TRUE; + recycle_write_packet(Packet); + break; + case NET_IF_B2ETHER: + if(!PacketSendPacket( fd, Packet, FALSE, TRUE )) { + // already recycled if async + } + break; + case NET_IF_SLIRP: + slirp_input((uint8 *)Packet->Buffer, Packet->Length); + Packet->bIoComplete = TRUE; + recycle_write_packet(Packet); + break; + } + } + } + + D(bug("ether_thread_write_packets exit\n")); + + thread_active_1 = false; + + return(0); +} + +static BOOL write_packet( uint8 *packet, int len ) +{ + LPPACKET Packet; + + D(bug("write_packet\n")); + + Packet = get_write_packet(len); + if(Packet) { + memcpy( Packet->Buffer, packet, len ); + + EnterCriticalSection( &fetch_csection ); + pending_packet_sz[echo_count] = min(sizeof(pending_packet),len); + memcpy( pending_packet[echo_count], packet, pending_packet_sz[echo_count] ); + echo_count = (echo_count+1) & (~(MAX_ECHO-1)); + LeaveCriticalSection( &fetch_csection ); + + insert_send_queue( Packet ); + + ReleaseSemaphore(int_send_now,1,NULL); + return(TRUE); + } else { + return(FALSE); + } +} + +static int16 ether_do_write(uint32 arg) +{ + D(bug("ether_write\n")); + + // Copy packet to buffer + uint8 packet[1514], *p = packet; + int len = ether_arg_to_buffer(arg, p); + + if(len > 1514) { + D(bug("illegal packet length: %d\n",len)); + return eLenErr; + } else { +#if MONITOR + bug("Sending Ethernet packet (%d bytes):\n",(int)len); + dump_packet( packet, len ); +#endif + } + + // Transmit packet + if (!write_packet(packet, len)) { + D(bug("WARNING: couldn't transmit packet\n")); + return excessCollsns; + } else { + // It's up to the protocol drivers to do the error checking. Even if the + // i/o completion routine returns ok, there can be errors, so there is + // no point to wait for write completion and try to make some sense of the + // possible error codes. + return noErr; + } +} + + +static void init_queue(void) +{ + queue_inx = 0; + queue_head = 0; + + for( int i=0; i 0) { + D(bug("ethernet queue full, packet dropped\n")); + } else { + if(sz > 1514) sz = 1514; + queue[queue_inx].sz = sz; + memcpy( queue[queue_inx].buf, buf, sz ); + queue_inx++; + if(queue_inx >= MAX_QUEUE_ITEMS) queue_inx = 0; + if(wait_request) { + wait_request = false; + ReleaseSemaphore(int_sig,1,NULL); + } + } + LeaveCriticalSection( &queue_csection ); +} + +static int dequeue_packet( uint8 *buf ) +{ + int sz; + + if(!thread_active) return(0); + + EnterCriticalSection( &queue_csection ); + sz = queue[queue_head].sz; + if(sz > 0) { + memcpy( buf, queue[queue_head].buf, sz ); + queue[queue_head].sz = 0; + queue_head++; + if(queue_head >= MAX_QUEUE_ITEMS) queue_head = 0; + } + LeaveCriticalSection( &queue_csection ); + return(sz); +} + +static void trigger_queue(void) +{ + EnterCriticalSection( &queue_csection ); + if( queue[queue_head].sz > 0 ) { + D(bug(" packet received, triggering Ethernet interrupt\n")); + SetInterruptFlag(INTFLAG_ETHER); + TriggerInterrupt(); + // of course can't wait here. + } + LeaveCriticalSection( &queue_csection ); +} + +static bool set_wait_request(void) +{ + bool result; + EnterCriticalSection( &queue_csection ); + if(queue[queue_head].sz) { + result = true; + } else { + result = false; + wait_request = true; + } + LeaveCriticalSection( &queue_csection ); + return(result); +} + + +/* + * SLIRP output buffer glue + */ + +int slirp_can_output(void) +{ + return 1; +} + +void slirp_output(const uint8 *packet, int len) +{ + enqueue_packet(packet, len); +} + +unsigned int slirp_receive_func(void *arg) +{ + D(bug("slirp_receive_func\n")); + thread_active_2 = true; + + while (thread_active) { + // Wait for packets to arrive + fd_set rfds, wfds, xfds; + int nfds, ret, timeout; + + // ... in the output queue + nfds = -1; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + timeout = slirp_select_fill(&nfds, &rfds, &wfds, &xfds); +#if ! USE_SLIRP_TIMEOUT + timeout = 10000; +#endif + if (nfds < 0) { + /* Windows does not honour the timeout if there is not + descriptor to wait for */ + Delay_usec(timeout); + ret = 0; + } + else { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = timeout; + ret = select(0, &rfds, &wfds, &xfds, &tv); + } + if (ret >= 0) + slirp_select_poll(&rfds, &wfds, &xfds); + } + + D(bug("slirp_receive_func exit\n")); + thread_active_2 = false; + return 0; +} + + +/* + * Packet reception threads + */ + +VOID CALLBACK packet_read_completion( + DWORD dwErrorCode, + DWORD dwNumberOfBytesTransfered, + LPOVERLAPPED lpOverlapped + ) +{ + EnterCriticalSection( &fetch_csection ); + + LPPACKET lpPacket = CONTAINING_RECORD(lpOverlapped,PACKET,OverLapped); + + D(bug("packet_read_completion bytes=%d, error code=%d\n",dwNumberOfBytesTransfered,dwErrorCode)); + + if(thread_active && !dwErrorCode) { + int count = min(dwNumberOfBytesTransfered,1514); + if(count) { + int j = echo_count; + for(int i=MAX_ECHO; i; i--) { + j--; + if(j < 0) j = MAX_ECHO-1; + if(count == pending_packet_sz[j] && + memcmp(pending_packet[j],lpPacket->Buffer,count) == 0) + { + D(bug("packet_read_completion discarding own packet.\n")); + dwNumberOfBytesTransfered = 0; + + j = (j+1) & (~(MAX_ECHO-1)); + if(j != echo_count) { + D(bug("Wow, this fix made some good after all...\n")); + } + + break; + } + } + if(dwNumberOfBytesTransfered) { + if(net_if_type != NET_IF_ROUTER || !router_read_packet((uint8 *)lpPacket->Buffer, dwNumberOfBytesTransfered)) { + enqueue_packet( (LPBYTE)lpPacket->Buffer, dwNumberOfBytesTransfered ); + } + } + } + } + + // actually an auto-reset event. + if(lpPacket->OverLapped.hEvent) ResetEvent(lpPacket->OverLapped.hEvent); + + lpPacket->free = TRUE; + lpPacket->bIoComplete = TRUE; + + if(wait_request2) { + wait_request2 = false; + ReleaseSemaphore(int_sig2,1,NULL); + } + + LeaveCriticalSection( &fetch_csection ); +} + +static BOOL has_no_completed_io(void) +{ + BOOL result = TRUE; + + EnterCriticalSection( &fetch_csection ); + + for( int i=0; ibIoComplete) { + result = FALSE; + break; + } + } + if(result) wait_request2 = true; + + LeaveCriticalSection( &fetch_csection ); + return(result); +} + +static bool allocate_read_packets(void) +{ + for( int i=0; ifree) { + packets[i]->free = FALSE; + if(PacketReceivePacket(fd,packets[i],FALSE)) { + if(packets[i]->bIoComplete) { + D(bug("Early io completion...\n")); + packet_read_completion( + ERROR_SUCCESS, + packets[i]->BytesReceived, + &packets[i]->OverLapped + ); + } + } else { + packets[i]->free = TRUE; + } + } + } + } + + if(thread_active && has_no_completed_io()) { + D(bug("Waiting for int_sig2\n")); + // "problem": awakens twice in a row. Fix if you increase the pool size. + WaitForSingleObjectEx(int_sig2,INFINITE,TRUE); + } + } + + D(bug("ether_thread_get_packets_nt exit\n")); + + thread_active_2 = false; + + return 0; +} + +static unsigned int ether_thread_feed_int(void *arg) +{ + bool looping; + + thread_active_3 = true; + + D(bug("ether_thread_feed_int start\n")); + + while(thread_active) { + D(bug("Waiting for int_sig\n")); + WaitForSingleObject(int_sig,INFINITE); + // Looping this way to avoid a race condition. + D(bug("Triggering\n")); + looping = true; + while(thread_active && looping) { + trigger_queue(); + // Wait for interrupt acknowledge by EtherInterrupt() + WaitForSingleObject(int_ack,INFINITE); + if(thread_active) looping = set_wait_request(); + } + D(bug("Queue empty.\n")); + } + + D(bug("ether_thread_feed_int exit\n")); + + thread_active_3 = false; + + return 0; +} + + +/* + * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers + */ + +static void ether_do_interrupt(void) +{ + // Call protocol handler for received packets + EthernetPacket ether_packet; + uint32 packet = ether_packet.addr(); + ssize_t length; + for (;;) { + + // Read packet from Ethernet device + length = dequeue_packet(Mac2HostAddr(packet)); + if (length < 14) + break; + +#if MONITOR + bug("Receiving Ethernet packet (%d bytes):\n",(int)length); + dump_packet( Mac2HostAddr(packet), length ); +#endif + + // Dispatch packet + ether_dispatch_packet(packet, length); + } +} + +#if DEBUG +#pragma optimize("",on) +#endif diff --git a/BasiliskII/src/Windows/ether_windows.h b/BasiliskII/src/Windows/ether_windows.h index 3c589721..aed67040 100755 --- a/BasiliskII/src/Windows/ether_windows.h +++ b/BasiliskII/src/Windows/ether_windows.h @@ -1,10 +1,10 @@ -#ifndef _ETHER_WINDOWS_H_ -#define _ETHER_WINDOWS_H_ - -void enqueue_packet( uint8 *buf, int sz ); - -#ifdef SHEEPSHAVER -extern uint8 ether_addr[6]; -#endif - -#endif // _ETHER_WINDOWS_H_ +#ifndef _ETHER_WINDOWS_H_ +#define _ETHER_WINDOWS_H_ + +void enqueue_packet( const uint8 *buf, int sz ); + +#ifdef SHEEPSHAVER +extern uint8 ether_addr[6]; +#endif + +#endif // _ETHER_WINDOWS_H_ diff --git a/BasiliskII/src/Windows/user_strings_windows.cpp b/BasiliskII/src/Windows/user_strings_windows.cpp index aea009d1..2f78204f 100755 --- a/BasiliskII/src/Windows/user_strings_windows.cpp +++ b/BasiliskII/src/Windows/user_strings_windows.cpp @@ -36,6 +36,7 @@ user_string_def platform_strings[] = { {STR_VOSF_INIT_ERR, "Cannot initialize Video on SEGV signals."}, {STR_SIG_INSTALL_ERR, "Cannot install %s handler (%s)."}, {STR_TICK_THREAD_ERR, "Cannot create 60Hz thread (%s)."}, + {STR_SLIRP_NO_DNS_FOUND_WARN, "Cannot get DNS address. Ethernet will not be available."}, {STR_NO_AUDIO_WARN, "No audio device found, audio output will be disabled."}, {STR_KEYCODE_FILE_WARN, "Cannot open keycode translation file %s (%s)."}, {STR_KEYCODE_VENDOR_WARN, "Cannot find vendor '%s' in keycode translation file %s."}, diff --git a/BasiliskII/src/Windows/user_strings_windows.h b/BasiliskII/src/Windows/user_strings_windows.h index 72575262..a77ff6aa 100755 --- a/BasiliskII/src/Windows/user_strings_windows.h +++ b/BasiliskII/src/Windows/user_strings_windows.h @@ -26,6 +26,7 @@ enum { STR_VOSF_INIT_ERR, STR_SIG_INSTALL_ERR, STR_TICK_THREAD_ERR, + STR_SLIRP_NO_DNS_FOUND_WARN, STR_NO_AUDIO_WARN, STR_KEYCODE_FILE_WARN, STR_KEYCODE_VENDOR_WARN,