--- BasiliskII/src/Windows/ether_windows.cpp 2004/12/06 23:31:03 1.1 +++ BasiliskII/src/Windows/ether_windows.cpp 2008/01/01 09:40:33 1.9 @@ -1,1092 +1,1628 @@ -/* - * ether_windows.cpp - Ethernet device driver - * - * Basilisk II (C) 1997-2004 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" - - -// 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; - - -// Need to fake a NIC if there is none but the router module is activated. -bool ether_fake = false; - -// These are protected by queue_csection -// Controls transfer for read thread to feed thread -static CRITICAL_SECTION queue_csection; -typedef struct _queue_t { - uint8 *buf; - int sz; -} queue_t; -#define MAX_QUEUE_ITEMS 1024 -static 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; - -static char edevice[512]; - - -// 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); - - -/* - * 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]; - - // Initialize NAT-Router - router_init(); - - // Do nothing if no Ethernet device specified - const char *name = PrefsFindString("ether"); - if (name) - strcpy(edevice, name); - - bool there_is_a_router = PrefsFindBool("routerenabled"); - - if (!name || !*name) { - if( there_is_a_router ) { - strcpy( edevice, "None" ); - ether_fake = true; - } else { - return false; - } - } - - ether_use_permanent = PrefsFindBool("etherpermanentaddress"); - ether_multi_mode = PrefsFindInt32("ethermulticastmode"); - - // Open ethernet device - if(ether_fake) { - memcpy( ether_addr, router_mac_addr, 6 ); - D(bug("Fake ethernet address (same as router) %02x %02x %02x %02x %02x %02x\r\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); - } else { - fd = PacketOpenAdapter( name, ether_multi_mode ); - if (!fd) { - sprintf(str, "Could not open ethernet adapter %s.", 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.", name); - WarningAlert(str); - goto open_error; - } - D(bug("Real ethernet address %02x %02x %02x %02x %02x %02x\r\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\r\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\r\n")); - goto open_error; - } - thread_active = true; -#if 0 - SetThreadPriority( ether_th, threads[THREAD_ETHER].priority_running ); - SetThreadAffinityMask( ether_th, threads[THREAD_ETHER].affinity_mask ); -#endif - - unsigned int dummy; - ether_th2 = (HANDLE)_beginthreadex( 0, 0, ether_thread_get_packets_nt, 0, 0, &dummy ); -#if 0 - SetThreadPriority( ether_th2, threads[THREAD_ETHER].priority_running ); - SetThreadAffinityMask( ether_th2, threads[THREAD_ETHER].affinity_mask ); -#endif - - ether_th1 = (HANDLE)_beginthreadex( 0, 0, ether_thread_write_packets, 0, 0, &dummy ); -#if 0 - SetThreadPriority( ether_th1, threads[THREAD_ETHER].priority_running ); - SetThreadAffinityMask( ether_th1, threads[THREAD_ETHER].affinity_mask ); -#endif - - // 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(!ether_fake) { - PacketCloseAdapter(fd); - } - fd = 0; - return false; -} - - -/* - * Deinitialization - */ - -void ether_exit(void) -{ - D(bug("EtherExit\r\n")); - - // Take them down in a controlled way. - thread_active = false; - - // _asm int 3 - - D(bug("Closing ethernet device %s\r\n",edevice)); - - if(!*edevice) return; - - 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\r\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\r\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\r\n")); - if(ether_th1) TerminateThread(ether_th1,0); - thread_active_1 = false; - } - if(thread_active_2) { - D(bug("Ether killing ether_th2\r\n")); - if(ether_th2) TerminateThread(ether_th2,0); - thread_active_2 = false; - } - if(thread_active_3) { - D(bug("Ether killing thread\r\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\r\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\r\n")); - NetProtocol *p = prot_list; - while (p) { - NetProtocol *next = p->next; - delete p; - p = next; - } - prot_list = 0; - - D(bug("Deleting sections\r\n")); - DeleteCriticalSection( &fetch_csection ); - DeleteCriticalSection( &queue_csection ); - DeleteCriticalSection( &send_csection ); - DeleteCriticalSection( &wpool_csection ); - - D(bug("Freeing read packets\r\n")); - free_read_packets(); - - D(bug("Freeing write packets\r\n")); - free_write_packets(); - - D(bug("Finalizing queue\r\n")); - final_queue(); - - D(bug("Stopping router\r\n")); - router_final(); - - D(bug("EtherExit done\r\n")); -} - - -/* - * Reset - */ - -void ether_reset(void) -{ - D(bug("EtherReset\r\n")); - - // Remove all protocols - NetProtocol *p = prot_list; - while (p) { - NetProtocol *next = p->next; - delete p; - p = next; - } - prot_list = NULL; -} - - -/* - * Add multicast address - */ - -int16 ether_add_multicast(uint32 pb) -{ - D(bug("ether_add_multicast\r\n")); - - // We wouldn't need to do this - // if(ether_multi_mode != ETHER_MULTICAST_MAC) return noErr; - - if (!ether_fake && !PacketAddMulticast( fd, Mac2HostAddr(pb + eMultiAddr))) { - D(bug("WARNING: couldn't enable multicast address\r\n")); - return eMultiErr; - } else { - D(bug("ether_add_multicast: noErr\r\n")); - return noErr; - } -} - - -/* - * Delete multicast address - */ - -int16 ether_del_multicast(uint32 pb) -{ - D(bug("ether_del_multicast\r\n")); - - // We wouldn't need to do this - // if(ether_multi_mode != ETHER_MULTICAST_MAC) return noErr; - - if (!ether_fake && !PacketDelMulticast( fd, Mac2HostAddr(pb + eMultiAddr))) { - D(bug("WARNING: couldn't disable multicast address\r\n")); - return eMultiErr; - } else - return noErr; -} - - -/* - * Attach protocol handler - */ - -int16 ether_attach_ph(uint16 type, uint32 handler) -{ - D(bug("ether_attach_ph type=0x%x, handler=0x%x\r\n",(int)type,handler)); - - // Already attached? - NetProtocol *p = find_protocol(type); - if (p != NULL) { - D(bug("ether_attach_ph: lapProtErr\r\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\r\n")); - return noErr; - } -} - - -/* - * Detach protocol handler - */ - -int16 ether_detach_ph(uint16 type) -{ - D(bug("ether_detach_ph type=%08lx\r\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\r\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\r\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\r\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\r\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 ) { - if(m_router_enabled && router_write_packet((uint8 *)Packet->Buffer, Packet->Length)) { - Packet->bIoComplete = TRUE; - recycle_write_packet(Packet); - } else if(ether_fake) { - Packet->bIoComplete = TRUE; - recycle_write_packet(Packet); - } else if(!PacketSendPacket( fd, Packet, FALSE, TRUE )) { - // already recycled if async - } - } - } - - D(bug("ether_thread_write_packets exit\r\n")); - - thread_active_1 = false; - - return(0); -} - -static BOOL write_packet( uint8 *packet, int len ) -{ - LPPACKET Packet; - - D(bug("write_packet\r\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); - } -} - -int16 ether_write(uint32 wds) -{ - D(bug("ether_write\r\n")); - - // Set source address - uint32 hdr = ReadMacInt32(wds + 2); - memcpy(Mac2HostAddr(hdr + 6), ether_addr, 6); - - // Copy packet to buffer - uint8 packet[1514], *p = packet; - int len = 0; - for (;;) { - uint16 w = (uint16)ReadMacInt16(wds); - if (w == 0) - break; - memcpy(p, Mac2HostAddr(ReadMacInt32(wds + 2)), w); - len += w; - p += w; - wds += 6; - } - - if(len > 1514) { - D(bug("illegal packet length: %d\r\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\r\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\r\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\r\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.\r\n")); - dwNumberOfBytesTransfered = 0; - - j = (j+1) & (~(MAX_ECHO-1)); - if(j != echo_count) { - D(bug("Wow, this fix made some good after all...\r\n")); - } - - break; - } - } - if(dwNumberOfBytesTransfered) { - if(!m_router_enabled || !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...\r\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\r\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\r\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\r\n")); - - while(thread_active) { - D(bug("Waiting for int_sig\r\n")); - WaitForSingleObject(int_sig,INFINITE); - // Looping this way to avoid a race condition. - D(bug("Triggering\r\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.\r\n")); - } - - D(bug("ether_thread_feed_int exit\r\n")); - - thread_active_3 = false; - - return 0; -} - - -/* - * Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers - */ - -void EtherInterrupt(void) -{ - int length; - static uint8 packet[1514]; - - D(bug("EtherIRQ\r\n")); - - // Call protocol handler for received packets - while( (length = dequeue_packet(packet)) > 0 ) { - - if (length < 14) - continue; - -#if MONITOR - bug("Receiving Ethernet packet (%d bytes):\n",(int)length); - dump_packet( packet, length ); -#endif - - // Get packet type - uint16 type = ntohs(*(uint16 *)(packet + 12)); - - // Look for protocol - NetProtocol *prot = find_protocol(type); - if (prot == NULL) - continue; - // break; - - // No default handler - if (prot->handler == 0) - continue; - // break; - - // Copy header to RHA - memcpy(Mac2HostAddr(ether_data + ed_RHA), packet, 14); - D(bug(" header %08lx%04lx %08lx%04lx %04lx\r\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] = (uint32)packet + 14; // Pointer to packet (host 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\r\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4])); - Execute68k(prot->handler, &r); - } - - // Acknowledge interrupt to reception thread - D(bug(" EtherIRQ done\r\n")); - ReleaseSemaphore(int_ack,1,NULL); -} - -#if DEBUG -#pragma optimize("",on) -#endif +/* + * ether_windows.cpp - Ethernet device driver + * + * Basilisk II (C) 1997-2008 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 "sysdeps.h" + +#include +#include +#include +#include + +#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_TAP, + NET_IF_FAKE, +}; + +// TAP-Win32 constants +#define TAP_VERSION_MIN_MAJOR 7 +#define TAP_VERSION_MIN_MINOR 1 + +#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) + +#define OLD_TAP_CONTROL_CODE(request, method) \ + CTL_CODE (FILE_DEVICE_PHYSICAL_NETCARD | 8000, request, method, FILE_ANY_ACCESS) + +#define OLD_TAP_IOCTL_GET_VERSION OLD_TAP_CONTROL_CODE (3, METHOD_BUFFERED) + +// 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 LPADAPTER tap_open_adapter(const char *dev_name); +static void tap_close_adapter(LPADAPTER fd); +static bool tap_check_version(LPADAPTER fd); +static bool tap_set_status(LPADAPTER fd, ULONG status); +static bool tap_get_mac(LPADAPTER fd, LPBYTE addr); +static bool tap_receive_packet(LPADAPTER fd, LPPACKET lpPacket, BOOLEAN Sync); +static bool tap_send_packet(LPADAPTER fd, LPPACKET lpPacket, BOOLEAN Sync, BOOLEAN recycle); +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 (PrefsFindBool("routerenabled") || strcmp(name, "router") == 0) + net_if_type = NET_IF_ROUTER; + else if (strcmp(name, "slirp") == 0) + net_if_type = NET_IF_SLIRP; + else if (strcmp(name, "tap") == 0) + net_if_type = NET_IF_TAP; + 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"); + if (dev_name == NULL || strcmp(name, "b2ether") != 0) + dev_name = name; + break; + case NET_IF_TAP: + 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_TAP) { + if (dev_name == NULL) { + WarningAlert("No ethernet device GUID specified. Ethernet is not available."); + goto open_error; + } + + fd = tap_open_adapter(dev_name); + if (!fd) { + sprintf(str, "Could not open ethernet adapter %s.", dev_name); + WarningAlert(str); + goto open_error; + } + + if (!tap_check_version(fd)) { + sprintf(str, "Minimal TAP-Win32 version supported is %d.%d.", TAP_VERSION_MIN_MAJOR, TAP_VERSION_MIN_MINOR); + WarningAlert(str); + goto open_error; + } + + if (!tap_set_status(fd, true)) { + sprintf(str, "Could not set media status to connected."); + WarningAlert(str); + goto open_error; + } + + if (!tap_get_mac(fd, ether_addr)) { + 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); + } + } +#if 1 + /* + If we bridge the underlying ethernet connection and the TAP + device altogether, we have to use a fake address. + */ + else { + ether_addr[0] = 0x52; + ether_addr[1] = 0x54; + ether_addr[2] = 0x00; + } +#endif + 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; + unsigned int (WINAPI *receive_func)(void *); + switch (net_if_type) { + case NET_IF_SLIRP: + receive_func = slirp_receive_func; + break; + default: + receive_func = ether_thread_get_packets_nt; + break; + } + ether_th2 = (HANDLE)_beginthreadex( 0, 0, receive_func, 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 (fd) { + switch (net_if_type) { + case NET_IF_B2ETHER: + PacketCloseAdapter(fd); + break; + case NET_IF_TAP: + tap_close_adapter(fd); + break; + } + 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) { + switch (net_if_type) { + case NET_IF_B2ETHER: + PacketCloseAdapter(fd); + break; + case NET_IF_TAP: + tap_close_adapter(fd); + break; + } + 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_TAP: + if (!tap_send_packet(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); +} + + +/* + * TAP-Win32 glue + */ + +static LPADAPTER tap_open_adapter(const char *dev_name) +{ + fd = (LPADAPTER)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(*fd)); + if (fd == NULL) + return NULL; + + char dev_path[MAX_PATH]; + snprintf(dev_path, sizeof(dev_path), + "\\\\.\\Global\\%s.tap", dev_name); + + HANDLE handle = CreateFile( + dev_path, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + NULL); + if (handle == NULL || handle == INVALID_HANDLE_VALUE) + return NULL; + + fd->hFile = handle; + return fd; +} + +static void tap_close_adapter(LPADAPTER fd) +{ + if (fd) { + if (fd->hFile) { + tap_set_status(fd, false); + CloseHandle(fd->hFile); + } + GlobalFreePtr(fd); + } +} + +static bool tap_check_version(LPADAPTER fd) +{ + ULONG len; + ULONG info[3] = { 0, }; + + if (!DeviceIoControl(fd->hFile, TAP_IOCTL_GET_VERSION, + &info, sizeof(info), + &info, sizeof(info), &len, NULL) + && !DeviceIoControl(fd->hFile, OLD_TAP_IOCTL_GET_VERSION, + &info, sizeof(info), + &info, sizeof(info), &len, NULL)) + return false; + + if (info[0] > TAP_VERSION_MIN_MAJOR) + return true; + if (info[0] == TAP_VERSION_MIN_MAJOR && info[1] >= TAP_VERSION_MIN_MINOR) + return true; + + return false; +} + +static bool tap_set_status(LPADAPTER fd, ULONG status) +{ + DWORD len = 0; + return DeviceIoControl(fd->hFile, TAP_IOCTL_SET_MEDIA_STATUS, + &status, sizeof (status), + &status, sizeof (status), &len, NULL); +} + +static bool tap_get_mac(LPADAPTER fd, LPBYTE addr) +{ + DWORD len = 0; + return DeviceIoControl(fd->hFile, TAP_IOCTL_GET_MAC, + addr, 6, + addr, 6, &len, NULL); + +} + +static VOID CALLBACK tap_write_completion( + DWORD dwErrorCode, + DWORD dwNumberOfBytesTransfered, + LPOVERLAPPED lpOverLapped + ) +{ + LPPACKET lpPacket = CONTAINING_RECORD(lpOverLapped, PACKET, OverLapped); + + lpPacket->bIoComplete = TRUE; + recycle_write_packet(lpPacket); +} + +static bool tap_send_packet( + LPADAPTER fd, + LPPACKET lpPacket, + BOOLEAN Sync, + BOOLEAN RecyclingAllowed) +{ + BOOLEAN Result; + + lpPacket->OverLapped.Offset = 0; + lpPacket->OverLapped.OffsetHigh = 0; + lpPacket->bIoComplete = FALSE; + + if (Sync) { + Result = WriteFile(fd->hFile, + lpPacket->Buffer, + lpPacket->Length, + &lpPacket->BytesReceived, + &lpPacket->OverLapped); + if (Result) { + GetOverlappedResult(fd->hFile, + &lpPacket->OverLapped, + &lpPacket->BytesReceived, + TRUE); + } + lpPacket->bIoComplete = TRUE; + if (RecyclingAllowed) + PacketFreePacket(lpPacket); + } + else { + Result = WriteFileEx(fd->hFile, + lpPacket->Buffer, + lpPacket->Length, + &lpPacket->OverLapped, + tap_write_completion); + + if (!Result && RecyclingAllowed) + recycle_write_packet(lpPacket); + } + + return Result; +} + +static bool tap_receive_packet(LPADAPTER fd, LPPACKET lpPacket, BOOLEAN Sync) +{ + BOOLEAN Result; + + lpPacket->OverLapped.Offset = 0; + lpPacket->OverLapped.OffsetHigh = 0; + lpPacket->bIoComplete = FALSE; + + if (Sync) { + Result = ReadFile(fd->hFile, + lpPacket->Buffer, + lpPacket->Length, + &lpPacket->BytesReceived, + &lpPacket->OverLapped); + if (Result) { + Result = GetOverlappedResult(fd->hFile, + &lpPacket->OverLapped, + &lpPacket->BytesReceived, + TRUE); + if (Result) + lpPacket->bIoComplete = TRUE; + else + lpPacket->free = TRUE; + } + } + else { + Result = ReadFileEx(fd->hFile, + lpPacket->Buffer, + lpPacket->Length, + &lpPacket->OverLapped, + packet_read_completion); + + if (!Result) + lpPacket->BytesReceived = 0; + } + + 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); +} + +static 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; + } + } + // XXX drop packets that are not for us + if (net_if_type == NET_IF_TAP) { + if (memcmp((LPBYTE)lpPacket->Buffer, ether_addr, 6) != 0) + dwNumberOfBytesTransfered = 0; + } + 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; + BOOLEAN Result; + switch (net_if_type) { + case NET_IF_B2ETHER: + Result = PacketReceivePacket(fd, packets[i], FALSE); + break; + case NET_IF_TAP: + Result = tap_receive_packet(fd, packets[i], FALSE); + break; + } + if (Result) { + 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