From fb4dacd6a64c0a8d21a592b3c5b6e39c8cc06f1a Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Mon, 6 Dec 2004 23:31:03 +0000 Subject: [PATCH] Raw import of ether_windows.cpp from original Basilisk II for Windows. Fix code so that it builds in Cygwin/Mingw32. --- BasiliskII/src/Windows/ether_windows.cpp | 1092 ++++++++++++++++++++++ 1 file changed, 1092 insertions(+) create mode 100755 BasiliskII/src/Windows/ether_windows.cpp diff --git a/BasiliskII/src/Windows/ether_windows.cpp b/BasiliskII/src/Windows/ether_windows.cpp new file mode 100755 index 00000000..03c15717 --- /dev/null +++ b/BasiliskII/src/Windows/ether_windows.cpp @@ -0,0 +1,1092 @@ +/* + * 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