| Re: [hatari-devel] Network support for Hatari |
[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]
|
Small update, because i just realized that LOG_TRACE() in Hatari needs a trailing newline.
PS.: while looking after the STinG drivers, i think there is a problem. AFAIK, TOS 2.x/TOS 3.x sets a default interrupt level of 3, which would permanently block the interrupt i'm using. Any idea how to solve that? |
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 90d2830f..577b8b03 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -74,11 +74,13 @@ endif(CapsImage_FOUND)
link_directories(${CMAKE_CURRENT_BINARY_DIR}/debug
${CMAKE_CURRENT_BINARY_DIR}/falcon
+ ${CMAKE_CURRENT_BINARY_DIR}/ethernet
${CMAKE_CURRENT_BINARY_DIR}/gui-sdl
${CMAKE_CURRENT_BINARY_DIR}/cpu)
add_subdirectory(debug)
add_subdirectory(falcon)
+add_subdirectory(ethernet)
add_subdirectory(gui-sdl)
add_subdirectory(cpu)
@@ -153,7 +155,7 @@ if(SDL2_OTHER_CFLAGS)
# message(STATUS "Additional CFLAGS of SDL: ${SDL2_OTHER_CFLAGS}")
endif(SDL2_OTHER_CFLAGS)
-target_link_libraries(hatari Falcon UaeCpu GuiSdl Floppy Debug ${SDL2_LIBRARY})
+target_link_libraries(hatari Falcon UaeCpu GuiSdl Floppy Debug Ethernet ${SDL2_LIBRARY})
if(Math_FOUND AND NOT APPLE)
target_link_libraries(hatari ${MATH_LIBRARY})
diff --git a/src/configuration.c b/src/configuration.c
index 15daca88..bb4a92d1 100644
--- a/src/configuration.c
+++ b/src/configuration.c
@@ -455,6 +455,18 @@ static const struct Config_Tag configs_Midi[] =
{ NULL , Error_Tag, NULL }
};
+/* Used to load/save ETHERNET options */
+static const struct Config_Tag configs_Ethernet[] =
+{
+ { "sType0", String_Tag, ConfigureParams.Ethernet[0].type },
+ { "sTunnel0", String_Tag, ConfigureParams.Ethernet[0].tunnel },
+ { "sHostIP0", String_Tag, ConfigureParams.Ethernet[0].ip_host },
+ { "sAtariIP0", String_Tag, ConfigureParams.Ethernet[0].ip_atari },
+ { "sNetmask0", String_Tag, ConfigureParams.Ethernet[0].netmask },
+ { "sMAC0", String_Tag, ConfigureParams.Ethernet[0].mac_addr },
+ { NULL , Error_Tag, NULL }
+};
+
/* Used to load system options from old config files */
static int nOldMachineType;
static bool bOldRealTimeClock;
@@ -683,6 +695,25 @@ void Configuration_SetDefault(void)
strcpy(ConfigureParams.Midi.sMidiInPortName, "Off");
strcpy(ConfigureParams.Midi.sMidiOutPortName, "Off");
+ /* Set defaults for ETHERNET */
+ /* ETH[0] with some default values */
+ strcpy(ConfigureParams.Ethernet[0].type, "ptp");
+ strcpy(ConfigureParams.Ethernet[0].tunnel, "tap0");
+ strcpy(ConfigureParams.Ethernet[0].ip_host, "192.168.0.1");
+ strcpy(ConfigureParams.Ethernet[0].ip_atari, "192.168.0.2");
+ strcpy(ConfigureParams.Ethernet[0].netmask, "255.255.255.0");
+ strcpy(ConfigureParams.Ethernet[0].mac_addr, "00:41:45:54:48:30");
+ /* ETH[1] - ETH[MAX_ETH] are empty by default */
+ for (i = 1; i < MAX_ETH; i++)
+ {
+ ConfigureParams.Ethernet[i].type[0] = '\0';
+ ConfigureParams.Ethernet[i].tunnel[0] = '\0';
+ ConfigureParams.Ethernet[i].ip_host[0] = '\0';
+ ConfigureParams.Ethernet[i].ip_atari[0] = '\0';
+ ConfigureParams.Ethernet[i].netmask[0] = '\0';
+ ConfigureParams.Ethernet[i].mac_addr[0] = '\0';
+ }
+
/* Set defaults for Screen */
ConfigureParams.Screen.bFullScreen = false;
ConfigureParams.Screen.bKeepResolution = true;
@@ -1018,6 +1049,7 @@ void Configuration_Load(const char *psFileName)
Configuration_LoadSection(psFileName, configs_Rs232, "[RS232]");
Configuration_LoadSection(psFileName, configs_Printer, "[Printer]");
Configuration_LoadSection(psFileName, configs_Midi, "[Midi]");
+ Configuration_LoadSection(psFileName, configs_Ethernet, "[Network]");
Configuration_LoadSection(psFileName, configs_System, "[System]");
Configuration_LoadSection(psFileName, configs_Video, "[Video]");
}
@@ -1075,6 +1107,7 @@ void Configuration_Save(void)
Configuration_SaveSection(sConfigFileName, configs_Rs232, "[RS232]");
Configuration_SaveSection(sConfigFileName, configs_Printer, "[Printer]");
Configuration_SaveSection(sConfigFileName, configs_Midi, "[Midi]");
+ Configuration_SaveSection(sConfigFileName, configs_Ethernet, "[Network]");
Configuration_SaveSection(sConfigFileName, configs_System, "[System]");
Configuration_SaveSection(sConfigFileName, configs_Video, "[Video]");
}
diff --git a/src/cpu/hatari-glue.c b/src/cpu/hatari-glue.c
index 94dad131..d41b7970 100644
--- a/src/cpu/hatari-glue.c
+++ b/src/cpu/hatari-glue.c
@@ -80,8 +80,12 @@ int intlev(void)
{
if ( pendingInterrupts & (1 << 6) ) /* MFP/DSP interrupt ? */
return 6;
+ else if ( pendingInterrupts & (1 << 5) ) /* SCC interrupt ? */
+ return 5;
else if ( pendingInterrupts & (1 << 4) ) /* VBL interrupt ? */
return 4;
+ else if ( pendingInterrupts & (1 << 3) ) /* VME interrupt ? */
+ return 3;
else if ( pendingInterrupts & (1 << 2) ) /* HBL interrupt ? */
return 2;
diff --git a/src/cpu/newcpu.c b/src/cpu/newcpu.c
index 5f3f2f55..508d8d41 100644
--- a/src/cpu/newcpu.c
+++ b/src/cpu/newcpu.c
@@ -2834,7 +2834,7 @@ static int iack_cycle(int nr)
pendingInterrupts &= ~( 1 << 6 );
}
}
- else if ( ( nr == 26 ) || ( nr == 28 ) ) /* HBL / VBL */
+ else if ( ( nr == 26 ) || ( nr == 27 ) || ( nr == 28 ) || ( nr == 29 ) ) /* HBL / VBL / VME / SCC */
{
if (cycle_exact)
{
diff --git a/src/debug/log.c b/src/debug/log.c
index eb437479..226af451 100644
--- a/src/debug/log.c
+++ b/src/debug/log.c
@@ -148,6 +148,8 @@ static flagname_t TraceFlags[] = {
{ TRACE_SCSIDRV , "scsidrv" } ,
+ { TRACE_ETHERNET , "ethernet" } ,
+
{ TRACE_MEM , "mem" } ,
{ TRACE_VME , "vme" } ,
diff --git a/src/debug/log.h b/src/debug/log.h
index 9be233f9..7db70297 100644
--- a/src/debug/log.h
+++ b/src/debug/log.h
@@ -184,6 +184,8 @@ extern char *Log_MatchTrace(const char *text, int state);
#define TRACE_VME (1ll<<55)
+#define TRACE_ETHERNET (1ll<<56)
+
#define TRACE_NONE (0)
#define TRACE_ALL (~0)
diff --git a/src/debug/natfeats.c b/src/debug/natfeats.c
index 517ed1e9..93fa14a0 100644
--- a/src/debug/natfeats.c
+++ b/src/debug/natfeats.c
@@ -22,6 +22,7 @@ const char Natfeats_fileid[] = "Hatari natfeats.c";
#include "debugui.h"
#include "statusbar.h"
#include "nf_scsidrv.h"
+#include "nf_ethernet.h"
#include "log.h"
/* maximum input string length */
@@ -286,9 +287,12 @@ static const struct {
{ "NF_SHUTDOWN", true, nf_shutdown },
{ "NF_EXIT", false, nf_exit },
{ "NF_DEBUGGER", false, nf_debugger },
- { "NF_FASTFORWARD", false, nf_fastforward }
+ { "NF_FASTFORWARD", false, nf_fastforward },
#if defined(__linux__)
- ,{ "NF_SCSIDRV", true, nf_scsidrv }
+ { "NF_SCSIDRV", true, nf_scsidrv },
+#endif
+#ifdef WITH_NF_ETHERNET
+ { "ETHERNET", false, nf_ethernet },
#endif
};
diff --git a/src/ethernet/CMakeLists.txt b/src/ethernet/CMakeLists.txt
new file mode 100644
index 00000000..adab63b9
--- /dev/null
+++ b/src/ethernet/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(Ethernet
+ nf_ethernet.c ethernet_linux.c ethernet_darwin.c
+ ethernet_macosx.c ethernet_win32.c fd_trans.c)
diff --git a/src/ethernet/ethernet.h b/src/ethernet/ethernet.h
new file mode 100644
index 00000000..a54b64ee
--- /dev/null
+++ b/src/ethernet/ethernet.h
@@ -0,0 +1,55 @@
+/*
+ * ethernet.h - Ethernet Card Emulation
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#include "nf_ethernet.h"
+
+#define MAX_PACKET_SIZE 9000
+
+struct EthernetHandler {
+ int ethX;
+ ssize_t packet_length;
+ uint8_t packet[MAX_PACKET_SIZE+2];
+ SDL_Thread *handlingThread; /* Packet reception thread */
+ SDL_sem *intAck; /* Interrupt acknowledge semaphore */
+
+ bool (*open)(struct EthernetHandler *);
+ void (*close)(struct EthernetHandler *);
+ int (*recv)(struct EthernetHandler *, uint8_t *, int);
+ int (*send)(struct EthernetHandler *, const uint8_t *, int);
+};
+
+#if defined(__CYGWIN32__) || defined(__WIN32__)
+ struct EthernetHandler *WinTapEthernetHandler(int eth_idx);
+# define ETHERNET_HANDLER_CLASSNAME WinTapEthernetHandler
+#elif defined(__APPLE__)
+# define ENABLE_BPF 1
+# if ENABLE_BPF
+ struct EthernetHandler *BPFEthernetHandler(int eth_idx);
+# define ETHERNET_HANDLER_CLASSNAME BPFEthernetHandler
+# else
+ struct EthernetHandler *TunTapEthernetHandler(int eth_idx);
+# define ETHERNET_HANDLER_CLASSNAME TunTapEthernetHandler
+# endif
+#elif defined(__linux__)
+ struct EthernetHandler *TunTapEthernetHandler(int eth_idx);
+# define ETHERNET_HANDLER_CLASSNAME TunTapEthernetHandler
+#endif
diff --git a/src/ethernet/ethernet_darwin.c b/src/ethernet/ethernet_darwin.c
new file mode 100644
index 00000000..1a5ce1ef
--- /dev/null
+++ b/src/ethernet/ethernet_darwin.c
@@ -0,0 +1,352 @@
+/*
+ * ethernet_darwin.c - Darwin Ethernet support (via TUN/TAP driver)
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * Inspired by Bernie Meyer's UAE-JIT and Gwenole Beauchesne's Basilisk II-JIT
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && defined(__APPLE__) && !defined(ENABLE_BPF)
+
+#include "screen.h" /* bInFullScreen */
+#include "paths.h"
+
+#include <sys/wait.h> /* waitpid() */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Authorization.h>
+#include <Security/AuthorizationTags.h>
+
+/****************************
+ * Configuration zone begins
+ */
+
+#define TAP_SHELL "/bin/sh"
+#define TAP_INIT "aratapif.sh"
+#define TAP_MTU "1500"
+
+/*
+ * Configuration zone ends
+ **************************/
+
+struct TunTapEthernetHandler
+{
+ struct EthernetHandler base;
+
+ int fd;
+};
+
+
+static OSStatus authStatus;
+static AuthorizationRef authorizationRef;
+static bool fullscreen;
+
+
+static int openAuthorizationContext(void)
+{
+ if (authorizationRef == NULL)
+ {
+ AuthorizationFlags authFlags = kAuthorizationFlagDefaults; /* 1 */
+
+ authStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, /* 3 */
+ authFlags, &authorizationRef); /* 4 */
+ if (authStatus != errAuthorizationSuccess)
+ return authStatus;
+
+ fullscreen = bInFullScreen;
+ if (fullscreen)
+ Screen_ReturnFromFullScreen();
+
+ AuthorizationItem authItems = { kAuthorizationRightExecute, 0, /* 5 */
+ NULL, 0
+ }; /* 6 */
+ AuthorizationRights authRights = { 1, &authItems }; /* 7 */
+ authFlags = kAuthorizationFlagDefaults | /* 8 */
+ kAuthorizationFlagInteractionAllowed | /* 9 */
+ kAuthorizationFlagPreAuthorize | /* 10 */
+ kAuthorizationFlagExtendRights; /* 11 */
+ authStatus = AuthorizationCopyRights(authorizationRef, &authRights, NULL, authFlags, NULL); /* 12 */
+ }
+ return authStatus;
+}
+
+
+static void closeAuthorizationContext(void)
+{
+ if (authorizationRef)
+ {
+ AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); /* 17 */
+ authorizationRef = NULL;
+
+ if (fullscreen)
+ {
+ Screen_EnterFullScreen();
+ }
+ }
+}
+
+
+static int executeScriptAsRoot(char *args[])
+{
+ OSStatus execStatus;
+ char app[FILENAME_MAX];
+ FILE *authCommunicationsPipe = NULL;
+ char execReadBuffer[128];
+ AuthorizationFlags authFlags = kAuthorizationFlagDefaults; /* 13 */
+
+ /*
+ CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ CFURLGetFileSystemRepresentation(url, true, (uint8_t) *)app, MAXPATHLEN);
+ printf("TunTap: Binary directory '%s'\n", app);
+ CFRelease(url); */
+ strcpy(app, Paths_GetWorkingDir());
+ strcat(app, "/");
+ strcat(app, args[0]);
+ args[0] = app;
+
+ execStatus = AuthorizationExecuteWithPrivileges /* 14 */
+ (authorizationRef, TAP_SHELL, authFlags, args, /* 15 */
+ &authCommunicationsPipe); /* 16 */
+
+ if (execStatus == errAuthorizationSuccess)
+ for (;;)
+ {
+ int bytesRead = read(fileno(authCommunicationsPipe),
+ execReadBuffer, sizeof(execReadBuffer));
+
+ if (bytesRead < 1)
+ break;
+ write(fileno(stdout), execReadBuffer, bytesRead);
+ }
+
+ return execStatus;
+}
+
+
+/*
+ * Allocate ETHERNETDriver TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+static int tapOpenOld(struct TunTapEthernetHandler *handler, char *dev)
+{
+ char tapname[sizeof(ConfigureParams.Ethernet[0].tunnel) + 5];
+ int i;
+ int fd;
+
+ if (*dev)
+ {
+ snprintf(tapname, sizeof(tapname), "/dev/%s", dev);
+ tapname[sizeof(tapname) - 1] = '\0';
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, tapname);
+ fd = open(tapname, O_RDWR);
+ if (fd > 0)
+ return fd;
+ }
+
+ for (i = 0; i < 255; i++)
+ {
+ sprintf(tapname, "/dev/tap%d", i);
+ /* Open device */
+ if ((fd = open(tapname, O_RDWR)) > 0)
+ {
+ sprintf(dev, "tap%d", i);
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, dev);
+ return fd;
+ }
+ }
+ return -1;
+}
+
+
+static int tapOpen(struct TunTapEthernetHandler *handler, char *dev)
+{
+ return tapOpenOld(handler, dev);
+}
+
+
+static bool TunTapEthernetHandler_open(struct EthernetHandler *_handler)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+ /* int nonblock = 1; */
+ char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+ char *devName = ConfigureParams.Ethernet[handler->base.ethX].tunnel;
+ int auth;
+ bool failed;
+
+ handler->base.close(&handler->base);
+
+ if (strlen(type) == 0 || strcmp(ConfigureParams.Ethernet[handler->base.ethX].type, "none") == 0)
+ {
+ if (handler->base.ethX == MAX_ETH - 1)
+ closeAuthorizationContext();
+ return false;
+ }
+
+ /* if 'bridge' mode then we are done */
+ if (strstr(ConfigureParams.Ethernet[handler->base.ethX].type, "bridge") != NULL)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): Bridge mode currently not supported '%s'\n", handler->base.ethX, devName);
+ if (handler->base.ethX == MAX_ETH - 1)
+ closeAuthorizationContext();
+ return false;
+ }
+
+ /* get the tunnel nif name if provided */
+ if (strlen(devName) == 0)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tunnel name undefined\n", handler->base.ethX);
+ if (handler->base.ethX == MAX_ETH - 1)
+ closeAuthorizationContext();
+ return false;
+ }
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): open('%s')\n", handler->base.ethX, devName);
+
+ handler->fd = tapOpen(handler, devName);
+ if (handler->fd < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): NO_NET_DRIVER_WARN '%s': %s\n", handler->base.ethX, devName, strerror(errno));
+ if (handler->base.ethX == MAX_ETH - 1)
+ closeAuthorizationContext();
+ return false;
+ }
+ auth = openAuthorizationContext();
+
+ if (auth)
+ {
+ handler->base.close(&handler->base);
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): Authorization failed'%s'\n", handler->base.ethX, devName);
+ return false;
+ }
+
+ failed = true;
+
+ {
+ /* the arguments _need_ to be placed into the child process */
+ /* memory (otherwise this does not work here) */
+ static char tap_mtu[] = TAP_MTU;
+ static char tap_init[] = TAP_INIT;
+ char *args[] = {
+ tap_init,
+ ConfigureParams.Ethernet[handler->base.ethX].tunnel,
+ ConfigureParams.Ethernet[handler->base.ethX].ip_host,
+ ConfigureParams.Ethernet[handler->base.ethX].ip_atari,
+ ConfigureParams.Ethernet[handler->base.ethX].netmask,
+ tap_mtu, NULL
+ };
+
+ int result = executeScriptAsRoot(args);
+
+ if (result != 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " failed (code %d). Ethernet disabled!\n", handler->base.ethX, result);
+ } else
+ {
+ failed = false;
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): " TAP_INIT " initialized OK\n", handler->base.ethX);
+ }
+ }
+
+ /* Close /dev/net/tun device if exec failed */
+ if (failed)
+ {
+ handler->base.close(&handler->base);
+ if (handler->base.ethX == MAX_ETH - 1)
+ closeAuthorizationContext();
+ return false;
+ }
+
+ /* Set nonblocking I/O */
+ /*ioctl(handler->fd, FIONBIO, &nonblock); */
+
+ if (handler->base.ethX == MAX_ETH - 1)
+ closeAuthorizationContext();
+ return true;
+}
+
+
+static void TunTapEthernetHandler_close(struct EthernetHandler *_handler)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): close\n", handler->base.ethX);
+ /* Close /dev/net/tun device */
+ if (handler->fd > 0)
+ {
+ close(handler->fd);
+ handler->fd = -1;
+ }
+}
+
+
+static int TunTapEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+ return read(handler->fd, buf, len);
+}
+
+
+static int TunTapEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+ int res = write(handler->fd, buf, len);
+
+ if (res < 0)
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): WARNING: Couldn't transmit packet\n", handler->base.ethX);
+ return res;
+}
+
+
+struct EthernetHandler *TunTapEthernetHandler(int eth_idx)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) malloc(sizeof(*handler));
+
+ handler->base.ethX = eth_idx;
+ handler->base.packet_length = 0;
+ handler->base.handlingThread = NULL;
+ handler->base.intAck = NULL;
+
+ handler->base.open = TunTapEthernetHandler_open;
+ handler->base.close = TunTapEthernetHandler_close;
+ handler->base.recv = TunTapEthernetHandler_recv;
+ handler->base.send = TunTapEthernetHandler_send;
+
+ handler->fd = -1;
+
+ return &handler->base;
+}
+
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/ethernet_linux.c b/src/ethernet/ethernet_linux.c
new file mode 100644
index 00000000..c1c60f11
--- /dev/null
+++ b/src/ethernet/ethernet_linux.c
@@ -0,0 +1,335 @@
+/*
+ * ethernet_linux.c - Linux Ethernet support (via TUN/TAP driver)
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && defined (__linux__)
+
+#include <sys/poll.h>
+#include <sys/wait.h> /* waitpid() */
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#define HAVE_LINUX_IF_H
+#define HAVE_LINUX_IF_TUN_H
+#define HAVE_SYS_SOCKET_H
+
+#if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H)
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#endif
+#if defined(HAVE_NET_IF_TUN_H)
+#include <net/if_tun.h>
+#endif
+
+#include <errno.h>
+
+/****************************
+ * Configuration zone begins
+ */
+
+#define TAP_INIT "aratapif"
+#define TAP_MTU "1500"
+
+/*
+ * Configuration zone ends
+ **************************/
+
+
+struct TunTapEthernetHandler {
+ struct EthernetHandler base;
+
+ int fd;
+};
+
+
+/*
+ * Allocate ETHERNETDriver TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+static int tapOpenOld(struct TunTapEthernetHandler *handler, char *dev)
+{
+ char tapname[sizeof(ConfigureParams.Ethernet[0].tunnel) + 5];
+ int i, fd;
+
+ if (*dev)
+ {
+ snprintf(tapname, sizeof(tapname), "/dev/%s", dev);
+ tapname[sizeof(tapname) - 1] = '\0';
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, tapname);
+ return open(tapname, O_RDWR);
+ }
+
+ for (i = 0; i < 255; i++)
+ {
+ sprintf(tapname, "/dev/tap%d", i);
+ /* Open device */
+ if ((fd = open(tapname, O_RDWR)) > 0)
+ {
+ sprintf(dev, "tap%d", i);
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, dev);
+ return fd;
+ }
+ }
+ return -1;
+}
+
+
+#ifdef HAVE_LINUX_IF_TUN_H /* New driver support */
+
+/* pre 2.4.6 compatibility */
+#define OTUNSETNOCSUM (('T'<< 8) | 200)
+#define OTUNSETDEBUG (('T'<< 8) | 201)
+#define OTUNSETIFF (('T'<< 8) | 202)
+#define OTUNSETPERSIST (('T'<< 8) | 203)
+#define OTUNSETOWNER (('T'<< 8) | 204)
+
+static int tapOpen(struct TunTapEthernetHandler *handler, char *dev)
+{
+ struct ifreq ifr;
+
+ if ((handler->fd = open("/dev/net/tun", O_RDWR)) < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): Error opening /dev/net/tun. Check if module is loaded and privileges are OK\n", handler->base.ethX);
+ return tapOpenOld(handler, dev);
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if (*dev)
+ {
+ int len = strlen(dev);
+ if (len >= IFNAMSIZ)
+ len = IFNAMSIZ - 1;
+ memcpy(ifr.ifr_name, dev, len);
+ ifr.ifr_name[len] = '\0';
+ }
+
+ if (ioctl(handler->fd, TUNSETIFF, (void *) &ifr) < 0)
+ {
+ if (errno != EBADFD)
+ goto failed;
+
+ /* Try old ioctl */
+ if (ioctl(handler->fd, OTUNSETIFF, (void *) &ifr) < 0)
+ goto failed;
+ }
+
+ strcpy(dev, ifr.ifr_name);
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): if opened %s\n", handler->base.ethX, dev);
+ return handler->fd;
+
+ failed:
+ handler->base.close(&handler->base);
+ return -1;
+}
+
+#else
+
+static int tapOpen(struct TunTapEthernetHandler *handler, char *dev)
+{
+ return tapOpenOld(handler, dev);
+}
+
+#endif /* New driver support */
+
+
+static bool TunTapEthernetHandler_open(struct EthernetHandler *_handler)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+ int pid;
+ int status;
+ bool failed;
+
+ /* int nonblock = 1; */
+ char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+ char *devName = ConfigureParams.Ethernet[handler->base.ethX].tunnel;
+
+ handler->base.close(&handler->base);
+
+ if (strcmp(type, "none") == 0 || strlen(type) == 0)
+ {
+ return false;
+ }
+
+ /* get the tunnel nif name if provided */
+ if (strlen(devName) == 0)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tunnel name undefined\n", handler->base.ethX);
+ return false;
+ }
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): open('%s')\n", handler->base.ethX, devName);
+
+ handler->fd = tapOpen(handler, devName);
+ if (handler->fd < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): NO_NET_DRIVER_WARN '%s': %s\n", handler->base.ethX, devName, strerror(errno));
+ return false;
+ }
+
+ /* if 'bridge' mode then we are done */
+ if (strstr(ConfigureParams.Ethernet[handler->base.ethX].type, "bridge") != NULL)
+ return true;
+
+ pid = fork();
+
+ if (pid < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: fork() failed. Ethernet disabled!\n", handler->base.ethX);
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ if (pid == 0)
+ {
+ /* the arguments _need_ to be placed into the child process */
+ /* memory (otherwise this does not work here) */
+ static char tap_init[] = TAP_INIT;
+ static char tap_mtu[] = TAP_MTU;
+ char *args[] = {
+ tap_init,
+ ConfigureParams.Ethernet[handler->base.ethX].tunnel,
+ ConfigureParams.Ethernet[handler->base.ethX].ip_host,
+ ConfigureParams.Ethernet[handler->base.ethX].ip_atari,
+ ConfigureParams.Ethernet[handler->base.ethX].netmask,
+ tap_mtu, NULL
+ };
+ int result;
+
+ result = execvp(TAP_INIT, args);
+ _exit(result);
+ }
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): waiting for " TAP_INIT " at pid %d\n", handler->base.ethX, pid);
+
+ waitpid(pid, &status, 0);
+ failed = true;
+
+ if (WIFEXITED(status))
+ {
+ int err = WEXITSTATUS(status);
+
+ if (err == 255)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " not found. Ethernet disabled!\n", handler->base.ethX);
+ } else if (err != 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " failed (code %d). Ethernet disabled!\n", handler->base.ethX, err);
+ } else
+ {
+ failed = false;
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): " TAP_INIT " initialized OK\n", handler->base.ethX);
+ }
+ } else
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " could not be started. Ethernet disabled!\n", handler->base.ethX);
+ }
+
+ /* Close /dev/net/tun device if exec failed */
+ if (failed)
+ {
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ /* Set nonblocking I/O */
+ /* ioctl(handler->fd, FIONBIO, &nonblock); */
+
+ return true;
+}
+
+
+static void TunTapEthernetHandler_close(struct EthernetHandler *_handler)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): close\n", handler->base.ethX);
+
+ /* Close /dev/net/tun device */
+ if (handler->fd > 0)
+ {
+ close(handler->fd);
+ handler->fd = -1;
+ }
+}
+
+
+static int TunTapEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+
+ return read(handler->fd, buf, len);
+}
+
+
+static int TunTapEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+ int res = write(handler->fd, buf, len);
+
+ if (res < 0)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): WARNING: Couldn't transmit packet\n", handler->base.ethX);
+ }
+ return res;
+}
+
+
+struct EthernetHandler *TunTapEthernetHandler(int eth_idx)
+{
+ struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) malloc(sizeof(*handler));
+
+ handler->base.ethX = eth_idx;
+ handler->base.packet_length = 0;
+ handler->base.handlingThread = NULL;
+ handler->base.intAck = NULL;
+
+ handler->base.open = TunTapEthernetHandler_open;
+ handler->base.close = TunTapEthernetHandler_close;
+ handler->base.recv = TunTapEthernetHandler_recv;
+ handler->base.send = TunTapEthernetHandler_send;
+
+ handler->fd = -1;
+
+ return &handler->base;
+}
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/ethernet_macosx.c b/src/ethernet/ethernet_macosx.c
new file mode 100644
index 00000000..d7045e57
--- /dev/null
+++ b/src/ethernet/ethernet_macosx.c
@@ -0,0 +1,688 @@
+/*
+ * ethernet_macosx.c - Mac OS X Ethernet support (via Berkley Packet Filter device)
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && defined(__APPLE__) && defined(ENABLE_BPF)
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <sys/fcntl.h>
+#include <sys/select.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/bpf.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "fd_trans.h"
+
+#define ETH_HELPER "bpf_helper"
+
+struct BPFEthernetHandler
+{
+ struct EthernetHandler base;
+
+ bool debug;
+ int fd;
+ int buf_len;
+ struct bpf_hdr *bpf_buf;
+
+ /* variables used for looping over multiple packets */
+ int read_len;
+ struct bpf_hdr *bpf_packet;
+};
+
+/* BPF filter program to ensure only ethernet packets are processed which are for configured MAC */
+static struct bpf_insn bpf_filter_mac_insn[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0), /* ld P[0:4] */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAAAAAA, 0, 3), /* jeq destMAC, CONT, NOK */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 4), /* CONT:ld P[4:2] */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAA, 0, 1), /* jeq destMAC, OK, NOK */
+ BPF_STMT(BPF_RET + BPF_K, (u_int) -1), /* OK: ret -1 */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* NOK: ret 0 */
+};
+
+static struct bpf_program bpf_filter_mac = { sizeof(bpf_filter_mac_insn) / sizeof(struct bpf_insn), &bpf_filter_mac_insn[0] };
+
+/* BPF filter program to process multicast ethernet and configured MAC address packets */
+static struct bpf_insn bpf_filter_mcast_insn[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0), /* ld P[0:4] */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAAAAAA, 0, 2), /* jeq destMAC, CONT, NOK1 */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 4), /* CONT:ld P[4:2] */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAA, 2, 0), /* jeq destMAC, OK, NOK1 */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 0), /* NOK1:ld P[0:1] */
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x01, 0, 1), /* if (A & 1) OK else NOK2 */
+ BPF_STMT(BPF_RET + BPF_K, (u_int) -1), /* OK: ret -1 */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* NOK2:ret 0 */
+};
+
+static struct bpf_program bpf_filter_mcast = { sizeof(bpf_filter_mcast_insn) / sizeof(struct bpf_insn), &bpf_filter_mcast_insn[0] };
+
+/* BPF filter program to process ethernet packets if the configured IP address matches */
+static struct bpf_insn bpf_filter_ip_insn[] = {
+ /* Check ethernet protocol (IP and ARP supported) */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), /* ld P[12:2] */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 3, 0), /* jeq IP_Type, CONT_IP, CONT */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 5), /* CONT: jeq IP_Type, CONT_ARP, NOK */
+ /* Load IP address from offset 38 */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 38), /* CONT_ARP:ld P[38:4] */
+ BPF_JUMP(BPF_JMP + BPF_JA, 1, 1, 1), /* jmp COMPARE */
+ /* Load IP address from offset 30 */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30), /* CONT_IP: ld P[30:4] */
+ /* Compare value with expected dest IP address */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAAAAAA, 0, 1), /* COMPARE: jeq destIP, OK, NOK */
+ BPF_STMT(BPF_RET + BPF_K, (u_int) -1), /* OK: ret -1 */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* NOK: ret 0 */
+};
+
+static struct bpf_program bpf_filter_ip = { sizeof(bpf_filter_ip_insn) / sizeof(struct bpf_insn), &bpf_filter_ip_insn[0] };
+
+
+/* Ethernet frame */
+struct ethernet_frame
+{
+ unsigned char dst_addr[6];
+ unsigned char src_addr[6];
+ unsigned char type[2];
+ unsigned char payload;
+};
+
+/* ARP packet structure */
+struct arp_packet
+{
+ unsigned char hardware_type[2];
+ unsigned char proto_type[2];
+ unsigned char hardware_addr_length;
+ unsigned char proto_addr_length;
+ unsigned char opcode[2];
+ unsigned char src_mac[6];
+ unsigned char src_ip[4];
+ unsigned char dst_mac[6];
+ unsigned char dst_ip[4];
+};
+
+/* IP packet structure */
+struct ip_packet
+{
+ unsigned char version_hdr_len;
+ unsigned char services;
+ unsigned char tot_len[2];
+ unsigned char ident[2];
+ unsigned char flags_frag_offset[2];
+ unsigned char ttl;
+ unsigned char proto_type;
+ unsigned char hdr_chksum[2];
+ unsigned char src_ip[4];
+ unsigned char dst_ip[4];
+ unsigned char data;
+};
+
+static void dump_frame(const char *prefix, const struct ethernet_frame *frame)
+{
+ unsigned short int frame_type = (frame->type[0] << 8) | frame->type[1];
+
+ LOG_TRACE(TRACE_ETHERNET, " %s: %02x:%02x:%02x:%02x:%02x:%02x > %02x:%02x:%02x:%02x:%02x:%02x (Type %04x)\n", prefix,
+ frame->src_addr[0], frame->src_addr[1], frame->src_addr[2], frame->src_addr[3], frame->src_addr[4],
+ frame->src_addr[5], frame->dst_addr[0], frame->dst_addr[1], frame->dst_addr[2], frame->dst_addr[3],
+ frame->dst_addr[4], frame->dst_addr[5], frame_type);
+ if (frame_type == 0x0806) /* TYPE_ARP */
+ {
+ const struct arp_packet *arp = (const struct arp_packet *) &frame->payload;
+
+ LOG_TRACE(TRACE_ETHERNET, " %s: ARP packet %d.%d.%d.%d > %d.%d.%d.%d (Type %04x)\n", prefix,
+ arp->src_ip[0], arp->src_ip[1], arp->src_ip[2], arp->src_ip[3],
+ arp->dst_ip[0], arp->dst_ip[1], arp->dst_ip[2], arp->dst_ip[3],
+ (arp->proto_type[0] << 8) | arp->proto_type[1]);
+ } else if (frame_type == 0x0800) /* TYPE_IP */
+ {
+ const struct ip_packet *ip = (const struct ip_packet *) &frame->payload;
+ int tot_len = (ip->tot_len[0] << 8) + ip->tot_len[1];
+
+ LOG_TRACE(TRACE_ETHERNET, " %s: IP packet %d.%d.%d.%d > %d.%d.%d.%d (Type %04x Total %02x%02x = %d, Payload=%d)\n", prefix,
+ ip->src_ip[0], ip->src_ip[1], ip->src_ip[2], ip->src_ip[3],
+ ip->dst_ip[0], ip->dst_ip[1], ip->dst_ip[2], ip->dst_ip[3],
+ ip->proto_type, ip->tot_len[0], ip->tot_len[1], tot_len, tot_len - (int) sizeof(struct ip_packet) + 1);
+
+#if 0
+ for (unsigned char *ptr=&ip->data; ptr<(unsigned char*)&ip->data+tot_len; ptr++)
+ LOG_TRACE(TRACE_ETHERNET, ">%*s\n", tot_len-(sizeof(struct ip_packet)-1), &ip->data);
+#endif
+ }
+}
+
+static void dump_bpf_buf(const char *prefix, struct bpf_hdr *bpf_buf)
+{
+ if (bpf_buf->bh_datalen > 0)
+ {
+ LOG_TRACE(TRACE_ETHERNET, " %s: %d bytes in buf:\n", prefix, bpf_buf->bh_datalen);
+
+ dump_frame(prefix, (const struct ethernet_frame *) ((char *) bpf_buf + bpf_buf->bh_hdrlen));
+ }
+}
+
+
+static void reset_read_pos(struct BPFEthernetHandler *handler)
+{
+ handler->read_len = 0;
+ handler->bpf_packet = NULL;
+}
+
+
+static bool BPFEthernetHandler_open(struct EthernetHandler *_handler)
+{
+ struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+ /* int nonblock = 1; */
+ char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+ char *dev_name = ConfigureParams.Ethernet[handler->base.ethX].tunnel;
+ int sockfd;
+ char exe_path[2048];
+ CFURLRef url;
+ CFURLRef url2;
+ pid_t pid;
+ struct ifreq ifr;
+ int immediate;
+ int promiscuous;
+ int complete;
+ int seesent;
+ int len;
+
+ handler->base.close(&handler->base);
+
+ if (strcmp(type, "none") == 0 || strlen(type) == 0)
+ {
+ return false;
+ }
+
+ handler->debug = strstr(type, "debug") != NULL;
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): debug mode=%d\n", handler->base.ethX, handler->debug);
+ }
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): open() type=%s\n", handler->base.ethX, type);
+ }
+ if (strstr(type, "bridge") == NULL)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): unsupported type '%s'\n", handler->base.ethX, type);
+ return false;
+ }
+
+ if ((sockfd = fd_setup_server()) < 0)
+ {
+ fprintf(stderr, "receiver: failed to create socket.\n");
+ return false;
+ }
+
+ /******************************************************
+ Fork child process to get an open BPF file descriptor
+ ******************************************************/
+ url = CFBundleCopyExecutableURL(CFBundleGetMainBundle());
+ url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+
+ CFURLGetFileSystemRepresentation(url2, true, (uint8_t *) exe_path, sizeof(exe_path));
+ CFRelease(url2);
+ CFRelease(url);
+ len = (int)strlen(exe_path);
+ if (len > 0 && exe_path[len - 1] != '/')
+ strcat(exe_path, "/");
+ strcat(exe_path, ETH_HELPER);
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): starting helper from <%s>\n", handler->base.ethX, exe_path);
+ }
+
+ pid = fork();
+
+ if (pid < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): ERROR: fork() failed. Ethernet disabled!\n", handler->base.ethX);
+ close(sockfd);
+ return false;
+ } else if (pid == 0)
+ {
+ int result;
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): " ETH_HELPER " child running\n", handler->base.ethX);
+ }
+ result = execl(exe_path, exe_path, NULL);
+
+ _exit(result);
+ }
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): waiting for " ETH_HELPER " (PID %d) to send file descriptor\n",
+ handler->base.ethX, pid);
+ }
+ handler->fd = fd_receive(sockfd, pid);
+ close(sockfd);
+ if (handler->fd < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): failed receiving file descriptor from " ETH_HELPER ".\n", handler->base.ethX);
+ return false;
+ }
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): got file descriptor %d\n", handler->base.ethX, handler->fd);
+ }
+
+
+ /******************************************************
+ Configure BPF device
+ ******************************************************/
+ /* associate with specified interface */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev_name, IFNAMSIZ - 1);
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): connecting with device %s\n", handler->base.ethX, dev_name);
+ }
+ if (ioctl(handler->fd, BIOCSETIF, &ifr) > 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Failed associating to %s: %s\n", handler->base.ethX, dev_name, strerror(errno));
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ /* activate immediate mode */
+ immediate = 1;
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): enabling immediate mode\n", handler->base.ethX);
+ }
+ if (ioctl(handler->fd, BIOCIMMEDIATE, &immediate) == -1)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to set immediate mode: %s\n", handler->base.ethX, strerror(errno));
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ /* request buffer length */
+ if (ioctl(handler->fd, BIOCGBLEN, &handler->buf_len) == -1)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to get buffer length: %s\n", handler->base.ethX, strerror(errno));
+ handler->base.close(&handler->base);
+ return false;
+ }
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): buf_len=%d\n", handler->base.ethX, handler->buf_len);
+ }
+ handler->bpf_buf = (struct bpf_hdr *) malloc(handler->buf_len);
+
+ /* activate promiscuous mode */
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): enabling promiscuous mode\n", handler->base.ethX);
+ }
+ promiscuous = 1;
+ if (ioctl(handler->fd, BIOCPROMISC, &promiscuous) == -1)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to set promiscuous mode: %s\n", handler->base.ethX, strerror(errno));
+#if 0
+ handler->base.close(&handler->base);
+ return false;
+#endif
+ }
+
+ /* activate "header complete" mode */
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): enabling header complete mode\n", handler->base.ethX);
+ }
+ complete = 1;
+ if (ioctl(handler->fd, BIOCGHDRCMPLT, &complete) == -1)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to set header complete mode: %s\n", handler->base.ethX, strerror(errno));
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ /* disable "see sent" mode */
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): disabling see sent mode\n", handler->base.ethX);
+ }
+ seesent = 1;
+ if (ioctl(handler->fd, BIOCSSEESENT, &seesent) == -1)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to disable see sent mode: %s\n", handler->base.ethX, strerror(errno));
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ if (strstr(type, "nofilter") == NULL)
+ {
+ /* convert and validate MAC address from configuration */
+ /* default MAC Address is just made up */
+ uint8_t mac_addr[6] = { '\0', 'A', 'E', 'T', 'H', 0 };
+
+ /* convert user-defined MAC Address from string to 6 bytes array */
+ char *mac_text = ConfigureParams.Ethernet[handler->base.ethX].mac_addr;
+ bool format_OK = false;
+ uint8_t ip_addr[4];
+ struct bpf_program *filter;
+
+ mac_addr[5] = handler->base.ethX + '0';
+ if (strlen(mac_text) == 2 * 6 + 5 && (mac_text[2] == ':' || mac_text[2] == '-'))
+ {
+ int matched;
+ int md[6] = { 0, 0, 0, 0, 0, 0 };
+
+ mac_text[2] = mac_text[5] = mac_text[8] = mac_text[11] = mac_text[14] = ':';
+ matched = sscanf(mac_text, "%02x:%02x:%02x:%02x:%02x:%02x",
+ &md[0], &md[1], &md[2], &md[3], &md[4], &md[5]);
+
+ if (matched == 6)
+ {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ mac_addr[i] = md[i];
+ format_OK = true;
+ }
+ }
+ if (!format_OK)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Invalid MAC address: %s\n", handler->base.ethX, mac_text);
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ /* convert and validate specified IP address */
+
+ if (!inet_pton(AF_INET, ConfigureParams.Ethernet[handler->base.ethX].ip_atari, ip_addr))
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Invalid IP address specified: %s\n", handler->base.ethX,
+ ConfigureParams.Ethernet[handler->base.ethX].ip_atari);
+ handler->base.close(&handler->base);
+ return false;
+ }
+
+ /* modify filter program to use specified IP address */
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): setting filter program for MAC address %s\n", handler->base.ethX,
+ mac_text);
+ }
+
+ /* Select filter program according to chosen options */
+
+ if (strstr(type, "mcast"))
+ {
+ filter = &bpf_filter_mcast;
+
+ /* patch filter instructions to detect valid MAC address */
+ filter->bf_insns[1].k = (mac_addr[0] << 24) | (mac_addr[1] << 16) | (mac_addr[2] << 8) | mac_addr[3];
+ filter->bf_insns[3].k = (mac_addr[4] << 8) | mac_addr[5];
+ } else if (strstr(type, "ip"))
+ {
+ filter = &bpf_filter_ip;
+
+ /* patch filter instructions to detect valid IP address */
+ filter->bf_insns[6].k = (ip_addr[0] << 24) | (ip_addr[1] << 16) | (ip_addr[2] << 8) | ip_addr[3];
+
+ } else
+ {
+ filter = &bpf_filter_mac;
+ /* patch filter instructions to detect valid MAC address */
+ filter->bf_insns[1].k = (mac_addr[0] << 24) | (mac_addr[1] << 16) | (mac_addr[2] << 8) | mac_addr[3];
+ filter->bf_insns[3].k = (mac_addr[4] << 8) | mac_addr[5];
+ }
+
+ /* Enable filter program */
+ if (ioctl(handler->fd, BIOCSETF, filter) == -1)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to load filter program: %s\n", handler->base.ethX, strerror(errno));
+ handler->base.close(&handler->base);
+ return false;
+ }
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): filter program load\n", handler->base.ethX);
+ }
+ } else
+ {
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): filter program skipped\n", handler->base.ethX);
+ }
+ }
+
+
+ /* Reset bpf buffer read position (for handling multi packet) */
+ reset_read_pos(handler);
+
+ return true;
+}
+
+
+static void BPFEthernetHandler_close(struct EthernetHandler *_handler)
+{
+ struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): close\n", handler->base.ethX);
+ }
+
+ reset_read_pos(handler);
+
+ if (handler->fd > 0)
+ {
+ close(handler->fd);
+ handler->fd = -1;
+ }
+
+ free(handler->bpf_buf);
+ handler->bpf_buf = NULL;
+
+ handler->debug = false;
+}
+
+
+static int BPFEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+ struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+ char *ptr;
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): recv(len=%d)\n", handler->base.ethX, len);
+ }
+
+ /* No more cached packet in memory? */
+ if (handler->bpf_packet == NULL)
+ {
+ /* Read a packet from the ethernet device, timeout after 2s */
+ /* Initialize the file descriptor set. */
+ fd_set set;
+ struct timeval timeout;
+
+ FD_ZERO(&set);
+ FD_SET(handler->fd, &set);
+
+ /* Initialize the timeout data structure. */
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ /* select returns 0 if timeout, 1 if input available, -1 if error. */
+ if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) == 1)
+ {
+ /* Read from BPF device */
+ handler->read_len = read(handler->fd, handler->bpf_buf, handler->buf_len);
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): %d bytes read => %d data bytes\n", handler->base.ethX, handler->read_len,
+ handler->bpf_buf->bh_datalen);
+ }
+ if (handler->read_len > 0)
+ {
+ handler->bpf_packet = handler->bpf_buf;
+
+ if ((int)BPF_WORDALIGN(handler->bpf_buf->bh_hdrlen + handler->bpf_buf->bh_caplen) < handler->read_len)
+ {
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): More than one packet received by BPF\n"
+ " read_len = %d\n"
+ " bh_hdrlen=%d\n"
+ " bh_datalen=%d\n"
+ " bh_caplen=%d\n", handler->base.ethX, handler->read_len, handler->bpf_buf->bh_hdrlen,
+ handler->bpf_buf->bh_datalen, handler->bpf_buf->bh_caplen);
+ }
+ }
+ }
+ }
+ } else
+ {
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): using cached %d data bytes from previous read\n", handler->base.ethX,
+ handler->read_len);
+ }
+ }
+
+ ptr = (char *) handler->bpf_packet;
+
+ if (handler->bpf_packet && (ptr < ((char *) handler->bpf_buf + handler->read_len)))
+ {
+ char *frame_start = ptr + handler->bpf_packet->bh_hdrlen;
+ int frame_len = handler->bpf_packet->bh_caplen;
+
+ ptr += BPF_WORDALIGN(handler->bpf_packet->bh_hdrlen + handler->bpf_packet->bh_caplen);
+
+ /* Copy valid frame data */
+ if (frame_len <= len)
+ {
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): frame length %d bytes\n", handler->base.ethX, frame_len);
+ dump_bpf_buf("recv", handler->bpf_packet);
+ }
+ memcpy(buf, frame_start, frame_len);
+ } else
+ {
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): Host side received %d bytes of data but only %d bytes expected by guest.\n"
+ "There are probably multiple packets in the received %d bytes.\n"
+ "Packet discarded!\n", handler->base.ethX, frame_len, len, handler->read_len);
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): read_len = %d\n"
+ " bh_hdrlen=%d\n"
+ " bh_datalen=%d\n"
+ " bh_caplen=%d\n", handler->base.ethX, handler->read_len, handler->bpf_packet->bh_hdrlen, handler->bpf_packet->bh_datalen,
+ handler->bpf_packet->bh_caplen);
+ frame_len = 0;
+ }
+
+ if (ptr < ((char *) handler->bpf_buf + handler->read_len))
+ handler->bpf_packet = (struct bpf_hdr *) ptr;
+ else
+ handler->bpf_packet = NULL;
+
+ return frame_len;
+ }
+ return 0;
+}
+
+
+static int BPFEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+ struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+ int res = -1;
+
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): send(len=%d)\n", handler->base.ethX, len);
+ }
+
+ if (len > 0)
+ {
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): send(len=%d)\n", handler->base.ethX, len);
+ dump_frame("send", (const struct ethernet_frame *) buf);
+ }
+ res = write(handler->fd, buf, len);
+ if (res < 0)
+ {
+ if (handler->debug)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): WARNING: Couldn't transmit packet\n", handler->base.ethX);
+ }
+ }
+ }
+ return res;
+}
+
+
+
+struct EthernetHandler *BPFEthernetHandler(int eth_idx)
+{
+ struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) malloc(sizeof(*handler));
+
+ handler->base.ethX = eth_idx;
+ handler->base.packet_length = 0;
+ handler->base.handlingThread = NULL;
+ handler->base.intAck = NULL;
+
+ handler->base.open = BPFEthernetHandler_open;
+ handler->base.close = BPFEthernetHandler_close;
+ handler->base.recv = BPFEthernetHandler_recv;
+ handler->base.send = BPFEthernetHandler_send;
+
+ handler->debug = false;
+ handler->fd = -1;
+ handler->buf_len = 0;
+ handler->bpf_buf = NULL;
+ handler->read_len = 0;
+ handler->bpf_packet = NULL;
+
+ return &handler->base;
+}
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/ethernet_nfapi.h b/src/ethernet/ethernet_nfapi.h
new file mode 100644
index 00000000..d97bcaf4
--- /dev/null
+++ b/src/ethernet/ethernet_nfapi.h
@@ -0,0 +1,55 @@
+/*
+ * ARAnyM ethernet driver - header file.
+ *
+ * Copyright (c) 2002-2004 Standa and Petr of ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef _ARAETHER_NFAPI_H
+#define _ARAETHER_NFAPI_H
+
+/*
+ The code for the FreeMiNT driver was moved to the FreeMiNT CVS:
+ freemint/sys/sockets/xif/nfeth.
+
+ If you edit this file then you would need to synchronize it with the
+ driver in the freemint CVS (nfeth_nfapi.h).
+
+ if you change anything in the enum {} below you have to increase
+ this ARAETHER_NFAPI_VERSION!
+*/
+#define ARAETHER_NFAPI_VERSION 0x00000005
+
+enum {
+ GET_VERSION = 0, /* no parameters, return NFAPI_VERSION in d0 */
+ XIF_INTLEVEL, /* no parameters, return Interrupt Level in d0 */
+ XIF_IRQ, /* acknowledge interrupt from host */
+ XIF_START, /* (ethX), called on 'ifup', start receiver thread */
+ XIF_STOP, /* (ethX), called on 'ifdown', stop the thread */
+ XIF_READLENGTH, /* (ethX), return size of network data block to read */
+ XIF_READBLOCK, /* (ethX, buffer, size), read block of network data */
+ XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */
+ XIF_GET_MAC, /* (ethX, buffer, size), return MAC HW addr in buffer */
+ XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */
+ XIF_GET_IPATARI, /* (ethX, buffer, size), return IP address of atari */
+ XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */
+};
+
+#define ETH(a) (nfEtherID + a)
+
+#endif /* _ARAETHER_NFAPI_H */
diff --git a/src/ethernet/ethernet_win32.c b/src/ethernet/ethernet_win32.c
new file mode 100644
index 00000000..594c2a04
--- /dev/null
+++ b/src/ethernet/ethernet_win32.c
@@ -0,0 +1,456 @@
+/*
+ ethernet_win32.c -- Interaction with Windows tap driver in a Win32/Cygwin environment
+ Copyright (C) 2002-2004 Ivo Timmermans <ivo@xxxxxxxxxxxx>,
+ 2002-2004 Guus Sliepen <guus@xxxxxxxxxxxx>
+
+ 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 see <http://www.gnu.org/licenses/>
+*/
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && (defined(__CYGWIN32__) || defined(__WIN32__))
+
+#define _WIN32_VERSION 0x501
+#undef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#if !defined(__CYGWIN__)
+#include <winsock2.h>
+#endif
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN /* to avoid redefinition in SDL headers */
+
+#include <winioctl.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include <errno.h>
+
+#define MTU 1500
+
+/*============= */
+/* TAP IOCTLs */
+/*============= */
+
+#define TAP_CONTROL_CODE(request,method) \
+ CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+/*================= */
+/* Registry keys */
+/*================= */
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+/*====================== */
+/* Filesystem prefixes */
+/*====================== */
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define SYSDEVICEDIR "\\Device\\"
+#define USERDEVICEDIR "\\DosDevices\\Global\\"
+#define TAPSUFFIX ".tap"
+
+/*========================================================= */
+/* TAP_COMPONENT_ID -- This string defines the TAP driver */
+/* type -- different component IDs can reside in the system */
+/* simultaneously. */
+/*========================================================= */
+
+#define TAP_COMPONENT_ID "tap0801"
+
+
+struct WinTapEthernetHandler
+{
+ struct EthernetHandler base;
+
+ OVERLAPPED read_overlapped;
+ OVERLAPPED write_overlapped;
+ HANDLE device_handle;
+ char *device;
+ char *iface;
+
+ int device_total_in;
+ int device_total_out;
+};
+
+
+static char *winerror(int err)
+{
+ static char buf[1024];
+ char *newline;
+
+ if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL))
+ {
+ strncpy(buf, "(unable to format errormessage)", sizeof(buf));
+ }
+
+ if ((newline = strchr(buf, '\r')))
+ *newline = '\0';
+
+ return buf;
+}
+
+
+static bool is_tap_win32_dev(const char *guid)
+{
+ HKEY netcard_key;
+ LONG status;
+ DWORD len;
+ int i = 0;
+
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &netcard_key);
+
+ if (status != ERROR_SUCCESS)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\n", ADAPTER_KEY);
+ return false;
+ }
+
+ for (;;)
+ {
+ char enum_name[256];
+ char unit_string[256];
+ HKEY unit_key;
+ char component_id_string[] = "ComponentId";
+ char component_id[256];
+ char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+ char net_cfg_instance_id[256];
+ DWORD data_type;
+
+ len = sizeof(enum_name);
+ status = RegEnumKeyEx(netcard_key, i, enum_name, &len, NULL, NULL, NULL, NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error enumerating registry subkeys of key: %s\n", ADAPTER_KEY);
+ return false;
+ }
+
+ snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, enum_name);
+
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key);
+
+ if (status != ERROR_SUCCESS)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\n", unit_string);
+ return false;
+ } else
+ {
+ len = sizeof(component_id);
+ status = RegQueryValueEx(unit_key, component_id_string, NULL, &data_type, (BYTE *) component_id, &len);
+
+ if (!(status != ERROR_SUCCESS || data_type != REG_SZ))
+ {
+ len = sizeof(net_cfg_instance_id);
+ status = RegQueryValueEx(unit_key,
+ net_cfg_instance_id_string,
+ NULL, &data_type, (BYTE *) net_cfg_instance_id, &len);
+
+ if (status == ERROR_SUCCESS && data_type == REG_SZ)
+ {
+ if (!strcmp(net_cfg_instance_id, guid))
+ {
+ RegCloseKey(unit_key);
+ RegCloseKey(netcard_key);
+ return true;
+ }
+ }
+ }
+ RegCloseKey(unit_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey(netcard_key);
+ return false;
+}
+
+
+static int get_device_guid(char *name, int name_size, char *actual_name, int actual_name_size)
+{
+ LONG status;
+ HKEY control_net_key;
+ DWORD len;
+ int i = 0;
+ int stop = 0;
+
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &control_net_key);
+
+ if (status != ERROR_SUCCESS)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
+ return -1;
+ }
+
+ while (!stop)
+ {
+ char enum_name[256];
+ char connection_string[256];
+ HKEY connection_key;
+ char name_data[256];
+ DWORD name_type;
+ const char name_string[] = "Name";
+
+ len = sizeof(enum_name);
+ status = RegEnumKeyEx(control_net_key, i, enum_name, &len, NULL, NULL, NULL, NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error enumerating registry subkeys of key: %s\n", NETWORK_CONNECTIONS_KEY);
+ return -1;
+ }
+
+ snprintf(connection_string,
+ sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name);
+
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string, 0, KEY_READ, &connection_key);
+
+ if (status == ERROR_SUCCESS)
+ {
+ len = sizeof(name_data);
+ status = RegQueryValueEx(connection_key, name_string, NULL, &name_type, (BYTE *) name_data, &len);
+
+ if (status != ERROR_SUCCESS || name_type != REG_SZ)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\\%s\\%s",
+ NETWORK_CONNECTIONS_KEY, connection_string, name_string);
+ return -1;
+ } else
+ {
+ if (is_tap_win32_dev(enum_name))
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: found TAP device named \"%s\" ~ \"%s\"\n", name_data, actual_name);
+
+ snprintf(name, name_size, "%s", enum_name);
+ if (actual_name)
+ {
+ if (strcmp(actual_name, "") != 0)
+ {
+ if (strcmp(name_data, actual_name) != 0)
+ {
+ RegCloseKey(connection_key);
+ ++i;
+ continue;
+ }
+ } else
+ {
+ snprintf(actual_name, actual_name_size, "%s", name_data);
+ }
+ }
+ stop = 1;
+ }
+ }
+
+ RegCloseKey(connection_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey(control_net_key);
+
+ if (stop == 0)
+ return -1;
+
+ return 0;
+}
+
+
+static bool WinTapEthernetHandler_open(struct EthernetHandler *_handler)
+{
+ struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+ char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+ char device_path[256];
+ char device_guid[256];
+ char name_buffer[256];
+
+ handler->base.close(&handler->base);
+
+ if (strcmp(type, "none") == 0 || strlen(type) == 0)
+ {
+ return false;
+ }
+
+ if (strlen(ConfigureParams.Ethernet[handler->base.ethX].tunnel) == 0)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): tunnel name undefined\n", handler->base.ethX);
+ return false;
+ }
+
+ strcpy(name_buffer, ConfigureParams.Ethernet[handler->base.ethX].tunnel);
+
+ if (get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)) < 0)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: WinTap(%d): ERROR: Could not find Windows tap device: %s\n", handler->base.ethX, winerror(GetLastError()));
+ return false;
+ }
+
+ /*
+ * Open Windows TAP-Win32 adapter
+ */
+ snprintf(device_path, sizeof(device_path), "%s%s%s", USERMODEDEVICEDIR, device_guid, TAPSUFFIX);
+
+ handler->device_handle =
+ CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
+
+ if (handler->device_handle == INVALID_HANDLE_VALUE)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: WinTap(%d): ERROR: Could not open (%s) Windows tap device: %s\n", handler->base.ethX, device_path,
+ winerror(GetLastError()));
+ return false;
+ }
+ handler->device = strdup(device_path);
+
+ handler->read_overlapped.Offset = 0;
+ handler->read_overlapped.OffsetHigh = 0;
+ handler->read_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ handler->write_overlapped.Offset = 0;
+ handler->write_overlapped.OffsetHigh = 0;
+ handler->write_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): tap device open %s [handle=%p]\n", handler->base.ethX, device_path, (void *) handler->device_handle);
+ return true;
+}
+
+
+static void WinTapEthernetHandler_close(struct EthernetHandler *_handler)
+{
+ struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+
+ if (handler->device_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(handler->device_handle);
+ handler->device_handle = INVALID_HANDLE_VALUE;
+ }
+ free(handler->device);
+ handler->device = NULL;
+}
+
+
+static int WinTapEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+ struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+ DWORD lenin;
+ BOOL result;
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Read packet from %s\n", handler->base.ethX, handler->device);
+
+ result = ReadFile(handler->device_handle, buf, len, &lenin, &handler->read_overlapped);
+
+ if (!result)
+ {
+ DWORD error = GetLastError();
+
+ switch (error)
+ {
+ case ERROR_IO_PENDING:
+ WaitForSingleObject(handler->read_overlapped.hEvent, INFINITE);
+ result = GetOverlappedResult(handler->device_handle, &handler->read_overlapped, &lenin, 0);
+ if (result)
+ break;
+ /* fallthrough */
+ default:
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Error while reading from %s: %s\n", handler->base.ethX, handler->device,
+ winerror(GetLastError()));
+ return -1;
+ }
+ }
+
+ handler->device_total_in += lenin;
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Read packet done (len %d)\n", handler->base.ethX, (int) lenin);
+ return lenin;
+}
+
+
+static int WinTapEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+ struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+ DWORD lenout;
+ BOOL result;
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Writing packet of %d bytes to %s\n", handler->base.ethX, len, handler->device);
+
+ result = WriteFile(handler->device_handle, buf, len, &lenout, &handler->write_overlapped);
+ if (!result)
+ {
+ DWORD error = GetLastError();
+
+ switch (error)
+ {
+ case ERROR_IO_PENDING:
+ WaitForSingleObject(handler->write_overlapped.hEvent, INFINITE);
+ break;
+ default:
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Error while writing to %s: %s\n", handler->base.ethX, handler->device,
+ winerror(GetLastError()));
+ return -1;
+ }
+ }
+
+ handler->device_total_out += lenout;
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Writing packet done\n", handler->base.ethX);
+ return lenout;
+}
+
+
+struct EthernetHandler *WinTapEthernetHandler(int eth_idx)
+{
+ struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) malloc(sizeof(*handler));
+
+ handler->base.ethX = eth_idx;
+ handler->base.packet_length = 0;
+ handler->base.handlingThread = NULL;
+ handler->base.intAck = NULL;
+
+ handler->base.open = WinTapEthernetHandler_open;
+ handler->base.close = WinTapEthernetHandler_close;
+ handler->base.recv = WinTapEthernetHandler_recv;
+ handler->base.send = WinTapEthernetHandler_send;
+
+ handler->device_handle = INVALID_HANDLE_VALUE;
+ handler->device = NULL;
+ handler->iface = NULL;
+
+ handler->device_total_in = 0;
+ handler->device_total_out = 0;
+
+ return &handler->base;
+}
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/fd_trans.c b/src/ethernet/fd_trans.c
new file mode 100644
index 00000000..354fd602
--- /dev/null
+++ b/src/ethernet/fd_trans.c
@@ -0,0 +1,258 @@
+/*
+ * fd_trans.c
+ * Used to transfer a file descriptor between two Unix processes
+ *
+ * Inspired by Amit Singhs description in the "Mac OS X manual":
+ * http://topiks.org/mac-os-x/0321278542/ch09lev1sec11.html
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*
+ * only used by ethernet implementation for macOS so far
+ */
+#ifdef __APPLE__
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+
+#include "fd_trans.h"
+
+#define SOCKET_PATH "/tmp/fd_trans_socket"
+
+#define MSG_CONTROL_SIZE CMSG_SPACE(sizeof(int))
+
+
+
+static int fd_receive_using_sockfd(int *fd, int sockfd)
+{
+ ssize_t ret;
+ u_char c;
+ int errcond = 0;
+ struct iovec iovec[1];
+ struct msghdr msg;
+ struct cmsghdr *cmsghdrp;
+ union {
+ struct cmsghdr cmsghdr;
+ u_char msg_control[MSG_CONTROL_SIZE];
+ } cmsghdr_msg_control;
+
+ iovec[0].iov_base = (void*)&c;
+ iovec[0].iov_len = 1;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iovec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (void*)cmsghdr_msg_control.msg_control;
+ msg.msg_controllen = sizeof(cmsghdr_msg_control.msg_control);
+ msg.msg_flags = 0;
+
+ if ((ret = recvmsg(sockfd, &msg, 0)) <= 0)
+ {
+ perror("recvmsg");
+ *fd = -1;
+ return (int)ret;
+ }
+
+ cmsghdrp = CMSG_FIRSTHDR(&msg);
+ if (cmsghdrp == NULL)
+ {
+ *fd = -1;
+ return (int)ret;
+ }
+
+ if (cmsghdrp->cmsg_len != CMSG_LEN(sizeof(int)))
+ errcond++;
+
+ if (cmsghdrp->cmsg_level != SOL_SOCKET)
+ errcond++;
+
+ if (cmsghdrp->cmsg_type != SCM_RIGHTS)
+ errcond++;
+
+ if (errcond)
+ {
+ fprintf(stderr, "receiver: %d errors in received message\n", errcond);
+ *fd = -1;
+ } else
+ *fd = *((int *)CMSG_DATA(cmsghdrp));
+
+ return (int)ret;
+}
+
+
+int fd_setup_server(void)
+{
+ int sockfd;
+ socklen_t len;
+ struct sockaddr_un server_unix_addr;
+ const char *name = SOCKET_PATH;
+
+ if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ perror("socket");
+ return sockfd;
+ }
+
+ unlink(name);
+ bzero((char *)&server_unix_addr, sizeof(server_unix_addr));
+ server_unix_addr.sun_family = AF_LOCAL;
+ strcpy(server_unix_addr.sun_path, name);
+ len = (socklen_t)strlen(name) + 1;
+ len += sizeof(server_unix_addr.sun_family);
+
+ if (bind(sockfd, (struct sockaddr *)&server_unix_addr, len) < 0)
+ {
+ close(sockfd);
+ return -1;
+ }
+
+ return sockfd;
+}
+
+
+static int fd_send_using_sockfd(int fd, int sockfd)
+{
+ ssize_t ret;
+ struct iovec iovec[1];
+ struct msghdr msg;
+ struct cmsghdr *cmsghdrp;
+ union {
+ struct cmsghdr cmsghdr;
+ u_char msg_control[MSG_CONTROL_SIZE];
+ } cmsghdr_msg_control;
+ static char iov_base[1] = { 0 };
+
+ iovec[0].iov_base = iov_base;
+ iovec[0].iov_len = 1;
+
+ msg.msg_name = NULL; /* address (optional) */
+ msg.msg_namelen = 0; /* size of address */
+ msg.msg_iov = iovec; /* scatter/gather array */
+ msg.msg_iovlen = 1; /* members in msg.msg_iov */
+ msg.msg_control = (void*)cmsghdr_msg_control.msg_control; /* ancillary data */
+ /* ancillary data buffer length */
+ msg.msg_controllen = sizeof(cmsghdr_msg_control.msg_control);
+ msg.msg_flags = 0; /* flags on received message */
+
+ /* CMSG_FIRSTHDR() returns a pointer to the first cmsghdr structure in */
+ /* the ancillary data associated with the given msghdr structure */
+ cmsghdrp = CMSG_FIRSTHDR(&msg);
+
+ cmsghdrp->cmsg_len = CMSG_LEN(sizeof(int)); /* data byte count */
+ cmsghdrp->cmsg_level = SOL_SOCKET; /* originating protocol */
+ cmsghdrp->cmsg_type = SCM_RIGHTS; /* protocol-specified type */
+
+ /* CMSG_DATA() returns a pointer to the data array associated with */
+ /* the cmsghdr structure pointed to by cmsghdrp */
+ *((int *)CMSG_DATA(cmsghdrp)) = fd;
+
+ if ((ret = sendmsg(sockfd, &msg, 0)) < 0)
+ {
+ perror("sendmsg");
+ return (int)ret;
+ }
+
+ return 0;
+}
+
+
+void fd_send(int fd)
+{
+ int sockfd, len;
+ struct sockaddr_un server_unix_addr;
+ int i, ret = -1;
+
+ bzero((char *)&server_unix_addr, sizeof(server_unix_addr));
+ strcpy(server_unix_addr.sun_path, SOCKET_PATH);
+ server_unix_addr.sun_family = AF_LOCAL;
+ len = strlen(SOCKET_PATH) + 1;
+ len += sizeof(server_unix_addr.sun_family);
+
+ if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ perror("socket");
+ return;
+ }
+
+ for (i = 1; i < 3 && ret < 0; i++)
+ {
+ ret = connect(sockfd, (struct sockaddr *)&server_unix_addr, len);
+ if (ret < 0)
+ {
+ perror("sender: connect");
+ sleep(1);
+ }
+ }
+ if (ret >= 0)
+ {
+ /* Send file descriptor message */
+ if ((fd_send_using_sockfd(fd, sockfd) < 0))
+ fprintf(stderr, "sender: failed to send file descriptor (fd = %d)\n", fd);
+ } else
+ {
+ perror("sender: connect");
+ }
+ sleep(1);
+
+ close(sockfd);
+}
+
+
+int fd_receive(int sockfd, pid_t from)
+{
+ int fd = -1;
+ int status;
+ fd_set readfds;
+ struct timeval timeout;
+
+ /* Wait until a connection is established */
+ listen(sockfd, 0);
+
+ for (;;)
+ {
+ struct sockaddr_un cl_un;
+ socklen_t cl_un_len = sizeof(cl_un);
+
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ if (select(sockfd + 1, &readfds, NULL, NULL, &timeout) >= 0 && FD_ISSET(sockfd, &readfds))
+ {
+ int csockfd = accept(sockfd, (struct sockaddr *)&cl_un, &cl_un_len);
+
+ if (csockfd < 0)
+ {
+ perror("accept");
+ }
+ else
+ {
+ int ret = fd_receive_using_sockfd(&fd, csockfd);
+ if (ret < 0)
+ fprintf(stderr, "receiver: failed to receive file descriptor\n");
+ break;
+ }
+ }
+
+ if (waitpid(from, &status, WNOHANG) > 0 && WIFEXITED(status))
+ {
+ fprintf(stderr, "accept: child process died\n");
+ break;
+ }
+ }
+
+ return fd;
+}
+
+#endif /* __APPLE__ */
diff --git a/src/ethernet/fd_trans.h b/src/ethernet/fd_trans.h
new file mode 100644
index 00000000..7253f09b
--- /dev/null
+++ b/src/ethernet/fd_trans.h
@@ -0,0 +1,20 @@
+/*
+ * fd_trans.h
+ * Used to transfer a file descriptor between two Unix processes
+ *
+ * Inspired by Amit Singhs description in the "Mac OS X manual":
+ * http://topiks.org/mac-os-x/0321278542/ch09lev1sec11.html
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int fd_receive(int sockfd, pid_t from);
+void fd_send(int fd);
+int fd_setup_server(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/ethernet/nf_ethernet.c b/src/ethernet/nf_ethernet.c
new file mode 100644
index 00000000..1b3be922
--- /dev/null
+++ b/src/ethernet/nf_ethernet.c
@@ -0,0 +1,606 @@
+/*
+ * nf_ethernet.c - Ethernet Card Emulation
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "m68000.h"
+#include "configuration.h"
+
+#include "nf_ethernet.h"
+#include "ethernet_nfapi.h"
+#include "ethernet.h"
+
+/****************************
+ * Configuration zone begins
+ */
+
+
+/* Ethernet runs at interrupt level 3 by default but can be reconfigured */
+#if 1
+#define INTLEVEL 3
+#define TRIGGER_INTERRUPT M68000_Exception(24 + INTLEVEL, M68000_EXC_SRC_AUTOVEC)
+#else
+#define INTLEVEL 5
+#define TRIGGER_INTERRUPT M68000_Exception(24 + INTLEVEL, M68000_EXC_SRC_AUTOVEC)
+#endif
+
+/*
+ * Configuration zone ends
+ **************************/
+
+typedef enum { HOST_IP, ATARI_IP, NETMASK } GET_PAR;
+
+static volatile int pending_interrupts;
+
+static struct EthernetHandler *handlers[MAX_ETH];
+
+
+static uint32_t read_stack_long(uint32_t * stack)
+{
+ uint32_t value = STMemory_ReadLong(*stack);
+
+ *stack += SIZE_LONG;
+
+ return value;
+}
+
+
+static void *read_stack_pointer(uint32_t * stack)
+{
+ uint32_t ptr = read_stack_long(stack);
+
+ return ptr ? STMemory_STAddrToPointer(ptr) : 0;
+}
+
+
+static struct EthernetHandler *getHandler(int ethX)
+{
+ if (ethX >= 0 && ethX < MAX_ETH)
+ {
+ struct EthernetHandler *h = handlers[ethX];
+
+ if (h != NULL)
+ {
+ assert(h->ethX == ethX);
+ return h;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int get_params(GET_PAR which, uint32_t stack)
+{
+ int ethX;
+ uint32_t st_name_ptr;
+ char *name_ptr;
+ uint32_t name_maxlen;
+ const char *text = NULL;
+
+ ethX = read_stack_long(&stack);
+ st_name_ptr = STMemory_ReadLong(stack);
+ name_ptr = read_stack_pointer(&stack);
+ name_maxlen = read_stack_long(&stack);
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: getPAR(%d) for eth%d to buffer at %x of size %d\n", which, ethX, st_name_ptr, name_maxlen);
+
+ if (ethX < 0 || ethX >= MAX_ETH)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+ return 0;
+ }
+ if (!STMemory_CheckAreaType(st_name_ptr, name_maxlen, ABFLAG_RAM))
+ {
+ Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_name_ptr, name_maxlen);
+ return 0;
+ }
+
+ switch (which)
+ {
+ case HOST_IP:
+ text = ConfigureParams.Ethernet[ethX].ip_host;
+ break;
+ case ATARI_IP:
+ text = ConfigureParams.Ethernet[ethX].ip_atari;
+ break;
+ case NETMASK:
+ text = ConfigureParams.Ethernet[ethX].netmask;
+ break;
+ default:
+ text = "";
+ break;
+ }
+
+ strncpy(name_ptr, text, name_maxlen);
+ return strlen(text);
+}
+
+
+static int32_t readPacketLength(int ethX)
+{
+ struct EthernetHandler *handler = getHandler(ethX);
+
+ if (handler == NULL)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+ return 0;
+ }
+ return handler->packet_length;
+}
+
+
+/*
+ * ETHERNETDriver ReadPacket routine
+ */
+
+static void readPacket(int ethX, unsigned char *buffer, uint32_t len)
+{
+ struct EthernetHandler *handler = getHandler(ethX);
+
+ if (handler == NULL)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+ return;
+ }
+ if (len > MAX_PACKET_SIZE)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: readPacket() - length %d > %d\n", len, MAX_PACKET_SIZE);
+ len = MAX_PACKET_SIZE;
+ }
+ memcpy(buffer, handler->packet, len);
+}
+
+
+/*
+ * ETHERNETDriver writePacket routine
+ */
+
+static void sendPacket(int ethX, const unsigned char *buffer, uint32_t len)
+{
+ struct EthernetHandler *handler = getHandler(ethX);
+ uint8_t packetToWrite[MAX_PACKET_SIZE + 2];
+
+ if (handler == NULL)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+ return;
+ }
+
+ if (len > MAX_PACKET_SIZE)
+ len = MAX_PACKET_SIZE;
+ memcpy(packetToWrite, buffer, len);
+
+ /* Transmit packet */
+ if (handler->send(handler, packetToWrite, len) < 0)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: WARNING: Couldn't transmit packet\n");
+ }
+}
+
+
+/*
+ * Packet reception thread
+ */
+static int receiveFunc(void *arg)
+{
+ struct EthernetHandler *handler = (struct EthernetHandler *) arg;
+
+ /* Call protocol handler for received packets */
+ for (;;)
+ {
+ /* Read packet device */
+ handler->packet_length = handler->recv(handler, handler->packet, MAX_PACKET_SIZE);
+
+ /* Trigger ETHERNETDriver interrupt (call the m68k side) */
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: packet received (len %d), triggering ETHERNETDriver interrupt\n", handler->ethX, (int) handler->packet_length);
+
+ pending_interrupts |= (1 << handler->ethX);
+ TRIGGER_INTERRUPT;
+
+ /* Wait for interrupt acknowledge (m68k network driver read interrupt to finish) */
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: waiting for int acknowledge with pending irq mask %02x\n", handler->ethX, pending_interrupts);
+ SDL_SemWait(handler->intAck);
+ pending_interrupts &= ~(1 << handler->ethX);
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: int acknowledged, pending irq mask now %02x\n", handler->ethX, pending_interrupts);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Stop packet reception thread
+ */
+static void stopThread(struct EthernetHandler *handler)
+{
+ if (handler && handler->handlingThread)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: Stop thread\n", handler->ethX);
+
+#ifdef FIXME
+ /* pthread_cancel(handlingThread); // FIXME: set the cancel flag. */
+ SDL_WaitThread(handler->handlingThread, NULL);
+ SDL_DestroySemaphore(handler->intAck);
+#endif
+ handler->handlingThread = NULL;
+ }
+}
+
+
+/*
+ * Start packet reception thread
+ */
+static bool startThread(struct EthernetHandler *handler)
+{
+ if (handler->handlingThread == NULL)
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: Start thread\n", handler->ethX);
+
+ if ((handler->intAck = SDL_CreateSemaphore(0)) == NULL)
+ {
+ Log_Printf(LOG_WARN, "Ethernet:%d: WARNING: Cannot init semaphore\n", handler->ethX);
+ return false;
+ }
+
+ handler->handlingThread = SDL_CreateThread(receiveFunc, "Ethernet", handler);
+ if (handler->handlingThread == NULL)
+ {
+ Log_Printf(LOG_WARN, "Ethernet:%d: WARNING: Cannot start ETHERNETDriver thread\n", handler->ethX);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/*
+ * Dispatcher for ETHERNET native features.
+ * Note that in ARAnyM, all of the functions below
+ * are only allowed to called from SuperVisor mode.
+ * This works because they are only issued from inside the kernel,
+ * through ioctl() on the device descriptor.
+ * In Hatari, to simplify things a bit, we allow a few query options
+ * to also be called from User mode.
+ */
+bool nf_ethernet(uint32_t stack, uint32_t subid, uint32_t *retval)
+{
+ int ethX;
+ struct EthernetHandler *handler;
+ bool super = ((M68000_GetSR() & SR_SUPERMODE) == SR_SUPERMODE);
+
+ *retval = 0;
+ switch (subid)
+ {
+ case GET_VERSION:
+ *retval = ARAETHER_NFAPI_VERSION;
+ break;
+
+ case XIF_INTLEVEL: /* what interrupt level is used? */
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: getINTlevel\n");
+ *retval = INTLEVEL;
+ break;
+
+ case XIF_GET_MAC: /* what is the MAC address? */
+ /* store MAC address to provided buffer */
+ {
+ uint32_t st_buf_ptr;
+ unsigned char *buf_ptr;
+ uint32_t buf_size;
+ char *ms;
+ bool format_OK;
+
+ /* default MAC Address is just made up */
+ uint8_t mac_addr[6] = { '\0', 'A', 'E', 'T', 'H', 0 };
+
+ ethX = read_stack_long(&stack);
+
+ handler = getHandler(ethX);
+
+ if (handler == NULL)
+ {
+ *retval = 0; /* return FALSE if ethX not defined */
+ return true;
+ }
+
+ st_buf_ptr = STMemory_ReadLong(stack);
+ buf_ptr = read_stack_pointer(&stack); /* destination buffer */
+ buf_size = read_stack_long(&stack); /* buffer size */
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: getMAC(%d, %x, %d)\n", ethX, st_buf_ptr, (int) buf_size);
+
+ /* default MAC Address is just made up */
+ mac_addr[5] = '0' + ethX;
+
+ /* convert user-defined MAC Address from string to 6 bytes array */
+ ms = ConfigureParams.Ethernet[ethX].mac_addr;
+ format_OK = false;
+ if (strlen(ms) == 2 * 6 + 5 && (ms[2] == ':' || ms[2] == '-'))
+ {
+ int md[6] = { 0, 0, 0, 0, 0, 0 };
+ int matched;
+ int i;
+
+ ms[2] = ms[5] = ms[8] = ms[11] = ms[14] = ':';
+ matched = sscanf(ms, "%02x:%02x:%02x:%02x:%02x:%02x", &md[0], &md[1], &md[2], &md[3], &md[4], &md[5]);
+ if (matched == 6)
+ {
+ for (i = 0; i < 6; i++)
+ mac_addr[i] = md[i];
+ format_OK = true;
+ }
+ }
+ if (!format_OK)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: MAC Address of [ETH%d] in incorrect format\n", ethX);
+ }
+ if (buf_size > sizeof(mac_addr))
+ buf_size = sizeof(mac_addr);
+ if (!STMemory_CheckAreaType(st_buf_ptr, buf_size, ABFLAG_RAM))
+ {
+ Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_buf_ptr, buf_size);
+ *retval = 0;
+ M68000_BusError(st_buf_ptr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA, 0);
+ return false;
+ } else
+ {
+ memcpy(buf_ptr, mac_addr, buf_size);
+ *retval = 1; /* TRUE */
+ }
+ }
+ break;
+
+ case XIF_IRQ: /* interrupt raised by native side thread polling tap0 interface */
+ if (!super)
+ {
+ LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+ M68000_Exception(8, M68000_EXC_SRC_CPU);
+ return false;
+ }
+ {
+ int dev_bit = read_stack_long(&stack);
+
+ if (dev_bit == 0)
+ {
+ /* dev_bit = 0 means "tell me what devices want me to serve their interrupts" */
+ *retval = pending_interrupts;
+ } else
+ {
+ /* otherwise the set bit means "I'm acknowledging this device's interrupt" */
+ ethX = -1;
+
+ switch (dev_bit)
+ {
+ case 0x01:
+ ethX = 0;
+ break;
+ case 0x02:
+ ethX = 1;
+ break;
+ case 0x04:
+ ethX = 2;
+ break;
+ case 0x08:
+ ethX = 3;
+ break;
+ case 0x10:
+ ethX = 4;
+ break;
+ case 0x20:
+ ethX = 5;
+ break;
+ case 0x40:
+ ethX = 6;
+ break;
+ case 0x80:
+ ethX = 7;
+ break;
+ default:
+ LOG_TRACE(LOG_WARN, "Ethernet: wrong XIF_IRQ(%d)\n", dev_bit);
+ break;
+ }
+
+ handler = getHandler(ethX);
+
+ if (handler == NULL)
+ {
+ LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+ } else
+ {
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: ETH%d IRQ acknowledged\n", ethX);
+ /* Acknowledge interrupt to reception thread */
+ SDL_SemPost(handler->intAck);
+ }
+ }
+ }
+ break;
+
+ case XIF_START:
+ if (!super)
+ {
+ LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+ M68000_Exception(8, M68000_EXC_SRC_CPU);
+ return false;
+ }
+ ethX = read_stack_long(&stack);
+ handler = getHandler(ethX);
+ if (handler == NULL || startThread(handler) == false)
+ {
+ *retval = GEMDOS_EUNDEV;
+ }
+ break;
+
+ case XIF_STOP:
+ if (!super)
+ {
+ LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+ M68000_Exception(8, M68000_EXC_SRC_CPU);
+ return false;
+ }
+ ethX = read_stack_long(&stack);
+ stopThread(getHandler(ethX));
+ break;
+
+ case XIF_READLENGTH:
+ if (!super)
+ {
+ LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+ M68000_Exception(8, M68000_EXC_SRC_CPU);
+ return false;
+ }
+ ethX = read_stack_long(&stack);
+ *retval = readPacketLength(ethX);
+ break;
+
+ case XIF_READBLOCK:
+ if (!super)
+ {
+ LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+ M68000_Exception(8, M68000_EXC_SRC_CPU);
+ return false;
+ }
+ {
+ uint32_t st_buf_ptr;
+ unsigned char *buf_ptr;
+ uint32_t buf_size;
+
+ ethX = read_stack_long(&stack);
+ st_buf_ptr = STMemory_ReadLong(stack);
+ buf_ptr = read_stack_pointer(&stack); /* destination buffer */
+ buf_size = read_stack_long(&stack); /* buffer size */
+ if (!STMemory_CheckAreaType(st_buf_ptr, buf_size, ABFLAG_RAM))
+ {
+ Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_buf_ptr, buf_size);
+ M68000_BusError(st_buf_ptr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA, 0);
+ return false;
+ }
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: ReadPacket dest %08x, len %x\n", st_buf_ptr, buf_size);
+ readPacket(ethX, buf_ptr, buf_size);
+ }
+ break;
+
+ case XIF_WRITEBLOCK:
+ if (!super)
+ {
+ LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+ M68000_Exception(8, M68000_EXC_SRC_CPU);
+ return false;
+ }
+ {
+ uint32_t st_buf_ptr;
+ unsigned char *buf_ptr;
+ uint32_t buf_size;
+
+ ethX = read_stack_long(&stack);
+ st_buf_ptr = STMemory_ReadLong(stack);
+ buf_ptr = read_stack_pointer(&stack); /* destination buffer */
+ buf_size = read_stack_long(&stack); /* buffer size */
+ if (!STMemory_CheckAreaType(st_buf_ptr, buf_size, ABFLAG_RAM | ABFLAG_ROM))
+ {
+ Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_buf_ptr, buf_size);
+ M68000_BusError(st_buf_ptr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA, 0);
+ return false;
+ }
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: SendPacket src %08x, len %x\n", st_buf_ptr, buf_size);
+ sendPacket(ethX, buf_ptr, buf_size);
+ }
+ break;
+
+ case XIF_GET_IPHOST:
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: XIF_GET_IPHOST\n");
+ *retval = get_params(HOST_IP, stack);
+ break;
+
+ case XIF_GET_IPATARI:
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: XIF_GET_IPATARI\n");
+ *retval = get_params(ATARI_IP, stack);
+ break;
+
+ case XIF_GET_NETMASK:
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: XIF_GET_NETMASK\n");
+ *retval = get_params(NETMASK, stack);
+ break;
+
+ default:
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: unsupported function %d\n", subid);
+ *retval = GEMDOS_EINVFN;
+ break;
+ }
+
+ return true;
+}
+
+
+/*
+ * Initialization
+ */
+void nf_ethernet_init(void)
+{
+ struct EthernetHandler *handler;
+ int i;
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: init\n");
+
+ pending_interrupts = 0;
+ for (i = 0; i < MAX_ETH; i++)
+ {
+ handler = ETHERNET_HANDLER_CLASSNAME(i);
+ if (!handler->open(handler))
+ {
+ free(handler);
+ handler = NULL;
+ }
+ handlers[i] = handler;
+ }
+}
+
+
+/*
+ * Deinitialization
+ */
+void nf_ethernet_reset(void)
+{
+ int i;
+
+ LOG_TRACE(TRACE_ETHERNET, "Ethernet: reset\n");
+
+ for (i = 0; i < MAX_ETH; i++)
+ {
+ /* Stop reception thread */
+ struct EthernetHandler *handler = handlers[i];
+
+ if (handler)
+ {
+ stopThread(handler);
+ handler->close(handler);
+ free(handler);
+ handlers[i] = NULL;
+ }
+ }
+ pending_interrupts = 0;
+}
diff --git a/src/includes/configuration.h b/src/includes/configuration.h
index a400e2c8..4a47e340 100644
--- a/src/includes/configuration.h
+++ b/src/includes/configuration.h
@@ -331,6 +331,17 @@ typedef struct
char sMidiOutPortName[FILENAME_MAX];
} CNF_MIDI;
+/* ethernet support */
+#define MAX_ETH 4
+typedef struct {
+ char type[32];
+ char tunnel[16];
+ char ip_host[16];
+ char ip_atari[16];
+ char netmask[16];
+ char mac_addr[18];
+} CNF_ETHERNET;
+
/* Dialog System */
typedef enum
@@ -427,6 +438,7 @@ typedef struct
CNF_MIDI Midi;
CNF_SYSTEM System;
CNF_VIDEO Video;
+ CNF_ETHERNET Ethernet[MAX_ETH];
} CNF_PARAMS;
diff --git a/src/includes/nf_ethernet.h b/src/includes/nf_ethernet.h
new file mode 100644
index 00000000..c948b897
--- /dev/null
+++ b/src/includes/nf_ethernet.h
@@ -0,0 +1,36 @@
+/*
+ * nf_ethernet.h - Ethernet Card Emulation
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef HATARI_ETHERNET_H
+#define HATARI_ETHERNET_H
+
+#if defined(__linux__) || defined(__CYGWIN32__) || defined(__WIN32__) || defined(__APPLE__)
+
+#define WITH_NF_ETHERNET 1
+
+bool nf_ethernet(Uint32 stack, Uint32 subid, Uint32 *retval);
+void nf_ethernet_init(void);
+void nf_ethernet_reset(void);
+
+#endif
+
+#endif /* HATARI_ETHERNET_H */
diff --git a/src/reset.c b/src/reset.c
index 3f668be5..9b548069 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -42,6 +42,7 @@ const char Reset_fileid[] = "Hatari reset.c";
#include "debugcpu.h"
#include "debugdsp.h"
#include "nf_scsidrv.h"
+#include "nf_ethernet.h"
/*-----------------------------------------------------------------------*/
/**
@@ -116,7 +117,12 @@ static int Reset_ST(bool bCold)
Midi_Reset();
#if defined(__linux__)
- nf_scsidrv_reset();
+ nf_scsidrv_reset();
+#endif
+
+#ifdef WITH_NF_ETHERNET
+ nf_ethernet_reset();
+ nf_ethernet_init();
#endif
/* Start HBL, Timer B and VBL interrupts with a 0 cycle delay */
| Mail converted by MHonArc 2.6.19+ | http://listengine.tuxfamily.org/ |