diff --git a/SheepShaver/src/EthernetDriver/.finf/Ethernet b/SheepShaver/src/EthernetDriver/.finf/Ethernet new file mode 100644 index 00000000..07b9b6a8 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/Ethernet differ diff --git a/SheepShaver/src/EthernetDriver/.finf/Ethernet Data b/SheepShaver/src/EthernetDriver/.finf/Ethernet Data new file mode 100644 index 00000000..c90c8351 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/Ethernet Data differ diff --git a/SheepShaver/src/EthernetDriver/.finf/Ethernet.cpp b/SheepShaver/src/EthernetDriver/.finf/Ethernet.cpp new file mode 100644 index 00000000..468e347d Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/Ethernet.cpp differ diff --git a/SheepShaver/src/EthernetDriver/.finf/cpu_emulation.h b/SheepShaver/src/EthernetDriver/.finf/cpu_emulation.h new file mode 100644 index 00000000..0f9350b2 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/cpu_emulation.h differ diff --git a/SheepShaver/src/EthernetDriver/.finf/debug.h b/SheepShaver/src/EthernetDriver/.finf/debug.h new file mode 100644 index 00000000..0547fd16 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/debug.h differ diff --git a/SheepShaver/src/EthernetDriver/.finf/ether.cpp b/SheepShaver/src/EthernetDriver/.finf/ether.cpp new file mode 100644 index 00000000..9562b07b Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/ether.cpp differ diff --git a/SheepShaver/src/EthernetDriver/.finf/ether.h b/SheepShaver/src/EthernetDriver/.finf/ether.h new file mode 100644 index 00000000..fe4c1a1e Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/ether.h differ diff --git a/SheepShaver/src/EthernetDriver/.finf/ether_defs.h b/SheepShaver/src/EthernetDriver/.finf/ether_defs.h new file mode 100644 index 00000000..822e5297 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/ether_defs.h differ diff --git a/SheepShaver/src/EthernetDriver/.finf/ethernet.ndrv b/SheepShaver/src/EthernetDriver/.finf/ethernet.ndrv new file mode 100644 index 00000000..f58fce9b Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/ethernet.ndrv differ diff --git a/SheepShaver/src/EthernetDriver/.finf/macos_util.cpp b/SheepShaver/src/EthernetDriver/.finf/macos_util.cpp new file mode 100644 index 00000000..d9cb0d54 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/macos_util.cpp differ diff --git a/SheepShaver/src/EthernetDriver/.finf/macos_util.h b/SheepShaver/src/EthernetDriver/.finf/macos_util.h new file mode 100644 index 00000000..c13d137a Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/macos_util.h differ diff --git a/SheepShaver/src/EthernetDriver/.finf/sysdeps.h b/SheepShaver/src/EthernetDriver/.finf/sysdeps.h new file mode 100644 index 00000000..a6db39b9 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/sysdeps.h differ diff --git a/SheepShaver/src/EthernetDriver/.finf/xlowmem.h b/SheepShaver/src/EthernetDriver/.finf/xlowmem.h new file mode 100644 index 00000000..47ae769e Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.finf/xlowmem.h differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/Ethernet.cpp b/SheepShaver/src/EthernetDriver/.rsrc/Ethernet.cpp new file mode 100644 index 00000000..5e3c49ca Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/Ethernet.cpp differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/cpu_emulation.h b/SheepShaver/src/EthernetDriver/.rsrc/cpu_emulation.h new file mode 100644 index 00000000..ad39a249 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/cpu_emulation.h differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/debug.h b/SheepShaver/src/EthernetDriver/.rsrc/debug.h new file mode 100644 index 00000000..d8022172 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/debug.h differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/ether.cpp b/SheepShaver/src/EthernetDriver/.rsrc/ether.cpp new file mode 100644 index 00000000..6b483286 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/ether.cpp differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/ether.h b/SheepShaver/src/EthernetDriver/.rsrc/ether.h new file mode 100644 index 00000000..510308e1 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/ether.h differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/ether_defs.h b/SheepShaver/src/EthernetDriver/.rsrc/ether_defs.h new file mode 100644 index 00000000..26bec8fb Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/ether_defs.h differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/ethernet.ndrv b/SheepShaver/src/EthernetDriver/.rsrc/ethernet.ndrv new file mode 100644 index 00000000..63653eda Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/ethernet.ndrv differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/macos_util.cpp b/SheepShaver/src/EthernetDriver/.rsrc/macos_util.cpp new file mode 100644 index 00000000..23ff9840 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/macos_util.cpp differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/macos_util.h b/SheepShaver/src/EthernetDriver/.rsrc/macos_util.h new file mode 100644 index 00000000..2f501442 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/macos_util.h differ diff --git a/SheepShaver/src/EthernetDriver/.rsrc/sysdeps.h b/SheepShaver/src/EthernetDriver/.rsrc/sysdeps.h new file mode 100644 index 00000000..4f537acf Binary files /dev/null and b/SheepShaver/src/EthernetDriver/.rsrc/sysdeps.h differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/CWSettingsMacOS.stg b/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/CWSettingsMacOS.stg new file mode 100644 index 00000000..2c25cdbd Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/CWSettingsMacOS.stg differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/PPC Debug MacOS Toolbox b/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/PPC Debug MacOS Toolbox new file mode 100644 index 00000000..4e4e4935 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/PPC Debug MacOS Toolbox differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/PPC Final MacOS Toolbox b/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/PPC Final MacOS Toolbox new file mode 100644 index 00000000..4e4e4935 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/.finf/PPC Final MacOS Toolbox differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/CWSettingsMacOS.stg b/SheepShaver/src/EthernetDriver/Ethernet Data/CWSettingsMacOS.stg new file mode 100644 index 00000000..7848ef0b Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/CWSettingsMacOS.stg differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/.finf/TargetDataMacOS.tdt b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/.finf/TargetDataMacOS.tdt new file mode 100644 index 00000000..2c25cdbd Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/.finf/TargetDataMacOS.tdt differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/.rsrc/TargetDataMacOS.tdt b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/.rsrc/TargetDataMacOS.tdt new file mode 100644 index 00000000..7c9986b9 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/.rsrc/TargetDataMacOS.tdt differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/TargetDataMacOS.tdt b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/TargetDataMacOS.tdt new file mode 100644 index 00000000..48fe5d70 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Debug MacOS Toolbox/TargetDataMacOS.tdt differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/.finf/TargetDataMacOS.tdt b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/.finf/TargetDataMacOS.tdt new file mode 100644 index 00000000..2c25cdbd Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/.finf/TargetDataMacOS.tdt differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/.rsrc/TargetDataMacOS.tdt b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/.rsrc/TargetDataMacOS.tdt new file mode 100644 index 00000000..7c9986b9 Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/.rsrc/TargetDataMacOS.tdt differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/TargetDataMacOS.tdt b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/TargetDataMacOS.tdt new file mode 100644 index 00000000..cdb3ae7c Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet Data/PPC Final MacOS Toolbox/TargetDataMacOS.tdt differ diff --git a/SheepShaver/src/EthernetDriver/Ethernet.cpp b/SheepShaver/src/EthernetDriver/Ethernet.cpp new file mode 100644 index 00000000..2c49fd86 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/Ethernet.cpp @@ -0,0 +1,308 @@ +/* + * Ethernet.cpp - SheepShaver ethernet PCI driver stub + * + * SheepShaver (C) 1997-2005 Christian Bauer and Marc Hellwig + * + * 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 "xlowmem.h" +#include "ether_defs.h" + + +// Macro for tail-calling native code from assembly functions +#define ASM_TAIL_CALL_NATIVE(NAME) \ + lwz r0,XLM_##NAME(r0) ;\ + lwz r2,XLM_TOC(r0) ;\ + mtctr r0 ;\ + bctr + +// Macro for calling native code from assembly functions +#define ASM_CALL_NATIVE(NAME) \ + mflr r0 ;\ + stw r2,12(r1) ;\ + stw r0,8(r1) ;\ + stwu r1,-64(r1) ;\ + lwz r0,XLM_##NAME(r0) ;\ + lwz r2,XLM_TOC(r0) ;\ + mtlr r0 ;\ + blrl ;\ + lwz r0,64+8(r1) ;\ + lwz r2,64+12(r1) ;\ + mtlr r0 ;\ + addi r1,r1,64 ;\ + blr + + +/* + * Driver Description structure + */ + +struct DriverDescription { + uint32 driverDescSignature; + uint32 driverDescVersion; + char nameInfoStr[32]; + uint32 version; + uint32 driverRuntime; + char driverName[32]; + uint32 driverDescReserved[8]; + uint32 nServices; + uint32 serviceCategory; + uint32 serviceType; + uint32 serviceVersion; +}; + +#pragma export on +DriverDescription TheDriverDescription = { + 'mtej', + 0, + "\pSheepShaver Ethernet", + 0x01008000, // V1.0.0final + 4, // kDriverIsUnderExpertControl + "\penet", + 0, 0, 0, 0, 0, 0, 0, 0, + 1, + 'otan', + 0x000a0b01, // Ethernet, Framing: Ethernet/EthernetIPX/802.2, IsDLPI + 0x01000000, // V1.0.0 +}; +#pragma export off + + +/* + * install_info and related structures + */ + +#ifdef BUILD_ETHER_FULL_DRIVER +#define ETHERDECL extern +#else +#define ETHERDECL static +#endif + +ETHERDECL int ether_open(queue_t *rdq, void *dev, int flag, int sflag, void *creds); +ETHERDECL int ether_close(queue_t *rdq, int flag, void *creds); +ETHERDECL int ether_wput(queue_t *q, msgb *mp); +ETHERDECL int ether_wsrv(queue_t *q); +ETHERDECL int ether_rput(queue_t *q, msgb *mp); +ETHERDECL int ether_rsrv(queue_t *q); + +struct ot_module_info { + uint16 mi_idnum; + char *mi_idname; + int32 mi_minpsz; // Minimum packet size + int32 mi_maxpsz; // Maximum packet size + uint32 mi_hiwat; // Queue hi-water mark + uint32 mi_lowat; // Queue lo-water mark +}; + +static ot_module_info module_information = { + kEnetModuleID, + "SheepShaver Ethernet", + 0, + kEnetTSDU, + 6000, + 5000 +}; + +typedef int (*putp_t)(queue_t *, msgb *); +typedef int (*srvp_t)(queue_t *); +typedef int (*openp_t)(queue_t *, void *, int, int, void *); +typedef int (*closep_t)(queue_t *, int, void *); + +struct qinit { + putp_t qi_putp; + srvp_t qi_srvp; + openp_t qi_qopen; + closep_t qi_qclose; + void *qi_qadmin; + struct ot_module_info *qi_minfo; + void *qi_mstat; +}; + +static qinit read_side = { + NULL, + ether_rsrv, + ether_open, + ether_close, + NULL, + &module_information, + NULL +}; + +static qinit write_side = { + ether_wput, + NULL, + ether_open, + ether_close, + NULL, + &module_information, + NULL +}; + +struct streamtab { + struct qinit *st_rdinit; + struct qinit *st_wrinit; + struct qinit *st_muxrinit; + struct qinit *st_muxwinit; +}; + +static streamtab the_streamtab = { + &read_side, + &write_side, + NULL, + NULL +}; + +struct install_info { + struct streamtab *install_str; + uint32 install_flags; + uint32 install_sqlvl; + char *install_buddy; + void *ref_load; + uint32 ref_count; +}; + +enum { + kOTModIsDriver = 0x00000001, + kOTModUpperIsDLPI = 0x00002000, + SQLVL_MODULE = 3, +}; + +static install_info the_install_info = { + &the_streamtab, + kOTModIsDriver /*| kOTModUpperIsDLPI */, + SQLVL_MODULE, + NULL, + NULL, + 0 +}; + + +// Prototypes for exported functions +extern "C" { +#pragma export on +extern uint32 ValidateHardware(void *theID); +extern install_info* GetOTInstallInfo(); +extern uint8 InitStreamModule(void *theID); +extern void TerminateStreamModule(void); +#pragma export off +} + + +/* + * Validate that our hardware is available (always available) + */ + +uint32 ValidateHardware(void *theID) +{ + return 0; +} + + +/* + * Return pointer to install_info structure + */ + +install_info *GetOTInstallInfo(void) +{ + return &the_install_info; +} + +/* + * Init module + */ + +#ifdef BUILD_ETHER_FULL_DRIVER +asm bool NativeInitStreamModule(register void *theID) +{ + ASM_CALL_NATIVE(ETHER_INIT) +} +#else +asm uint8 InitStreamModule(register void *theID) +{ + ASM_TAIL_CALL_NATIVE(ETHER_INIT) +} +#endif + + +/* + * Terminate module + */ + +#ifdef BUILD_ETHER_FULL_DRIVER +asm void NativeTerminateStreamModule(void) +{ + ASM_CALL_NATIVE(ETHER_TERM) +} +#else +asm void TerminateStreamModule(void) +{ + ASM_TAIL_CALL_NATIVE(ETHER_TERM) +} +#endif + + +/* + * DLPI functions + */ + +#ifndef BUILD_ETHER_FULL_DRIVER +static asm int ether_open(register queue_t *rdq, register void *dev, register int flag, register int sflag, register void *creds) +{ + ASM_TAIL_CALL_NATIVE(ETHER_OPEN) +} + +static asm int ether_close(register queue_t *rdq, register int flag, register void *creds) +{ + ASM_TAIL_CALL_NATIVE(ETHER_CLOSE) +} + +static asm int ether_wput(register queue_t *q, register msgb *mp) +{ + ASM_TAIL_CALL_NATIVE(ETHER_WPUT) +} + +static asm int ether_rsrv(register queue_t *q) +{ + ASM_TAIL_CALL_NATIVE(ETHER_RSRV) +} +#endif + + +/* + * Hooks to add-on low-level functions + */ + +asm void AO_get_ethernet_address(register uint32) +{ + ASM_CALL_NATIVE(ETHER_AO_GET_HWADDR) +} + +asm void AO_enable_multicast(register uint32 addr) +{ + ASM_CALL_NATIVE(ETHER_AO_ADD_MULTI) +} + +asm void AO_disable_multicast(register uint32 addr) +{ + ASM_CALL_NATIVE(ETHER_AO_DEL_MULTI) +} + +asm void AO_transmit_packet(register uint32 mp) +{ + ASM_CALL_NATIVE(ETHER_AO_SEND_PACKET) +} diff --git a/SheepShaver/src/EthernetDriver/Ethernet.mcp b/SheepShaver/src/EthernetDriver/Ethernet.mcp new file mode 100644 index 00000000..6e14f02e Binary files /dev/null and b/SheepShaver/src/EthernetDriver/Ethernet.mcp differ diff --git a/SheepShaver/src/EthernetDriver/cpu_emulation.h b/SheepShaver/src/EthernetDriver/cpu_emulation.h new file mode 100644 index 00000000..5d044357 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/cpu_emulation.h @@ -0,0 +1 @@ +#ifndef CPU_EMULATION_H #define CPU_EMULATION_H static inline uint32 ReadMacInt32(uint32 addr) {return *(uint32 *)addr;} static inline uint32 Host2MacAddr(uint8 *addr) {return (uint32)addr;} static inline uint8 *Mac2HostAddr(uint32 addr) {return (uint8 *)addr;} static inline void *Mac_memset(uint32 addr, int c, size_t n) {return memset(Mac2HostAddr(addr), c, n);} static inline void *Mac2Host_memcpy(uint8 *dst, uint32 src, size_t n) {return memcpy(dst,Mac2HostAddr(src),n);} #endif /* CPU_EMULATION */ \ No newline at end of file diff --git a/SheepShaver/src/EthernetDriver/debug.h b/SheepShaver/src/EthernetDriver/debug.h new file mode 100644 index 00000000..9969ae37 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/debug.h @@ -0,0 +1 @@ +#ifndef DEBUG_H #define DEBUG_H #define D(x) #include #define bug printf #endif /* DEBUG_H */ \ No newline at end of file diff --git a/SheepShaver/src/EthernetDriver/ether.cpp b/SheepShaver/src/EthernetDriver/ether.cpp new file mode 100644 index 00000000..71309582 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/ether.cpp @@ -0,0 +1,1734 @@ +/* + * ether.cpp - SheepShaver Ethernet Device Driver (DLPI) + * + * SheepShaver (C) 1997-2005 Marc Hellwig and Christian Bauer + * + * 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 + */ + +/* + * TODO + * - 802.2 TEST/XID + * - MIB statistics + */ + +#include + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "ether.h" +#include "ether_defs.h" +#include "macos_util.h" + +#define DEBUG 0 +#include "debug.h" + +// Packet types +enum { + kPktDIX = 0, + kPkt8022SAP = 1, + kPkt8022GroupSAP = 2, + kPkt8022SNAP = 3, + kPktIPX = 4, + kPktUnknown = 5 +}; + + +/* + * Stream private data structure + */ + +static const int kGroupSAPMapSize = 128/32; // Number of 32-bit values we need for 128 bits +static const int kGSshift = 6; +static const int kGSmask = 0x1F; + +struct multicast_node { + nw_multicast_node_p next; + uint8 addr[kEnetPhysicalAddressLength]; +}; + +struct DLPIStream { + void SetGroupSAP(uint8 sap) + { + group_sap[sap >> kGSshift] |= (1L << ((sap >> 1) & kGSmask)); + } + + void ClearGroupSAP(uint8 sap) + { + group_sap[sap >> kGSshift] &= ~(1L << ((sap >> 1) & kGSmask)); + } + + void ClearAllGroupSAPs(void) + { + for (int i=0; i> kGSshift] & (1L << ((sap >> 1) & kGSmask)); + } + + void AddMulticast(uint8 *addr) + { + multicast_node *n = (multicast_node *)Mac2HostAddr(Mac_sysalloc(sizeof(multicast_node))); + memcpy(n->addr, addr, kEnetPhysicalAddressLength); + n->next = multicast_list; + multicast_list = n; + } + + void RemoveMulticast(uint8 *addr) + { + multicast_node *p = multicast_list; + while (p) { + if (memcmp(addr, p->addr, kEnetPhysicalAddressLength) == 0) + goto found; + p = p->next; + } + return; + found: + multicast_node *q = (multicast_node *)&multicast_list; + while (q) { + if (q->next == p) { + q->next = p->next; + Mac_sysfree(Host2MacAddr((uint8 *)p)); + return; + } + q = q->next; + } + } + + uint8 *IsMulticastRegistered(uint8 *addr) + { + multicast_node *n = multicast_list; + while (n) { + if (memcmp(addr, n->addr, kEnetPhysicalAddressLength) == 0) + return n->addr; + n = n->next; + } + return NULL; + } + + nw_uint32 minor_num; // Minor device number of this stream + nw_uint32 dlpi_state; // DLPI state of this stream + nw_uint32 flags; // Flags + nw_uint16 dlsap; // SAP bound to this stream + nw_bool framing_8022; // Using 802.2 framing? This is only used to report the MAC type for DL_INFO_ACK and can be set with an ioctl() call + nw_queue_p rdq; // Read queue for this stream + nw_uint32 group_sap[kGroupSAPMapSize]; // Map of bound group SAPs + uint8 snap[k8022SNAPLength]; // SNAP bound to this stream + nw_multicast_node_p multicast_list; // List of enabled multicast addresses +}; + +// Hack to make DLPIStream list initialization early to NULL (do we really need this?) +struct DLPIStreamInit { + DLPIStreamInit(nw_DLPIStream_p *dlpi_stream_p) { *dlpi_stream_p = NULL; } +}; + +// Stream flags +enum { + kSnapStream = 0x00000001, + kAcceptMulticasts = 0x00000002, + kAcceptAll8022Packets = 0x00000004, + kFastPathMode = 0x00000008 +}; + +// List of opened streams (used internally by OpenTransport) +static nw_DLPIStream_p dlpi_stream_list; +static DLPIStreamInit dlpi_stream_init(&dlpi_stream_list); + +// Are we open? +bool ether_driver_opened = false; + +// Our ethernet hardware address +static uint8 hardware_address[6] = {0, 0, 0, 0, 0, 0}; + +// Statistics +int32 num_wput = 0; +int32 num_error_acks = 0; +int32 num_tx_packets = 0; +int32 num_tx_raw_packets = 0; +int32 num_tx_normal_packets = 0; +int32 num_tx_buffer_full = 0; +int32 num_rx_packets = 0; +int32 num_ether_irq = 0; +int32 num_unitdata_ind = 0; +int32 num_rx_fastpath = 0; +int32 num_rx_no_mem = 0; +int32 num_rx_dropped = 0; +int32 num_rx_stream_not_ready = 0; +int32 num_rx_no_unitdata_mem = 0; + + +// Function pointers of imported functions +typedef mblk_t *(*allocb_ptr)(size_t size, int pri); +static uint32 allocb_tvect = 0; +mblk_t *allocb(size_t arg1, int arg2) +{ + return (mblk_t *)Mac2HostAddr((uint32)CallMacOS2(allocb_ptr, allocb_tvect, arg1, arg2)); +} +typedef void (*freeb_ptr)(mblk_t *); +static uint32 freeb_tvect = 0; +static inline void freeb(mblk_t *arg1) +{ + CallMacOS1(freeb_ptr, freeb_tvect, arg1); +} +typedef int16 (*freemsg_ptr)(mblk_t *); +static uint32 freemsg_tvect = 0; +static inline int16 freemsg(mblk_t *arg1) +{ + return (int16)CallMacOS1(freemsg_ptr, freemsg_tvect, arg1); +} +typedef mblk_t *(*copyb_ptr)(mblk_t *); +static uint32 copyb_tvect = 0; +static inline mblk_t *copyb(mblk_t *arg1) +{ + return (mblk_t *)Mac2HostAddr((uint32)CallMacOS1(copyb_ptr, copyb_tvect, arg1)); +} +typedef mblk_t *(*dupmsg_ptr)(mblk_t *); +static uint32 dupmsg_tvect = 0; +static inline mblk_t *dupmsg(mblk_t *arg1) +{ + return (mblk_t *)Mac2HostAddr((uint32)CallMacOS1(dupmsg_ptr, dupmsg_tvect, arg1)); +} +typedef mblk_t *(*getq_ptr)(queue_t *); +static uint32 getq_tvect = 0; +static inline mblk_t *getq(queue_t *arg1) +{ + return (mblk_t *)Mac2HostAddr((uint32)CallMacOS1(getq_ptr, getq_tvect, arg1)); +} +typedef int (*putq_ptr)(queue_t *, mblk_t *); +static uint32 putq_tvect = 0; +static inline int putq(queue_t *arg1, mblk_t *arg2) +{ + return (int)CallMacOS2(putq_ptr, putq_tvect, arg1, arg2); +} +typedef int (*putnext_ptr)(queue_t *, mblk_t *); +static uint32 putnext_tvect = 0; +static inline int putnext(queue_t *arg1, mblk_t *arg2) +{ + return (int)CallMacOS2(putnext_ptr, putnext_tvect, arg1, arg2); +} +typedef int (*putnextctl1_ptr)(queue_t *, int type, int c); +static uint32 putnextctl1_tvect = 0; +static inline int putnextctl1(queue_t *arg1, int arg2, int arg3) +{ + return (int)CallMacOS3(putnextctl1_ptr, putnextctl1_tvect, arg1, arg2, arg3); +} +typedef int (*canputnext_ptr)(queue_t *); +static uint32 canputnext_tvect = 0; +static inline int canputnext(queue_t *arg1) +{ + return (int)CallMacOS1(canputnext_ptr, canputnext_tvect, arg1); +} +typedef int (*qreply_ptr)(queue_t *, mblk_t *); +static uint32 qreply_tvect = 0; +static inline int qreply(queue_t *arg1, mblk_t *arg2) +{ + return (int)CallMacOS2(qreply_ptr, qreply_tvect, arg1, arg2); +} +typedef void (*flushq_ptr)(queue_t *, int flag); +static uint32 flushq_tvect = 0; +static inline void flushq(queue_t *arg1, int arg2) +{ + CallMacOS2(flushq_ptr, flushq_tvect, arg1, arg2); +} +typedef int (*msgdsize_ptr)(const mblk_t *); +static uint32 msgdsize_tvect = 0; +static inline int msgdsize(const mblk_t *arg1) +{ + return (int)CallMacOS1(msgdsize_ptr, msgdsize_tvect, arg1); +} +typedef void (*otenterint_ptr)(void); +static uint32 otenterint_tvect = 0; +void OTEnterInterrupt(void) +{ + CallMacOS(otenterint_ptr, otenterint_tvect); +} +typedef void (*otleaveint_ptr)(void); +static uint32 otleaveint_tvect = 0; +void OTLeaveInterrupt(void) +{ + CallMacOS(otleaveint_ptr, otleaveint_tvect); +} +typedef int (*mi_open_comm_ptr)(DLPIStream **mi_opp_orig, size_t size, queue_t *q, void *dev, int flag, int sflag, void *credp); +static uint32 mi_open_comm_tvect = 0; +static inline int mi_open_comm(DLPIStream **arg1, size_t arg2, queue_t *arg3, void *arg4, int arg5, int arg6, void *arg7) +{ + return (int)CallMacOS7(mi_open_comm_ptr, mi_open_comm_tvect, arg1, arg2, arg3, arg4, arg5, arg6, arg7); +} +typedef int (*mi_close_comm_ptr)(DLPIStream **mi_opp_orig, queue_t *q); +static uint32 mi_close_comm_tvect = 0; +static inline int mi_close_comm(DLPIStream **arg1, queue_t *arg2) +{ + return (int)CallMacOS2(mi_close_comm_ptr, mi_close_comm_tvect, arg1, arg2); +} +typedef DLPIStream *(*mi_next_ptr_ptr)(DLPIStream *); +static uint32 mi_next_ptr_tvect = 0; +static inline DLPIStream *mi_next_ptr(DLPIStream *arg1) +{ + return (DLPIStream *)Mac2HostAddr((uint32)CallMacOS1(mi_next_ptr_ptr, mi_next_ptr_tvect, arg1)); +} +#ifdef USE_ETHER_FULL_DRIVER +typedef void (*ether_dispatch_packet_ptr)(uint32 p, uint32 size); +static uint32 ether_dispatch_packet_tvect = 0; +#endif + +// Prototypes +static void ether_ioctl(DLPIStream *the_stream, queue_t* q, mblk_t* mp); +static void ether_flush(queue_t* q, mblk_t* mp); +static mblk_t *build_tx_packet_header(DLPIStream *the_stream, mblk_t *mp, bool fast_path); +static void transmit_packet(mblk_t *mp); +static void DLPI_error_ack(DLPIStream *the_stream, queue_t *q, mblk_t *ack_mp, uint32 prim, uint32 err, uint32 uerr); +static void DLPI_ok_ack(DLPIStream *the_stream, queue_t *q, mblk_t *ack_mp, uint32 prim); +static void DLPI_info(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_phys_addr(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_bind(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_unbind(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_subs_bind(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_subs_unbind(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_enable_multi(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_disable_multi(DLPIStream *the_stream, queue_t *q, mblk_t *mp); +static void DLPI_unit_data(DLPIStream *the_stream, queue_t *q, mblk_t *mp); + + +/* + * Initialize ethernet stream module + */ + +static uint8 InitStreamModuleImpl(void *theID) +{ + D(bug("InitStreamModule\n")); + + // Don't re-open if already open + if (ether_driver_opened) + return true; + ether_driver_opened = false; + + // Import functions from OTKernelLib + allocb_tvect = FindLibSymbol("\013OTKernelLib", "\006allocb"); + D(bug("allocb TVECT at %08lx\n", allocb_tvect)); + if (allocb_tvect == 0) + return false; + freeb_tvect = FindLibSymbol("\013OTKernelLib", "\005freeb"); + D(bug("freeb TVECT at %08lx\n", freeb_tvect)); + if (freeb_tvect == 0) + return false; + freemsg_tvect = FindLibSymbol("\013OTKernelLib", "\007freemsg"); + D(bug("freemsg TVECT at %08lx\n", freemsg_tvect)); + if (freemsg_tvect == 0) + return false; + copyb_tvect = FindLibSymbol("\013OTKernelLib", "\005copyb"); + D(bug("copyb TVECT at %08lx\n", copyb_tvect)); + if (copyb_tvect == 0) + return false; + dupmsg_tvect = FindLibSymbol("\013OTKernelLib", "\006dupmsg"); + D(bug("dupmsg TVECT at %08lx\n", dupmsg_tvect)); + if (dupmsg_tvect == 0) + return false; + getq_tvect = FindLibSymbol("\013OTKernelLib", "\004getq"); + D(bug("getq TVECT at %08lx\n", getq_tvect)); + if (getq_tvect == 0) + return false; + putq_tvect = FindLibSymbol("\013OTKernelLib", "\004putq"); + D(bug("putq TVECT at %08lx\n", putq_tvect)); + if (putq_tvect == 0) + return false; + putnext_tvect = FindLibSymbol("\013OTKernelLib", "\007putnext"); + D(bug("putnext TVECT at %08lx\n", putnext_tvect)); + if (putnext_tvect == 0) + return false; + putnextctl1_tvect = FindLibSymbol("\013OTKernelLib", "\013putnextctl1"); + D(bug("putnextctl1 TVECT at %08lx\n", putnextctl1_tvect)); + if (putnextctl1_tvect == 0) + return false; + canputnext_tvect = FindLibSymbol("\013OTKernelLib", "\012canputnext"); + D(bug("canputnext TVECT at %08lx\n", canputnext_tvect)); + if (canputnext_tvect == 0) + return false; + qreply_tvect = FindLibSymbol("\013OTKernelLib", "\006qreply"); + D(bug("qreply TVECT at %08lx\n", qreply_tvect)); + if (qreply_tvect == 0) + return false; + flushq_tvect = FindLibSymbol("\013OTKernelLib", "\006flushq"); + D(bug("flushq TVECT at %08lx\n", flushq_tvect)); + if (flushq_tvect == 0) + return false; + msgdsize_tvect = FindLibSymbol("\013OTKernelLib", "\010msgdsize"); + D(bug("msgdsize TVECT at %08lx\n", msgdsize_tvect)); + if (msgdsize_tvect == 0) + return false; + otenterint_tvect = FindLibSymbol("\017OTKernelUtilLib", "\020OTEnterInterrupt"); + D(bug("OTEnterInterrupt TVECT at %08lx\n", otenterint_tvect)); + if (otenterint_tvect == 0) + return false; + otleaveint_tvect = FindLibSymbol("\017OTKernelUtilLib", "\020OTLeaveInterrupt"); + D(bug("OTLeaveInterrupt TVECT at %08lx\n", otleaveint_tvect)); + if (otleaveint_tvect == 0) + return false; + mi_open_comm_tvect = FindLibSymbol("\013OTKernelLib", "\014mi_open_comm"); + D(bug("mi_open_comm TVECT at %08lx\n", mi_open_comm_tvect)); + if (mi_open_comm_tvect == 0) + return false; + mi_close_comm_tvect = FindLibSymbol("\013OTKernelLib", "\015mi_close_comm"); + D(bug("mi_close_comm TVECT at %08lx\n", mi_close_comm_tvect)); + if (mi_close_comm_tvect == 0) + return false; + mi_next_ptr_tvect = FindLibSymbol("\013OTKernelLib", "\013mi_next_ptr"); + D(bug("mi_next_ptr TVECT at %08lx\n", mi_next_ptr_tvect)); + if (mi_next_ptr_tvect == 0) + return false; + +#ifndef USE_ETHER_FULL_DRIVER + // Initialize stream list (which might be leftover) + dlpi_stream_list = NULL; + + // Ask add-on for ethernet hardware address + AO_get_ethernet_address(Host2MacAddr(hardware_address)); +#endif + + // Yes, we're open + ether_driver_opened = true; + return true; +} + +uint8 InitStreamModule(void *theID) +{ + // Common initialization code + bool net_open = InitStreamModuleImpl(theID); + + // Call InitStreamModule() in native side +#ifdef BUILD_ETHER_FULL_DRIVER + extern bool NativeInitStreamModule(void *); + if (!NativeInitStreamModule((void *)ether_dispatch_packet)) + net_open = false; +#endif + + // Import functions from the Ethernet driver +#ifdef USE_ETHER_FULL_DRIVER + ether_dispatch_packet_tvect = (uintptr)theID; + D(bug("ether_dispatch_packet TVECT at %08lx\n", ether_dispatch_packet_tvect)); + if (ether_dispatch_packet_tvect == 0) + net_open = false; +#endif + + return net_open; +} + + +/* + * Terminate ethernet stream module + */ + +static void TerminateStreamModuleImpl(void) +{ + D(bug("TerminateStreamModule\n")); + +#ifndef USE_ETHER_FULL_DRIVER + // This happens sometimes. I don't know why. + if (dlpi_stream_list != NULL) + printf("FATAL: TerminateStreamModule() called, but streams still open\n"); +#endif + + // Sorry, we're closed + ether_driver_opened = false; +} + +void TerminateStreamModule(void) +{ + // Common termination code + TerminateStreamModuleImpl(); + + // Call TerminateStreamModule() in native side +#ifdef BUILD_ETHER_FULL_DRIVER + extern void NativeTerminateStreamModule(void); + NativeTerminateStreamModule(); +#endif +} + + +/* + * Open new stream + */ + +int ether_open(queue_t *rdq, void *dev, int flag, int sflag, void *creds) +{ + D(bug("ether_open(%p,%p,%d,%d,%p)\n", rdq, dev, flag, sflag, creds)); + + // Return if driver was closed + if (!ether_driver_opened) { + printf("FATAL: ether_open(): Ethernet driver not opened\n"); + return MAC_ENXIO; + } + + // If we're being reopened, just return + if (rdq->q_ptr != NULL) + return 0; + + // Allocate DLPIStream structure + int err = mi_open_comm((DLPIStream **)&dlpi_stream_list, sizeof(DLPIStream), rdq, dev, flag, sflag, creds); + if (err) + return err; + DLPIStream *the_stream = (DLPIStream *)rdq->q_ptr; + the_stream->rdq = rdq; + the_stream->dlpi_state = DL_UNBOUND; + the_stream->flags = 0; + the_stream->dlsap = 0; + the_stream->framing_8022 = false; + the_stream->multicast_list = NULL; + return 0; +} + + +/* + * Close stream + */ + +int ether_close(queue_t *rdq, int flag, void *creds) +{ + D(bug("ether_close(%p,%d,%p)\n", rdq, flag, creds)); + + // Return if driver was closed + if (!ether_driver_opened) { + printf("FATAL: ether_close(): Ethernet driver not opened\n"); + return MAC_ENXIO; + } + + // Get stream + DLPIStream *the_stream = (DLPIStream *)rdq->q_ptr; + + // Don't close if never opened + if (the_stream == NULL) + return 0; + + // Disable all registered multicast addresses + while (the_stream->multicast_list) { + AO_disable_multicast(Host2MacAddr(the_stream->multicast_list->addr)); + the_stream->RemoveMulticast(the_stream->multicast_list->addr); + } + the_stream->multicast_list = NULL; + + // Delete the DLPIStream + return mi_close_comm((DLPIStream **)&dlpi_stream_list, rdq); +} + + +/* + * Put something on the write queue + */ + +int ether_wput(queue_t *q, mblk_t *mp) +{ + D(bug("ether_wput(%p,%p)\n", q, mp)); + + // Return if driver was closed + if (!ether_driver_opened) { + printf("FATAL: ether_wput(): Ethernet driver not opened\n"); + return MAC_ENXIO; + } + + // Get stream + DLPIStream *the_stream = (DLPIStream *)q->q_ptr; + if (the_stream == NULL) + return MAC_ENXIO; + + D(bug(" db_type %d\n", (int)mp->b_datap->db_type)); + switch (mp->b_datap->db_type) { + + case M_DATA: + // Transmit raw packet + D(bug(" raw packet\n")); + num_tx_raw_packets++; + transmit_packet(mp); + break; + + case M_PROTO: + case M_PCPROTO: { + union DL_primitives *dlp = (union DL_primitives *)(void *)mp->b_rptr; + uint32 prim = dlp->dl_primitive; + D(bug(" dl_primitive %d\n", prim)); + switch (prim) { + case DL_UNITDATA_REQ: + // Transmit normal packet + num_tx_normal_packets++; + DLPI_unit_data(the_stream, q, mp); + break; + + case DL_INFO_REQ: + DLPI_info(the_stream, q, mp); + break; + + case DL_PHYS_ADDR_REQ: + DLPI_phys_addr(the_stream, q, mp); + break; + + case DL_BIND_REQ: + DLPI_bind(the_stream, q, mp); + break; + + case DL_UNBIND_REQ: + DLPI_unbind(the_stream, q, mp); + break; + + case DL_SUBS_BIND_REQ: + DLPI_subs_bind(the_stream, q, mp); + break; + + case DL_SUBS_UNBIND_REQ: + DLPI_subs_unbind(the_stream, q, mp); + break; + + case DL_ENABMULTI_REQ: + DLPI_enable_multi(the_stream, q, mp); + break; + + case DL_DISABMULTI_REQ: + DLPI_disable_multi(the_stream, q, mp); + break; + + default: + D(bug("WARNING: ether_wsrv(): Unknown primitive\n")); + DLPI_error_ack(the_stream, q, mp, prim, DL_NOTSUPPORTED, 0); + break; + } + break; + } + + case M_IOCTL: + ether_ioctl(the_stream, q, mp); + break; + + case M_FLUSH: + ether_flush(q, mp); + break; + + default: + D(bug("WARNING: ether_wput(): Unknown message type\n")); + freemsg(mp); + break; + } + num_wput++; + return 0; +} + + +/* + * Dequeue and process messages from the read queue + */ + +int ether_rsrv(queue_t *q) +{ + mblk_t *mp; + while ((mp = getq(q)) != NULL) { + if (canputnext(q)) + putnext(q, mp); + else { + freemsg(mp); + flushq(q, FLUSHDATA); + break; + } + } + return 0; +} + + +/* + * Handle ioctl calls + */ + +static void ether_ioctl(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + struct iocblk *ioc = (struct iocblk *)(void *)mp->b_rptr; + D(bug(" ether_ioctl(%p,%p) cmd %d\n", q, mp, (int)ioc->ioc_cmd)); + + switch (ioc->ioc_cmd) { + + case I_OTSetFramingType: { // Toggles what the general info primitive returns for dl_mac_type in dl_info_ack_t structure + mblk_t *info_mp = mp->b_cont; + if (info_mp == NULL || ((info_mp->b_wptr - info_mp->b_rptr) != sizeof(uint32))) { + ioc->ioc_error = MAC_EINVAL; + goto ioctl_error; + } + uint32 framing_type = ntohl(*(uint32 *)(void *)info_mp->b_rptr); + D(bug(" I_OTSetFramingType type %d\n", framing_type)); + if (framing_type != kOTGetFramingValue) + the_stream->framing_8022 = (framing_type == kOTFraming8022); + mp->b_cont = NULL; + freemsg(info_mp); + if (the_stream->framing_8022) + ioc->ioc_rval = kOTFraming8022; + else + ioc->ioc_rval = kOTFramingEthernet; + goto ioctl_ok; + } + + case DL_IOC_HDR_INFO: { // Special Mentat call, for fast transmits + D(bug(" DL_IOC_HDR_INFO\n")); + mblk_t *info_mp = mp->b_cont; + + // Copy DL_UNITDATA_REQ block + mblk_t *unitdata_mp = copyb(info_mp); + if (unitdata_mp == NULL) { + ioc->ioc_error = MAC_ENOMEM; + goto ioctl_error; + } + unitdata_mp->b_datap->db_type = M_PROTO; + + // Construct header (converts DL_UNITDATA_REQ -> M_DATA) + mblk_t *header_mp = build_tx_packet_header(the_stream, unitdata_mp, true); + + if (header_mp == NULL) { + // Could not allocate a message block large enough + ioc->ioc_error = MAC_ENOMEM; + goto ioctl_error; + } + + // Attach header block at the end + mp->b_cont->b_cont = header_mp; + the_stream->flags |= kFastPathMode; + goto ioctl_ok; + } + + case I_OTSetRawMode: { + mblk_t *info_mp = mp->b_cont; + dl_recv_control_t *dlrc; + if (info_mp == NULL || ((info_mp->b_wptr - info_mp->b_rptr) != sizeof(dlrc->dl_primitive))) { + ioc->ioc_error = MAC_EINVAL; + goto ioctl_error; + } + dlrc = (dl_recv_control_t *)(void *)info_mp->b_rptr; + D(bug(" I_OTSetRawMode primitive %d\n", (int)dlrc->dl_primitive)); + ioc->ioc_error = MAC_EINVAL; + goto ioctl_error; + } + + default: + D(bug("WARNING: Unknown ether_ioctl() call\n")); + ioc->ioc_error = MAC_EINVAL; + goto ioctl_error; + } + +ioctl_ok: + ioc->ioc_count = 0; + for (mblk_t *mp1 = mp; (mp1 = mp1->b_cont) != NULL;) + ioc->ioc_count += mp1->b_wptr - mp1->b_rptr; + ioc->ioc_error = 0; + mp->b_datap->db_type = M_IOCACK; + qreply(q, mp); + return; + +ioctl_error: + mp->b_datap->db_type = M_IOCNAK; + qreply(q, mp); + return; +} + + +/* + * Flush call, send it up to the read side of the stream + */ + +static void ether_flush(queue_t* q, mblk_t* mp) +{ + D(bug(" ether_flush(%p,%p)\n", q, mp)); + + uint8 *rptr = mp->b_rptr; + if (*rptr & FLUSHW) + flushq(q, FLUSHALL); + if (*rptr & FLUSHR) { + flushq(RD(q), FLUSHALL); + *rptr &= ~FLUSHW; + qreply(q, mp); + } else + freemsg(mp); +} + + +/* + * Classify packet into the different types of protocols + */ + +static uint16 classify_packet_type(uint16 primarySAP, uint16 secondarySAP) +{ + if (primarySAP >= kMinDIXSAP) + return kPktDIX; + + if ((primarySAP == kIPXSAP) && (secondarySAP == kIPXSAP)) + return kPktIPX; + + if (primarySAP == kSNAPSAP) + return kPkt8022SNAP; + + if (primarySAP <= k8022GlobalSAP) + return kPkt8022SAP; + + return kPktUnknown; +} + + +/* + * Check if the address is a multicast, broadcast or standard address + */ + +static int32 get_address_type(uint8 *addr) +{ + if (addr[0] & 1) { // Multicast/broadcast flag + if (OTIs48BitBroadcastAddress(addr)) + return keaBroadcast; + else + return keaMulticast; + } else + return keaStandardAddress; +} + + +/* + * Reuse a message block, make room for more data + */ + +static mblk_t *reuse_message_block(mblk_t *mp, uint16 needed_size) +{ + mblk_t *nmp; + + if ((mp->b_datap->db_ref == 1) && ((mp->b_datap->db_lim - mp->b_datap->db_base) >= needed_size)) { + mp->b_datap->db_type = M_DATA; + mp->b_rptr = mp->b_datap->db_base; + mp->b_wptr = mp->b_datap->db_base + needed_size; + } else { + nmp = mp->b_cont; // Grab the M_DATA blocks + mp->b_cont = NULL; // Detach the M_(PC)PROTO + freemsg(mp); // Free the M_(PC)PROTO + mp = nmp; // Point to the M_DATA blocks + + // Try to get space on the first M_DATA block + if (mp && (mp->b_datap->db_ref == 1) && ((mp->b_rptr - mp->b_datap->db_base) >= needed_size)) + mp->b_rptr -= needed_size; + else { + // Try to allocate a new message + if ((nmp = allocb(needed_size, BPRI_HI)) == NULL) { + // Could not get a new message block so lets forget about the message altogether + freemsg(mp); // Free the original M_DATA portion of the message + mp = NULL; // Indicates the reuse failed + } else { + nmp->b_cont = mp; // Attach the new message block as the head + nmp->b_wptr += needed_size; + mp = nmp; + } + } + } + + return mp; +} + + +/* + * Built header for packet to be transmitted (convert DL_UNITDATA_REQ -> M_DATA) + * The passed-in message has the header info in the first message block and the data + * in the following blocks + */ + +static mblk_t *build_tx_packet_header(DLPIStream *the_stream, mblk_t *mp, bool fast_path) +{ + // Only handle unit_data requests + dl_unitdata_req_t *req = (dl_unitdata_req_t *)(void *)mp->b_rptr; + if (req->dl_primitive != DL_UNITDATA_REQ) { + freemsg(mp); + return NULL; + } + + // Extract destination address and its length + uint8 *destAddrOrig = ((uint8 *)req) + req->dl_dest_addr_offset; + uint32 destAddrLen = req->dl_dest_addr_length; + uint8 ctrl = 0x03; + + // Extract DLSAP + uint16 dlsap; + switch (destAddrLen) { + case kEnetPhysicalAddressLength: + dlsap = the_stream->dlsap; + break; + case kEnetAndSAPAddressLength: + dlsap = ntohs(*(uint16 *)(destAddrOrig + kEnetPhysicalAddressLength)); + break; + case kEnetPhysicalAddressLength + k8022DLSAPLength + k8022SNAPLength: // SNAP SAP + dlsap = ntohs(*(uint16 *)(destAddrOrig + kEnetPhysicalAddressLength)); + break; + default: + dlsap = the_stream->dlsap; + break; + } + + // Extract data size (excluding header info) and packet type + uint16 datasize = msgdsize(mp); + uint16 packetType = classify_packet_type(the_stream->dlsap, dlsap); + + // Calculate header size and protocol type/size field + uint16 hdrsize, proto; + switch (packetType) { + case kPktDIX: + hdrsize = kEnetPacketHeaderLength; + proto = dlsap; + break; + case kPkt8022SAP: + hdrsize = kEnetPacketHeaderLength + k8022BasicHeaderLength; + if (fast_path) + proto = 0; + else + proto = datasize + k8022BasicHeaderLength; + break; + case kPkt8022SNAP: + hdrsize = kEnetPacketHeaderLength + k8022SNAPHeaderLength; + if (fast_path) + proto = 0; + else + proto = datasize + k8022SNAPHeaderLength; + break; + case kPktIPX: + hdrsize = kEnetPacketHeaderLength; + if (fast_path) + proto = 0; + else + proto = datasize; + break; + default: + hdrsize = kEnetPacketHeaderLength; + proto = dlsap; + break; + } + + // We need to copy the dest address info in the message before we can reuse it + uint8 destAddrCopy[kMaxBoundAddrLength]; + memcpy(destAddrCopy, destAddrOrig, destAddrLen); + + // Resize header info in message block + if ((mp = reuse_message_block(mp, hdrsize)) == NULL) + return NULL; + struct T8022FullPacketHeader *packetHeader = (struct T8022FullPacketHeader *)(void *)mp->b_rptr; + + // Set protocol type/size field + packetHeader->fEnetPart.fProto = proto; + + // Set destination ethernet address + OTCopy48BitAddress(destAddrCopy, packetHeader->fEnetPart.fDestAddr); + + // Set other header fields + switch (packetType) { + case kPkt8022SAP: + packetHeader->f8022Part.fDSAP = (uint8)dlsap; + packetHeader->f8022Part.fSSAP = (uint8)the_stream->dlsap; + packetHeader->f8022Part.fCtrl = ctrl; + break; + case kPkt8022SNAP: { + uint8 *snapStart; + packetHeader->f8022Part.fDSAP = (uint8)dlsap; + packetHeader->f8022Part.fSSAP = (uint8)the_stream->dlsap; + packetHeader->f8022Part.fCtrl = ctrl; + if (destAddrLen >= kEnetAndSAPAddressLength + k8022SNAPLength) + snapStart = destAddrCopy + kEnetAndSAPAddressLength; + else + snapStart = the_stream->snap; + OTCopy8022SNAP(snapStart, packetHeader->f8022Part.fSNAP); + break; + } + } + + // Return updated message + return mp; +} + + +/* + * Transmit packet + */ + +static void transmit_packet(mblk_t *mp) +{ + EnetPacketHeader *enetHeader = (EnetPacketHeader *)(void *)mp->b_rptr; + + // Fill in length in 802.3 packets + if (enetHeader->fProto == 0) + enetHeader->fProto = msgdsize(mp) - sizeof(EnetPacketHeader); + + // Fill in ethernet source address + OTCopy48BitAddress(hardware_address, enetHeader->fSourceAddr); + + // Tell add-on to transmit packet + AO_transmit_packet(Host2MacAddr((uint8 *)mp)); + freemsg(mp); +} + + +/* + * Handle incoming packet (one stream), construct DL_UNITDATA_IND message + */ + +static void handle_received_packet(DLPIStream *the_stream, mblk_t *mp, uint16 packet_type, int32 dest_addr_type) +{ + // Find address and header length + uint32 addr_len; + uint32 header_len; + switch (packet_type) { + case kPkt8022SAP: + addr_len = kEnetAndSAPAddressLength; + header_len = kEnetPacketHeaderLength + k8022BasicHeaderLength; + break; + case kPkt8022SNAP: + addr_len = kEnetAndSAPAddressLength + k8022SNAPLength; + header_len = kEnetPacketHeaderLength + k8022SNAPHeaderLength; + break; + default: // DIX and IPX + addr_len = kEnetAndSAPAddressLength; + header_len = kEnetPacketHeaderLength; + break; + } + + // In Fast Path mode, don't send DL_UNITDATA_IND messages for unicast packets + if ((the_stream->flags & kFastPathMode) && dest_addr_type == keaStandardAddress) { + mp->b_rptr += header_len; + num_rx_fastpath++; + putq(the_stream->rdq, mp); + return; + } + + // Allocate the dl_unitdata_ind_t message + mblk_t *nmp; + if ((nmp = allocb(sizeof(dl_unitdata_ind_t) + 2*addr_len, BPRI_HI)) == NULL) { + freemsg(mp); + num_rx_no_unitdata_mem++; + return; + } + + // Set message type + nmp->b_datap->db_type = M_PROTO; + dl_unitdata_ind_t *ind = (dl_unitdata_ind_t*)(void *)nmp->b_rptr; + ind->dl_primitive = DL_UNITDATA_IND; + nmp->b_wptr += (sizeof(dl_unitdata_ind_t) + 2*addr_len); + + // Link M_DATA block + nmp->b_cont = mp; + + // Set address fields + ind->dl_dest_addr_length = addr_len; + ind->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t); + ind->dl_src_addr_length = addr_len; + ind->dl_src_addr_offset = sizeof(dl_unitdata_ind_t) + addr_len; + + // Set address type + ind->dl_group_address = dest_addr_type; + + // Set address fields + T8022FullPacketHeader *packetHeader = (T8022FullPacketHeader *)(void *)mp->b_rptr; + T8022AddressStruct *destAddr = ((T8022AddressStruct*)(nmp->b_rptr + ind->dl_dest_addr_offset)); + T8022AddressStruct *srcAddr = ((T8022AddressStruct*)(nmp->b_rptr + ind->dl_src_addr_offset)); + + OTCopy48BitAddress(packetHeader->fEnetPart.fDestAddr, destAddr->fHWAddr); + OTCopy48BitAddress(packetHeader->fEnetPart.fSourceAddr, srcAddr->fHWAddr); + + destAddr->fSAP = packetHeader->f8022Part.fDSAP; + srcAddr->fSAP = packetHeader->f8022Part.fSSAP; + + if (packet_type == kPkt8022SNAP) { + OTCopy8022SNAP(packetHeader->f8022Part.fSNAP, destAddr->fSNAP); + OTCopy8022SNAP(packetHeader->f8022Part.fSNAP, srcAddr->fSNAP); + } + + // "Hide" the ethernet and protocol header(s) + mp->b_rptr += header_len; + + // Pass message up the stream + num_unitdata_ind++; + putq(the_stream->rdq, nmp); + return; +} + + +/* + * Packet received, distribute it to the streams that want it + */ + +void ether_packet_received(mblk_t *mp) +{ + // Extract address and types + EnetPacketHeader *pkt = (EnetPacketHeader *)(void *)mp->b_rptr; + T8022FullPacketHeader *fullpkt = (T8022FullPacketHeader *)pkt; + uint16 sourceSAP, destSAP; + destSAP = fullpkt->fEnetPart.fProto; + if (destSAP >= kMinDIXSAP) { + // Classic ethernet + sourceSAP = destSAP; + } else { + destSAP = fullpkt->f8022Part.fDSAP; + sourceSAP = fullpkt->f8022Part.fSSAP; + } + uint16 packetType = classify_packet_type(sourceSAP, destSAP); + int32 destAddressType = get_address_type(pkt->fDestAddr); + + // Look which streams want it + DLPIStream *the_stream, *found_stream = NULL; + uint16 found_packetType = 0; + int32 found_destAddressType = 0; + for (the_stream = dlpi_stream_list; the_stream != NULL; the_stream = mi_next_ptr(the_stream)) { + + // Don't send to unbound streams + if (the_stream->dlpi_state == DL_UNBOUND) + continue; + + // Does this stream want all 802.2 packets? + if ((the_stream->flags & kAcceptAll8022Packets) && (destSAP <= 0xff)) + goto type_found; + + // No, check SAP/SNAP + if (destSAP == the_stream->dlsap) { + if (the_stream->flags & kSnapStream) { + // Check SNAPs if necessary + uint8 sum = fullpkt->f8022Part.fSNAP[0] ^ the_stream->snap[0]; + sum |= fullpkt->f8022Part.fSNAP[1] ^ the_stream->snap[1]; + sum |= fullpkt->f8022Part.fSNAP[2] ^ the_stream->snap[2]; + sum |= fullpkt->f8022Part.fSNAP[3] ^ the_stream->snap[3]; + sum |= fullpkt->f8022Part.fSNAP[4] ^ the_stream->snap[4]; + if (sum == 0) + goto type_found; + } else { + // No SNAP, found a match since saps match + goto type_found; + } + } else { + // Check for an 802.3 Group/Global (odd) + if (((packetType == kPkt8022SAP) || (packetType == kPkt8022SNAP)) && (destSAP & 1) && the_stream->TestGroupSAP(destSAP)) + goto type_found; + } + + // No stream for this SAP/SNAP found + continue; + +type_found: + // If it's a multicast packet, it must be in the stream's multicast list + if ((destAddressType == keaMulticast) && (the_stream->flags & kAcceptMulticasts) && (!the_stream->IsMulticastRegistered(pkt->fDestAddr))) + continue; + + // Send packet to stream + // found_stream keeps a pointer to the previously found stream, so that only the last + // stream gets the original message, the other ones get duplicates + if (found_stream) + handle_received_packet(found_stream, dupmsg(mp), found_packetType, found_destAddressType); + found_stream = the_stream; + found_packetType = packetType; + found_destAddressType = destAddressType; + } + + // Send original message to last found stream + if (found_stream) + handle_received_packet(found_stream, mp, found_packetType, found_destAddressType); + else { + freemsg(mp); // Nobody wants it *snief* + num_rx_dropped++; + } +} + +void ether_dispatch_packet(uint32 p, uint32 size) +{ +#ifdef USE_ETHER_FULL_DRIVER + // Call handler from the Ethernet driver + D(bug("ether_dispatch_packet\n")); + D(bug(" packet data at %p, %d bytes\n", p, size)); + CallMacOS2(ether_dispatch_packet_ptr, ether_dispatch_packet_tvect, p, size); +#else + // Wrap packet in message block + num_rx_packets++; + mblk_t *mp; + if ((mp = allocb(size, 0)) != NULL) { + D(bug(" packet data at %p\n", (void *)mp->b_rptr)); + Mac2Host_memcpy(mp->b_rptr, p, size); + mp->b_wptr += size; + ether_packet_received(mp); + } else { + D(bug("WARNING: Cannot allocate mblk for received packet\n")); + num_rx_no_mem++; + } +#endif +} + + +/* + * Build and send an error acknowledge + */ + +static void DLPI_error_ack(DLPIStream *the_stream, queue_t *q, mblk_t *ack_mp, uint32 prim, uint32 err, uint32 uerr) +{ + D(bug(" DLPI_error_ack(%p,%p) prim %d, err %d, uerr %d\n", the_stream, ack_mp, prim, err, uerr)); + num_error_acks++; + + if (ack_mp != NULL) + freemsg(ack_mp); + if ((ack_mp = allocb(sizeof(dl_error_ack_t), BPRI_HI)) == NULL) + return; + + ack_mp->b_datap->db_type = M_PCPROTO; + dl_error_ack_t *errp = (dl_error_ack_t *)(void *)ack_mp->b_wptr; + errp->dl_primitive = DL_ERROR_ACK; + errp->dl_error_primitive = prim; + errp->dl_errno = err; + errp->dl_unix_errno = uerr; + ack_mp->b_wptr += sizeof(dl_error_ack_t); + qreply(q, ack_mp); +} + + +/* + * Build and send an OK acknowledge + */ + +static void DLPI_ok_ack(DLPIStream *the_stream, queue_t *q, mblk_t *ack_mp, uint32 prim) +{ + if (ack_mp->b_datap->db_ref != 1) { + // Message already in use, create a new one + freemsg(ack_mp); + if ((ack_mp = allocb(sizeof(dl_error_ack_t), BPRI_HI)) == NULL) + return; + } else { + // Message free + if (ack_mp->b_cont != NULL) { + freemsg(ack_mp->b_cont); + ack_mp->b_cont = NULL; + } + } + + ack_mp->b_datap->db_type = M_PCPROTO; + dl_ok_ack_t *ackp = (dl_ok_ack_t *)(void *)ack_mp->b_rptr; + ackp->dl_primitive = DL_OK_ACK; + ackp->dl_correct_primitive = prim; + ack_mp->b_wptr = ack_mp->b_rptr + sizeof(dl_ok_ack_t); + qreply(q, ack_mp); +} + + +/* + * Handle DL_INFO_REQ (report general information) + */ + +static void DLPI_info(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + D(bug(" DLPI_info(%p)\n", the_stream)); + uint32 saplen = 0; + uint32 addrlen = kEnetPhysicalAddressLength; + uint32 bcastlen = kEnetPhysicalAddressLength; + uint32 hdrlen = kEnetPacketHeaderLength; + + // Calculate header length + if (the_stream->dlpi_state != DL_UNBOUND) { + saplen = (the_stream->flags & kSnapStream) ? k8022DLSAPLength+k8022SNAPLength : k8022DLSAPLength; + if (the_stream->dlsap == kSNAPSAP) + hdrlen = kEnetPacketHeaderLength + k8022SNAPHeaderLength; // SNAP address + else if ((the_stream->dlsap <= kMax8022SAP) || (the_stream->dlsap == kIPXSAP)) + hdrlen = kEnetPacketHeaderLength + k8022BasicHeaderLength; // SAP or IPX + else + hdrlen = kEnetPacketHeaderLength; // Basic Ethernet + } + + // Allocate message block for reply + mblk_t *ack_mp; + if ((ack_mp = allocb(sizeof(dl_info_ack_t) + addrlen + saplen + bcastlen, BPRI_LO)) == NULL) { + DLPI_error_ack(the_stream, q, mp, DL_INFO_REQ, DL_SYSERR, MAC_ENOMEM); + return; + } + + // Set up message type + ack_mp->b_datap->db_type = M_PCPROTO; + dl_info_ack_t *ackp = (dl_info_ack_t *)(void *)ack_mp->b_rptr; + ackp->dl_primitive = DL_INFO_ACK; + + // Info/version fields + ackp->dl_service_mode = DL_CLDLS; + ackp->dl_provider_style = DL_STYLE1; + ackp->dl_version = DL_VERSION_2; + ackp->dl_current_state = the_stream->dlpi_state; + ackp->dl_mac_type = the_stream->framing_8022 ? DL_CSMACD : DL_ETHER; + ackp->dl_reserved = 0; + ackp->dl_qos_length = 0; + ackp->dl_qos_offset = (uint32)DL_UNKNOWN; + ackp->dl_qos_range_length = 0; + ackp->dl_qos_range_offset = (uint32)DL_UNKNOWN; + ackp->dl_growth = 0; + ackp->dl_min_sdu = 1; + ackp->dl_max_sdu = kEnetTSDU - hdrlen; + + // Address fields + ackp->dl_sap_length = -saplen; // Negative to indicate sap follows physical address + ackp->dl_addr_length = addrlen + saplen; + ackp->dl_addr_offset = sizeof(dl_info_ack_t); + T8022AddressStruct *boundAddr = ((T8022AddressStruct *)(ack_mp->b_rptr + ackp->dl_addr_offset)); + OTCopy48BitAddress(hardware_address, boundAddr->fHWAddr); + if (saplen) { + boundAddr->fSAP = the_stream->dlsap; + if (the_stream->flags & kSnapStream) + OTCopy8022SNAP(the_stream->snap, boundAddr->fSNAP); + } + ackp->dl_brdcst_addr_length = bcastlen; + ackp->dl_brdcst_addr_offset = sizeof(dl_info_ack_t) + addrlen + saplen; + OTSet48BitBroadcastAddress(ack_mp->b_rptr + ackp->dl_brdcst_addr_offset); + + // Advance write pointer + ack_mp->b_wptr += sizeof(dl_info_ack_t) + addrlen + saplen + bcastlen; + + // Free request + freemsg(mp); + + // Send reply + qreply(q, ack_mp); + return; +} + + +/* + * Handle DL_PHYS_ADDR_REQ (report physical address) + */ + +static void DLPI_phys_addr(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + D(bug(" DLPI_phys_addr(%p,%p)\n", the_stream, mp)); + dl_phys_addr_req_t *req = (dl_phys_addr_req_t *)(void *)mp->b_rptr; + + // Allocate message block for reply + mblk_t *ack_mp; + if ((ack_mp = allocb(sizeof(dl_phys_addr_ack_t) + kEnetPhysicalAddressLength, BPRI_HI)) == NULL) { + DLPI_error_ack(the_stream, q, mp, DL_PHYS_ADDR_REQ, DL_SYSERR, MAC_ENOMEM); + return; + } + + // Set up message type + ack_mp->b_datap->db_type = M_PCPROTO; + dl_phys_addr_ack_t *ackp = (dl_phys_addr_ack_t *)(void *)ack_mp->b_wptr; + ackp->dl_primitive = DL_PHYS_ADDR_ACK; + + // Fill in address + ackp->dl_addr_length = kEnetPhysicalAddressLength; + ackp->dl_addr_offset = sizeof(dl_phys_addr_ack_t); + ack_mp->b_wptr += sizeof(dl_phys_addr_ack_t) + kEnetPhysicalAddressLength; + if (req->dl_addr_type == DL_CURR_PHYS_ADDR || req->dl_addr_type == DL_FACT_PHYS_ADDR) + OTCopy48BitAddress(hardware_address, ack_mp->b_rptr + ackp->dl_addr_offset); + else { + DLPI_error_ack(the_stream, q, mp, DL_PHYS_ADDR_REQ, DL_BADPRIM, 0); + return; + } + + // Free request + freemsg(mp); + + // Send reply + qreply(q, ack_mp); + return; +} + + +/* + * Handle DL_BIND_REQ (bind a stream) + */ + +static void DLPI_bind(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + dl_bind_req_t *req = (dl_bind_req_t *)(void *)mp->b_rptr; + uint32 sap = req->dl_sap; + D(bug(" DLPI_bind(%p,%p) SAP %04x\n", the_stream, mp, sap)); + + // Stream must be unbound + if (the_stream->dlpi_state != DL_UNBOUND) { + DLPI_error_ack(the_stream, q, mp, DL_BIND_REQ, DL_OUTSTATE, 0); + return; + } + + // We only support connectionless data link services + if (req->dl_service_mode != DL_CLDLS || req->dl_max_conind != 0) { + DLPI_error_ack(the_stream, q, mp, DL_BIND_REQ, DL_UNSUPPORTED, 0); + return; + } + + // Don't bind to 802.2 group saps, can't check 802.2 global sap (0xFF) + // because it looks like IPX + if ((sap <= kMax8022SAP) && (sap & 1)) { + DLPI_error_ack(the_stream, q, mp, DL_BIND_REQ, DL_BADADDR, 0); + return; + } + + if (classify_packet_type(sap, sap) == kPktUnknown) { + DLPI_error_ack(the_stream, q, mp, DL_BIND_REQ, DL_BADADDR, 0); + return; + } + + // Allocate message block for reply + mblk_t *ack_mp; + if ((ack_mp = allocb(sizeof(dl_bind_ack_t) + kEnetAndSAPAddressLength, BPRI_HI)) == NULL) { + DLPI_error_ack(the_stream, q, mp, DL_BIND_REQ, DL_SYSERR, MAC_ENOMEM); + return; + } + + // Set up message type + ack_mp->b_datap->db_type = M_PCPROTO; + dl_bind_ack_t *ackp = (dl_bind_ack_t *)(void *)ack_mp->b_rptr; + ackp->dl_primitive = DL_BIND_ACK; + + // Fill in other fields + ackp->dl_sap = sap; + ackp->dl_addr_length = kEnetAndSAPAddressLength; + ackp->dl_addr_offset = sizeof(dl_bind_ack_t); + ackp->dl_max_conind = 0; + ackp->dl_xidtest_flg = 0; + + T8022AddressStruct *addrInfo = (T8022AddressStruct *)(ack_mp->b_rptr + sizeof(dl_bind_ack_t)); + OTCopy48BitAddress(hardware_address, addrInfo->fHWAddr); + addrInfo->fSAP = sap; + + // Must move b_wptr past the address info data + ack_mp->b_wptr = ack_mp->b_rptr + sizeof(dl_bind_ack_t) + kEnetAndSAPAddressLength; + + // Set group SAP if necessary + the_stream->ClearAllGroupSAPs(); + if (sap <= kMax8022SAP) + the_stream->SetGroupSAP(k8022GlobalSAP); + + // The stream is now bound and idle + the_stream->dlpi_state = DL_IDLE; + the_stream->dlsap = sap; + the_stream->flags &= ~kSnapStream; + + // Free request + freemsg(mp); + + // Send reply + qreply(q, ack_mp); + return; +} + + +/* + * Handle DL_UNBIND_REQ (unbind a stream) + */ + +static void DLPI_unbind(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + D(bug(" DLPI_unbind(%p,%p)\n", the_stream, mp)); + + // Stream must be bound and idle + if (the_stream->dlpi_state != DL_IDLE) { + DLPI_error_ack(the_stream, q, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0); + return; + } + + // Stream is now unbound + the_stream->dlpi_state = DL_UNBOUND; + the_stream->dlsap = 0; + + // Flush all pending outbound messages + flushq(q, FLUSHDATA); + + // Flush all inbound messages pending on the stream + flushq(RD(q), FLUSHDATA); + putnextctl1(RD(q), M_FLUSH, FLUSHRW); + + // Send reply + DLPI_ok_ack(the_stream, q, mp, DL_UNBIND_REQ); + return; +} + + +/* + * Handle DL_SUBS_BIND_REQ (register 802.2 SAP group addresses and SNAPs) + */ + +static void DLPI_subs_bind(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + dl_subs_bind_req_t *req = (dl_subs_bind_req_t *)(void *)mp->b_rptr; + uint8 *sap = ((uint8 *)req) + req->dl_subs_sap_offset; + int32 length = req->dl_subs_sap_length; + uint16 theSap = ntohs(*((uint16 *)sap)); + int32 error = 0; + D(bug(" DLPI_subs_bind(%p,%p) SAP %02x%02x%02x%02x%02x\n", the_stream, mp, sap[0], sap[1], sap[2], sap[3], sap[4])); + + // Stream must be idle + if (the_stream->dlpi_state != DL_IDLE) { + DLPI_error_ack(the_stream, q, mp, DL_SUBS_BIND_REQ, DL_OUTSTATE, 0); + return; + } + + // Check if address is valid + switch (req->dl_subs_bind_class) { + case DL_PEER_BIND: // Bind a group address + if (the_stream->dlsap <= kMax8022SAP) { + if ((theSap & 1) && (length == sizeof(theSap))) + the_stream->SetGroupSAP(theSap); + else + if (theSap == 0x0000) // special case to receive all 802.2 packets + the_stream->flags |= kAcceptAll8022Packets; + else + error = DL_BADADDR; + } else + error = DL_UNSUPPORTED; + break; + + case DL_HIERARCHICAL_BIND: // Bind an additional SNAP + if (the_stream->dlsap == kSNAPSAP) { + if (the_stream->flags & kSnapStream) + error = DL_TOOMANY; // only one SNAP binding allowed + else { + OTCopy8022SNAP(sap, the_stream->snap); + the_stream->flags |= kSnapStream; + } + } else + error = DL_BADADDR; + break; + + default: + error = DL_UNSUPPORTED; + break; + } + if (error) { + DLPI_error_ack(the_stream, q, mp, DL_SUBS_BIND_REQ, error, 0); + return; + } + + // Allocate message block for reply + mblk_t *ack_mp; + if ((ack_mp = allocb(sizeof(dl_subs_bind_ack_t) + length, BPRI_HI)) == NULL) { + DLPI_error_ack(the_stream, q, mp, DL_SUBS_BIND_REQ, DL_SYSERR, MAC_ENOMEM); + return; + } + + // Set up message type + ack_mp->b_datap->db_type = M_PCPROTO; + dl_subs_bind_ack_t *ackp = (dl_subs_bind_ack_t *)(void *)ack_mp->b_wptr; + memset(ackp, 0, sizeof(dl_subs_bind_ack_t) + length); + ackp->dl_primitive = DL_SUBS_BIND_ACK; + + // Fill in other fields + ackp->dl_subs_sap_length = length; + ackp->dl_subs_sap_offset = length ? sizeof(dl_subs_bind_ack_t) : 0; + ack_mp->b_wptr += sizeof(dl_subs_bind_ack_t); + if (length) + memcpy(ack_mp->b_wptr, sap, length); + ack_mp->b_wptr += length; + + // Free request + freemsg(mp); + + // Send reply + qreply(q, ack_mp); + return; +} + + +/* + * Handle DL_SUBS_UNBIND_REQ (unregister 802.2 SAP group addresses and snaps) + */ + +static void DLPI_subs_unbind(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + dl_subs_unbind_req_t *req = (dl_subs_unbind_req_t *)(void *)mp->b_rptr; + uint8 *sap = ((uint8 *)req) + req->dl_subs_sap_offset; + int32 length = req->dl_subs_sap_length; + int32 error = 0; + D(bug(" DLPI_subs_unbind(%p,%p) SAP %02x%02x%02x%02x%02x\n", the_stream, mp, sap[0], sap[1], sap[2], sap[3], sap[4])); + + // Stream must be idle + if (the_stream->dlpi_state != DL_IDLE) { + DLPI_error_ack(the_stream, q, mp, DL_SUBS_UNBIND_REQ, DL_OUTSTATE, 0); + return; + } + + // Check if we are unbinding from an address we are bound to + if (length == k8022SAPLength) { + if ((*sap & 1) && (*sap != kIPXSAP)) { + if (the_stream->dlsap <= kMax8022SAP) + the_stream->ClearGroupSAP(*sap); + else + error = DL_UNSUPPORTED; + } else + error = DL_BADADDR; + } else if (length == k8022SNAPLength) { + if (the_stream->dlsap == kSNAPSAP) { + if (the_stream->flags & kSnapStream) { + if (memcmp(the_stream->snap, sap, length) != 0) + error = DL_BADADDR; + } else + error = DL_BADADDR; + } else + error = DL_UNSUPPORTED; + } + if (error) { + DLPI_error_ack(the_stream, q, mp, DL_SUBS_UNBIND_REQ, error, 0); + return; + } + + // Stream is no longer bound to SNAP + the_stream->flags &= ~kSnapStream; + + // Send reply + DLPI_ok_ack(the_stream, q, mp, DL_SUBS_UNBIND_REQ); + return; +} + + +/* + * Handles DL_ENABMULTI_REQ (enable multicast address) + */ + +static void DLPI_enable_multi(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + dl_enabmulti_req_t* req = (dl_enabmulti_req_t*)(void *)mp->b_rptr; + uint8 *reqaddr = (uint8 *)(mp->b_rptr + req->dl_addr_offset); + D(bug(" DLPI_enable_multi(%p,%p) addr %02x%02x%02x%02x%02x%02x\n", the_stream, mp, reqaddr[0], reqaddr[1], reqaddr[2], reqaddr[3], reqaddr[4], reqaddr[5])); + + // Address must be a multicast address + if (get_address_type(reqaddr) != keaMulticast) { + DLPI_error_ack(the_stream, q, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0); + return; + } + + // Address already in multicast list? + if (the_stream->IsMulticastRegistered(reqaddr)) { + DLPI_error_ack(the_stream, q, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0); + return; + } + + // Tell add-on to enable multicast address + AO_enable_multicast(Host2MacAddr((uint8 *)reqaddr)); + + // Add new address to multicast list + uint8 *addr = Mac2HostAddr(Mac_sysalloc(kEnetPhysicalAddressLength)); + OTCopy48BitAddress(reqaddr, addr); + the_stream->AddMulticast(addr); + + // On receive now check multicast packets + the_stream->flags |= kAcceptMulticasts; + + // Send reply + DLPI_ok_ack(the_stream, q, mp, DL_ENABMULTI_REQ); + return; +} + + +/* + * Handles DL_DISABMULTI_REQ (disable multicast address) + */ + +static void DLPI_disable_multi(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + dl_disabmulti_req_t *req = (dl_disabmulti_req_t*)(void *)mp->b_rptr; + uint8 *reqaddr = (uint8 *)(mp->b_rptr + req->dl_addr_offset); + D(bug(" DLPI_disable_multi(%p,%p) addr %02x%02x%02x%02x%02x%02x\n", the_stream, mp, reqaddr[0], reqaddr[1], reqaddr[2], reqaddr[3], reqaddr[4], reqaddr[5])); + + // Address must be a multicast address + if (get_address_type(reqaddr) != keaMulticast) { + DLPI_error_ack(the_stream, q, mp, DL_DISABMULTI_REQ, DL_BADADDR, 0); + return; + } + + // Find address in multicast list + uint8 *addr = the_stream->IsMulticastRegistered(reqaddr); + if (addr == NULL) { + DLPI_error_ack(the_stream, q, mp, DL_DISABMULTI_REQ, DL_BADADDR, 0); + return; + } + + // Found, then remove + the_stream->RemoveMulticast(addr); + Mac_sysfree(Host2MacAddr(addr)); + + // Tell add-on to disable multicast address + AO_disable_multicast(Host2MacAddr((uint8 *)reqaddr)); + + // No longer check multicast packets if no multicast addresses are registered + if (the_stream->multicast_list == NULL) + the_stream->flags &= ~kAcceptMulticasts; + + // Send reply + DLPI_ok_ack(the_stream, q, mp, DL_DISABMULTI_REQ); + return; +} + + +/* + * Handle DL_UNITDATA_REQ (transmit packet) + */ + +static void DLPI_unit_data(DLPIStream *the_stream, queue_t *q, mblk_t *mp) +{ + D(bug(" DLPI_unit_data(%p,%p)\n", the_stream, mp)); + dl_unitdata_req_t *req = (dl_unitdata_req_t *)(void *)mp->b_rptr; + + // Stream must be idle + if (the_stream->dlpi_state != DL_IDLE) { + + // Not idle, send error response + dl_uderror_ind_t *errp; + mblk_t *bp; + + int i = sizeof(dl_uderror_ind_t) + req->dl_dest_addr_length; + if ((bp = allocb(i, BPRI_HI)) == NULL) { + freemsg(mp); + return; + } + bp->b_datap->db_type = M_PROTO; + errp = (dl_uderror_ind_t *)(void *)bp->b_wptr; + errp->dl_primitive = DL_UDERROR_IND; + errp->dl_errno = DL_OUTSTATE; + errp->dl_unix_errno = 0; + errp->dl_dest_addr_length = req->dl_dest_addr_length; + errp->dl_dest_addr_offset = sizeof(dl_uderror_ind_t); + bp->b_wptr += sizeof(dl_uderror_ind_t); + memcpy((uint8 *)bp->b_wptr, ((uint8 *)req) + req->dl_dest_addr_offset, req->dl_dest_addr_length); + bp->b_wptr += req->dl_dest_addr_length; + qreply(q, bp); + + freemsg(mp); + return; + } + + // Build packet header and transmit packet + if ((mp = build_tx_packet_header(the_stream, mp, false)) != NULL) + transmit_packet(mp); +} + + +/* + * Ethernet packet allocator + */ + +#if SIZEOF_VOID_P != 4 || REAL_ADDRESSING == 0 +static uint32 ether_packet = 0; // Ethernet packet (cached allocation) +static uint32 n_ether_packets = 0; // Number of ethernet packets allocated so far (should be at most 1) + +EthernetPacket::EthernetPacket() +{ + ++n_ether_packets; + if (ether_packet && n_ether_packets == 1) + packet = ether_packet; + else { + packet = Mac_sysalloc(1516); + assert(packet != 0); + Mac_memset(packet, 0, 1516); + if (ether_packet == 0) + ether_packet = packet; + } +} + +EthernetPacket::~EthernetPacket() +{ + --n_ether_packets; + if (packet != ether_packet) + Mac_sysfree(packet); + if (n_ether_packets > 0) { + bug("WARNING: Nested allocation of ethernet packets!\n"); + } +} +#endif diff --git a/SheepShaver/src/EthernetDriver/ether.h b/SheepShaver/src/EthernetDriver/ether.h new file mode 100644 index 00000000..7ea2c881 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/ether.h @@ -0,0 +1,113 @@ +/* + * ether.h - SheepShaver Ethernet Device Driver + * + * SheepShaver (C) 1997-2005 Marc Hellwig and Christian Bauer + * + * 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 + */ + +#ifndef ETHER_H +#define ETHER_H + +struct queue; +struct msgb; +typedef struct queue queue_t; +typedef struct msgb mblk_t; + +// Prototypes for exported functions +extern "C" { +#pragma export on +extern uint32 ValidateHardware(void *theID); +struct install_info; +extern install_info* GetOTInstallInfo(); +extern uint8 InitStreamModule(void *theID); +extern void TerminateStreamModule(void); +#pragma export off +} + +extern bool NativeInitStreamModule(void *); +extern void NativeTerminateStreamModule(void); + +extern int ether_open(queue_t *rdq, void *dev, int flag, int sflag, void *creds); +extern int ether_close(queue_t *rdq, int flag, void *creds); +extern int ether_wput(queue_t *q, mblk_t *mp); +extern int ether_rsrv(queue_t *q); + +// System specific and internal functions/data +extern void EtherInit(void); +extern void EtherExit(void); + +extern void EtherIRQ(void); + +extern void AO_get_ethernet_address(uint32 addr); +extern void AO_enable_multicast(uint32 addr); +extern void AO_disable_multicast(uint32 addr); +extern void AO_transmit_packet(uint32 mp); + +extern mblk_t *allocb(size_t size, int pri); +extern void OTEnterInterrupt(void); +extern void OTLeaveInterrupt(void); + +extern void ether_dispatch_packet(uint32 p, uint32 length); +extern void ether_packet_received(mblk_t *mp); + +extern bool ether_driver_opened; + +// Ethernet packet allocator (optimized for 32-bit platforms in real addressing mode) +class EthernetPacket { +#if SIZEOF_VOID_P == 4 && REAL_ADDRESSING + uint8 packet[1516]; + public: + uint32 addr(void) const { return (uint32)packet; } +#else + uint32 packet; + public: + EthernetPacket(); + ~EthernetPacket(); + uint32 addr(void) const { return packet; } +#endif +}; + +// Copy packet data from message block to linear buffer (must hold at +// least 1514 bytes), returns packet length +static inline int ether_msgb_to_buffer(uint32 mp, uint8 *p) +{ + int len = 0; + while (mp) { + uint32 size = ReadMacInt32(mp + 16) - ReadMacInt32(mp + 12); + Mac2Host_memcpy(p, ReadMacInt32(mp + 12), size); + len += size; + p += size; + mp = ReadMacInt32(mp + 8); + } + return len; +} + +extern int32 num_wput; +extern int32 num_error_acks; +extern int32 num_tx_packets; +extern int32 num_tx_raw_packets; +extern int32 num_tx_normal_packets; +extern int32 num_tx_buffer_full; +extern int32 num_rx_packets; +extern int32 num_ether_irq; +extern int32 num_unitdata_ind; +extern int32 num_rx_fastpath; +extern int32 num_rx_no_mem; +extern int32 num_rx_dropped; +extern int32 num_rx_stream_not_ready; +extern int32 num_rx_no_unitdata_mem; + +#endif diff --git a/SheepShaver/src/EthernetDriver/ether_defs.h b/SheepShaver/src/EthernetDriver/ether_defs.h new file mode 100644 index 00000000..72bb58ed --- /dev/null +++ b/SheepShaver/src/EthernetDriver/ether_defs.h @@ -0,0 +1,552 @@ +/* + * ether_defs.h - Definitions for DLPI Ethernet Driver + * + * SheepShaver (C) 1997-2005 Marc Hellwig and Christian Bauer + * + * 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 + */ + +#ifndef ETHER_DEFS_H +#define ETHER_DEFS_H + + +#if __MWERKS__ && __POWERPC__ +#ifndef PRAGMA_ALIGN_SUPPORTED +#define PRAGMA_ALIGN_SUPPORTED 1 +#endif +#define PACKED__ +#else +#define PACKED__ __attribute__ ((packed)) +#endif + + +/* + * Macros + */ + +// Get pointer to the read queue, assumes 'q' is a write queue ptr +#define RD(q) (&q[-1]) + +// Get pointer to the write queue, assumes 'q' is a read queue ptr +#define WR(q) (&q[1]) + +#define OTCompare48BitAddresses(p1, p2) \ + (*(const uint32*)((const uint8*)(p1)) == *(const uint32*)((const uint8*)(p2)) && \ + *(const uint16*)(((const uint8*)(p1))+4) == *(const uint16*)(((const uint8*)(p2))+4) ) + +#define OTCopy48BitAddress(p1, p2) \ + (*(uint32*)((uint8*)(p2)) = *(const uint32*)((const uint8*)(p1)), \ + *(uint16*)(((uint8*)(p2))+4) = *(const uint16*)(((const uint8*)(p1))+4) ) + +#define OTClear48BitAddress(p1) \ + (*(uint32*)((uint8*)(p1)) = 0, \ + *(uint16*)(((uint8*)(p1))+4) = 0 ) + +#define OTCompare8022SNAP(p1, p2) \ + (*(const uint32*)((const uint8*)(p1)) == *(const uint32*)((const uint8*)(p2)) && \ + *(((const uint8*)(p1))+4) == *(((const uint8*)(p2))+4) ) + +#define OTCopy8022SNAP(p1, p2) \ + (*(uint32*)((uint8*)(p2)) = *(const uint32*)((const uint8*)(p1)), \ + *(((uint8*)(p2))+4) = *(((const uint8*)(p1))+4) ) + +#define OTIs48BitBroadcastAddress(p1) \ + (*(uint32*)((uint8*)(p1)) == 0xffffffff && \ + *(uint16*)(((uint8*)(p1))+4) == 0xffff ) + +#define OTSet48BitBroadcastAddress(p1) \ + (*(uint32*)((uint8*)(p1)) = 0xffffffff, \ + *(uint16*)(((uint8*)(p1))+4) = 0xffff ) + +#define OTIs48BitZeroAddress(p1) \ + (*(uint32*)((uint8*)(p1)) == 0 && \ + *(uint16*)(((uint8*)(p1))+4) == 0 ) + + +/* + * Constants + */ + +enum { + // Address and packet lengths + kEnetPhysicalAddressLength = 6, + k8022SAPLength = 1, + k8022DLSAPLength = 2, + k8022SNAPLength = 5, + kMaxBoundAddrLength = 6 + 2 + 5, // addr/SAP/SNAP + kEnetAndSAPAddressLength = kEnetPhysicalAddressLength + k8022DLSAPLength, + kEnetPacketHeaderLength = (2 * kEnetPhysicalAddressLength) + k8022DLSAPLength, + k8022BasicHeaderLength = 3, // SSAP/DSAP/Control + k8022SNAPHeaderLength = k8022SNAPLength + k8022BasicHeaderLength, + kMinDIXSAP = 1501, + kEnetTSDU = 1514, + + // Special addresses + kSNAPSAP = 0xaa, + kMax8022SAP = 0xfe, + k8022GlobalSAP = 0xff, + kIPXSAP = 0xff, + + // DLPI interface states + DL_UNBOUND = 0, + + // Message types + M_DATA = 0, + M_PROTO = 1, + M_IOCTL = 14, + M_IOCACK = 129, + M_IOCNAK = 130, + M_PCPROTO = 131, // priority message + M_FLUSH = 134, + FLUSHDATA = 0, + FLUSHALL = 1, + FLUSHR = 1, + FLUSHW = 2, + FLUSHRW = 3, + + // DLPI primitives + DL_INFO_REQ = 0, + DL_BIND_REQ = 1, + DL_PEER_BIND = 1, + DL_HIERARCHICAL_BIND = 2, + DL_UNBIND_REQ = 2, + DL_INFO_ACK = 3, + DL_BIND_ACK = 4, + DL_ERROR_ACK = 5, + DL_OK_ACK = 6, + DL_UNITDATA_REQ = 7, + DL_UNITDATA_IND = 8, + DL_UDERROR_IND = 9, + DL_SUBS_UNBIND_REQ = 21, + DL_SUBS_BIND_REQ = 27, + DL_SUBS_BIND_ACK = 28, + DL_ENABMULTI_REQ = 29, + DL_DISABMULTI_REQ = 30, + DL_PHYS_ADDR_REQ = 49, + DL_PHYS_ADDR_ACK = 50, + DL_FACT_PHYS_ADDR = 1, + DL_CURR_PHYS_ADDR = 2, + + // DLPI states + DL_IDLE = 3, + + // DLPI error codes + DL_BADADDR = 1, // improper address format + DL_OUTSTATE = 3, // improper state + DL_SYSERR = 4, // UNIX system error + DL_UNSUPPORTED = 7, // service unsupported + DL_BADPRIM = 9, // primitive unknown + DL_NOTSUPPORTED = 18, // primitive not implemented + DL_TOOMANY = 19, // limit exceeded + + // errnos + MAC_ENXIO = 6, + MAC_ENOMEM = 12, + MAC_EINVAL = 22, + + // Various DLPI constants + DL_CLDLS = 2, // connectionless data link service + DL_STYLE1 = 0x500, + DL_VERSION_2 = 2, + DL_CSMACD = 0, + DL_ETHER = 4, + DL_UNKNOWN = -1, + + // ioctl() codes + I_OTSetFramingType = (('O' << 8) | 2), + kOTGetFramingValue = -1, + kOTFramingEthernet = 1, + kOTFramingEthernetIPX = 2, + kOTFramingEthernet8023 = 4, + kOTFraming8022 = 8, + I_OTSetRawMode = (('O' << 8) | 3), + DL_IOC_HDR_INFO = (('l' << 8) | 10), + + // Buffer allocation priority + BPRI_LO = 1, + BPRI_HI = 3, + + // Misc constants + kEnetModuleID = 7101 +}; + +enum EAddrType { + keaStandardAddress = 0, + keaMulticast, + keaBroadcast, + keaBadAddress +}; + + +/* + * Data member wrappers + */ + +// Forward declarations +struct datab; +struct msgb; +struct queue; +struct multicast_node; +struct DLPIStream; + +// Optimize for 32-bit big endian targets +#if defined(WORDS_BIGENDIAN) && (SIZEOF_VOID_P == 4) + +// Predefined member types +typedef int8 nw_int8; +typedef int16 nw_int16; +typedef int32 nw_int32; +typedef uint8 nw_uint8; +typedef uint16 nw_uint16; +typedef uint32 nw_uint32; +typedef int nw_bool; +typedef uint8 * nw_uint8_p; +typedef void * nw_void_p; +typedef datab * nw_datab_p; +typedef msgb * nw_msgb_p; +typedef queue * nw_queue_p; +typedef multicast_node *nw_multicast_node_p; +typedef DLPIStream * nw_DLPIStream_p; + +#else + +// Big-endian memory accessor +template< int nbytes > +struct nw_memory_helper; + +template<> +struct nw_memory_helper<1> { + static inline uint8 load(void *ptr) { return *((uint8 *)ptr); } + static inline void store(void *ptr, uint8 val) { *((uint8 *)ptr) = val; } +}; + +template<> +struct nw_memory_helper<2> { + static inline uint16 load(void *ptr) { return ntohs(*((uint16 *)ptr)); } + static inline void store(void *ptr, uint16 val) { *((uint16 *)ptr) = htons(val); } +}; + +template<> +struct nw_memory_helper<4> { + static inline uint32 load(void *ptr) { return ntohl(*((uint32 *)ptr)); } + static inline void store(void *ptr, uint32 val) { *((uint32 *)ptr) = htonl(val); } +}; + +// Scalar data member wrapper (specialise for pointer member types?) +template< class type, class public_type > +class nw_scalar_member_helper { + uint8 _pad[sizeof(type)]; +public: + operator public_type () const { + return (public_type)(uintptr)nw_memory_helper::load((void *)this); + } + public_type operator -> () const { + return this->operator public_type (); + } + nw_scalar_member_helper & operator = (public_type val) { + nw_memory_helper::store((void *)this, (type)(uintptr)val); + return *this; + } + nw_scalar_member_helper & operator += (int val) { + *this = *this + val; + return *this; + } + nw_scalar_member_helper & operator -= (int val) { + *this = *this - val; + return *this; + } + nw_scalar_member_helper & operator &= (int val) { + *this = *this & val; + return *this; + } + nw_scalar_member_helper & operator |= (int val) { + *this = *this | val; + return *this; + } +}; + +// Predefined member types +typedef nw_scalar_member_helper nw_int8; +typedef nw_scalar_member_helper nw_int16; +typedef nw_scalar_member_helper nw_int32; +typedef nw_scalar_member_helper nw_uint8; +typedef nw_scalar_member_helper nw_uint16; +typedef nw_scalar_member_helper nw_uint32; +typedef nw_scalar_member_helper nw_bool; +typedef nw_scalar_member_helper nw_uint8_p; +typedef nw_scalar_member_helper nw_void_p; +typedef nw_scalar_member_helper nw_datab_p; +typedef nw_scalar_member_helper nw_msgb_p; +typedef nw_scalar_member_helper nw_queue_p; +typedef nw_scalar_member_helper nw_multicast_node_p; +typedef nw_scalar_member_helper nw_DLPIStream_p; + +#endif + + +/* + * Structures + */ + +// Data block +struct datab { + nw_datab_p db_freep; + nw_uint8_p db_base; + nw_uint8_p db_lim; + nw_uint8 db_ref; + nw_uint8 db_type; + // ... +}; + +// Message block +struct msgb { + nw_msgb_p b_next; + nw_msgb_p b_prev; + nw_msgb_p b_cont; + nw_uint8_p b_rptr; + nw_uint8_p b_wptr; + nw_datab_p b_datap; + // ... +}; + +// Queue (full structure required because of size) +struct queue { + nw_void_p q_qinfo; + nw_msgb_p q_first; + nw_msgb_p q_last; + nw_queue_p q_next; + nw_queue_p q_link; + nw_DLPIStream_p q_ptr; + nw_uint32 q_count; + nw_int32 q_minpsz; + nw_int32 q_maxpsz; + nw_uint32 q_hiwat; + nw_uint32 q_lowat; + nw_void_p q_bandp; + nw_uint16 q_flag; + nw_uint8 q_nband; + uint8 _q_pad1[1]; + nw_void_p q_osx; + nw_queue_p q_ffcp; + nw_queue_p q_bfcp; +}; +typedef struct queue queue_t; + +// M_IOCTL parameters +struct iocblk { + nw_int32 ioc_cmd; + nw_void_p ioc_cr; + nw_uint32 ioc_id; + nw_uint32 ioc_count; + nw_int32 ioc_error; + nw_int32 ioc_rval; + int32 _ioc_filler[4]; +}; + +// Priority specification +struct dl_priority_t { + nw_int32 dl_min, dl_max; +}; + +// DPLI primitives +struct dl_info_req_t { + nw_uint32 dl_primitive; // DL_INFO_REQ +}; + +struct dl_info_ack_t { + nw_uint32 dl_primitive; // DL_INFO_ACK + nw_uint32 dl_max_sdu; + nw_uint32 dl_min_sdu; + nw_uint32 dl_addr_length; + nw_uint32 dl_mac_type; + nw_uint32 dl_reserved; + nw_uint32 dl_current_state; + nw_int32 dl_sap_length; + nw_uint32 dl_service_mode; + nw_uint32 dl_qos_length; + nw_uint32 dl_qos_offset; + nw_uint32 dl_qos_range_length; + nw_uint32 dl_qos_range_offset; + nw_uint32 dl_provider_style; + nw_uint32 dl_addr_offset; + nw_uint32 dl_version; + nw_uint32 dl_brdcst_addr_length; + nw_uint32 dl_brdcst_addr_offset; + nw_uint32 dl_growth; +}; + +struct dl_bind_req_t { + nw_uint32 dl_primitive; // DL_BIND_REQ + nw_uint32 dl_sap; + nw_uint32 dl_max_conind; + nw_uint16 dl_service_mode; + nw_uint16 dl_conn_mgmt; + nw_uint32 dl_xidtest_flg; +}; + +struct dl_bind_ack_t { + nw_uint32 dl_primitive; // DL_BIND_ACK + nw_uint32 dl_sap; + nw_uint32 dl_addr_length; + nw_uint32 dl_addr_offset; + nw_uint32 dl_max_conind; + nw_uint32 dl_xidtest_flg; +}; + +struct dl_error_ack_t { + nw_uint32 dl_primitive; // DL_ERROR_ACK + nw_uint32 dl_error_primitive; + nw_uint32 dl_errno; + nw_uint32 dl_unix_errno; +}; + +struct dl_ok_ack_t { + nw_uint32 dl_primitive; // DL_ERROR_ACK + nw_uint32 dl_correct_primitive; +}; + +struct dl_unitdata_req_t { + nw_uint32 dl_primitive; // DL_UNITDATA_REQ + nw_uint32 dl_dest_addr_length; + nw_uint32 dl_dest_addr_offset; + dl_priority_t dl_priority; +}; + +struct dl_unitdata_ind_t { + nw_uint32 dl_primitive; // DL_UNITDATA_IND + nw_uint32 dl_dest_addr_length; + nw_uint32 dl_dest_addr_offset; + nw_uint32 dl_src_addr_length; + nw_uint32 dl_src_addr_offset; + nw_uint32 dl_group_address; +}; + +struct dl_uderror_ind_t { + nw_uint32 dl_primitive; // DL_UDERROR_IND + nw_uint32 dl_dest_addr_length; + nw_uint32 dl_dest_addr_offset; + nw_uint32 dl_unix_errno; + nw_uint32 dl_errno; +}; + +struct dl_subs_bind_req_t { + nw_uint32 dl_primitive; // DL_SUBS_BIND_REQ + nw_uint32 dl_subs_sap_offset; + nw_uint32 dl_subs_sap_length; + nw_uint32 dl_subs_bind_class; +}; + +struct dl_subs_bind_ack_t { + nw_uint32 dl_primitive; // DL_SUBS_BIND_ACK + nw_uint32 dl_subs_sap_offset; + nw_uint32 dl_subs_sap_length; +}; + +struct dl_subs_unbind_req_t { + nw_uint32 dl_primitive; // DL_SUBS_UNBIND_REQ + nw_uint32 dl_subs_sap_offset; + nw_uint32 dl_subs_sap_length; +}; + +struct dl_enabmulti_req_t { + nw_uint32 dl_primitive; // DL_ENABMULTI_REQ + nw_uint32 dl_addr_length; + nw_uint32 dl_addr_offset; +}; + +struct dl_disabmulti_req_t { + nw_uint32 dl_primitive; // DL_DISABMULTI_REQ + nw_uint32 dl_addr_length; + nw_uint32 dl_addr_offset; +}; + +struct dl_phys_addr_req_t { + nw_uint32 dl_primitive; // DL_PHYS_ADDR_REQ + nw_uint32 dl_addr_type; +}; + +struct dl_phys_addr_ack_t { + nw_uint32 dl_primitive; // DL_PHYS_ADDR_ACK + nw_uint32 dl_addr_length; + nw_uint32 dl_addr_offset; +}; + +// Parameters for I_OTSetRawMode/kOTSetRecvMode ioctl() +struct dl_recv_control_t { + nw_uint32 dl_primitive; + nw_uint32 dl_flags; + nw_uint32 dl_truncation_length; +}; + +union DL_primitives { + nw_uint32 dl_primitive; + dl_info_req_t info_req; + dl_info_ack_t info_ack; + dl_bind_req_t bind_req; + dl_bind_ack_t bind_ack; + dl_error_ack_t error_ack; + dl_ok_ack_t ok_ack; + dl_unitdata_req_t unitdata_req; + dl_unitdata_ind_t unitdata_ind; + dl_uderror_ind_t uderror_ind; + dl_subs_bind_req_t subs_bind_req; + dl_subs_bind_ack_t subs_bind_ack; + dl_subs_unbind_req_t subs_unbind_req; + dl_enabmulti_req_t enabmulti_req; + dl_disabmulti_req_t disabmulti_req; + dl_phys_addr_req_t phys_addr_req; + dl_phys_addr_ack_t phys_addr_ack; +}; + +#ifdef PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif + +// Packet headers +struct EnetPacketHeader { + uint8 fDestAddr[6]; + uint8 fSourceAddr[6]; + nw_uint16 fProto; +} PACKED__; + +struct T8022Header { + uint8 fDSAP; + uint8 fSSAP; + uint8 fCtrl; +} PACKED__; + +struct T8022SNAPHeader { + uint8 fDSAP; + uint8 fSSAP; + uint8 fCtrl; + uint8 fSNAP[k8022SNAPLength]; +} PACKED__; + +struct T8022FullPacketHeader { + EnetPacketHeader fEnetPart; + T8022SNAPHeader f8022Part; +} PACKED__; + +struct T8022AddressStruct { + uint8 fHWAddr[6]; + nw_uint16 fSAP; + uint8 fSNAP[k8022SNAPLength]; +} PACKED__; + +#ifdef PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + +#endif diff --git a/SheepShaver/src/EthernetDriver/ethernet.ndrv b/SheepShaver/src/EthernetDriver/ethernet.ndrv new file mode 100644 index 00000000..857c765e Binary files /dev/null and b/SheepShaver/src/EthernetDriver/ethernet.ndrv differ diff --git a/SheepShaver/src/EthernetDriver/macos_util.cpp b/SheepShaver/src/EthernetDriver/macos_util.cpp new file mode 100644 index 00000000..0cf05e35 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/macos_util.cpp @@ -0,0 +1 @@ +#include "sysdeps.h" #include "macos_util.h" #include "xlowmem.h" #include #include #include #define DEBUG 0 #include "debug.h" /* * Find symbol in shared library (using CFM) * lib and sym must be Pascal strings! */ typedef OpaqueCFragConnectionID * ConnectionID; typedef unsigned char SymClass; static uint32 FindLibSymbol(Str63 lib, Str255 sym) { ConnectionID conn_id = 0; Ptr main_addr = NULL; Str255 err = ""; Ptr sym_addr = NULL; SymClass sym_class = 0; int16 res; res = GetSharedLibrary(lib, FOURCC('p','w','p','c'), 1, &conn_id, &main_addr, err); D(bug(" GetSharedLibrary: ret %d, connection ID %ld, main %p\n", res, conn_id, main_addr)); if (res) return NULL; res = FindSymbol(conn_id, sym, &sym_addr, &sym_class); D(bug(" FindSymbol: ret %d, sym_addr %p, sym_class %ld\n", res, sym_addr, sym_class));//!!?? CloseConnection(&conn_id); if (res) return NULL; else return (uint32)sym_addr; } uint32 FindLibSymbol(char *lib_name, char *sym_name) { Str63 lib; memcpy(&lib[0], lib_name, lib_name[0]+1); Str255 sym; memcpy(&sym[0], sym_name, sym_name[0]+1); return FindLibSymbol(lib, sym); } /* * Memory allocators in MacOS system heap zone */ uint32 Mac_sysalloc(uint32 size) { return (uint32)NewPtrSys(size); } void Mac_sysfree(uint32 addr) { DisposePtr((char *)addr); } /* * Glue for calling MacOS routines */ #define prolog ;\ mflr r0 ;\ stw r0,8(r1) ;\ stw r2,12(r1) ;\ stwu r1,-64(r1) ;\ lwz r0,0(r3) ;\ lwz r2,4(r3) ;\ mtctr r0 #define epilog ;\ bctrl ;\ lwz r0,64+8(r1) ;\ lwz r2,64+12(r1);\ mtlr r0 ;\ addi r1,r1,64 ;\ blr asm uint32 call_macos(register uint32 tvect) { prolog epilog } asm uint32 call_macos1(register uint32 tvect, register uint32 arg1) { prolog mr r3,r4 epilog } asm uint32 call_macos2(register uint32 tvect, register uint32 arg1, register uint32 arg2) { prolog mr r3,r4 mr r4,r5 epilog } asm uint32 call_macos3(register uint32 tvect, register uint32 arg1, register uint32 arg2, register uint32 arg3) { prolog mr r3,r4 mr r4,r5 mr r5,r6 epilog } asm uint32 call_macos4(register uint32 tvect, register uint32 arg1, register uint32 arg2, register uint32 arg3, register uint32 arg4) { prolog mr r3,r4 mr r4,r5 mr r5,r6 mr r6,r7 epilog } asm uint32 call_macos5(register uint32 tvect, register uint32 arg1, register uint32 arg2, register uint32 arg3, register uint32 arg4, register uint32 arg5) { prolog mr r3,r4 mr r4,r5 mr r5,r6 mr r6,r7 mr r7,r8 epilog } asm uint32 call_macos6(register uint32 tvect, register uint32 arg1, register uint32 arg2, register uint32 arg3, register uint32 arg4, register uint32 arg5, register uint32 arg6) { prolog mr r3,r4 mr r4,r5 mr r5,r6 mr r6,r7 mr r7,r8 mr r8,r9 epilog } asm uint32 call_macos7(register uint32 tvect, register uint32 arg1, register uint32 arg2, register uint32 arg3, register uint32 arg4, register uint32 arg5, register uint32 arg6, register uint32 arg7) { prolog mr r3,r4 mr r4,r5 mr r5,r6 mr r6,r7 mr r7,r8 mr r8,r9 mr r9,r10 epilog } /* * Some standard C library implementations */ extern "C" void *memcpy(void *dest, const void *src, size_t n); void *memcpy(void *dest, const void *src, size_t n) { BlockMoveData(src, dest, n); return dest; } extern "C" void *memset(void *s, int c, size_t n); void *memset(void *s, int c, size_t n) { if (c == 0) BlockZero(s, n); else { char *p = (char *)s; n++; while (--n) *p++ = c; } return s; } extern "C" int memcmp(const void *s1, const void *s2, size_t n); int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *d = (const unsigned char *)s1; const unsigned char *s = (const unsigned char *)s2; n++; while (--n) { int r; if (r = (*d - *s)) return r; ++d; ++s; } return 0; } extern "C" int printf(const char *format, ...); int printf(const char *format, ...) { return 0; } \ No newline at end of file diff --git a/SheepShaver/src/EthernetDriver/macos_util.h b/SheepShaver/src/EthernetDriver/macos_util.h new file mode 100644 index 00000000..20cc5ec4 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/macos_util.h @@ -0,0 +1 @@ +#ifndef MACOS_UTIL_H #define MACOS_UTIL_H // Macro for calling MacOS routines #define CallMacOS(type, tvect) call_macos((uintptr)tvect) #define CallMacOS1(type, tvect, arg1) call_macos1((uintptr)tvect, (uintptr)arg1) #define CallMacOS2(type, tvect, arg1, arg2) call_macos2((uintptr)tvect, (uintptr)arg1, (uintptr)arg2) #define CallMacOS3(type, tvect, arg1, arg2, arg3) call_macos3((uintptr)tvect, (uintptr)arg1, (uintptr)arg2, (uintptr)arg3) #define CallMacOS4(type, tvect, arg1, arg2, arg3, arg4) call_macos4((uintptr)tvect, (uintptr)arg1, (uintptr)arg2, (uintptr)arg3, (uintptr)arg4) #define CallMacOS5(type, tvect, arg1, arg2, arg3, arg4, arg5) call_macos5((uintptr)tvect, (uintptr)arg1, (uintptr)arg2, (uintptr)arg3, (uintptr)arg4, (uintptr)arg5) #define CallMacOS6(type, tvect, arg1, arg2, arg3, arg4, arg5, arg6) call_macos6((uintptr)tvect, (uintptr)arg1, (uintptr)arg2, (uintptr)arg3, (uintptr)arg4, (uintptr)arg5, (uintptr)arg6) #define CallMacOS7(type, tvect, arg1, arg2, arg3, arg4, arg5, arg6, arg7) call_macos7((uintptr)tvect, (uintptr)arg1, (uintptr)arg2, (uintptr)arg3, (uintptr)arg4, (uintptr)arg5, (uintptr)arg6, (uintptr)arg7) #ifdef __cplusplus extern "C" { #endif extern uint32 call_macos(uint32 tvect); extern uint32 call_macos1(uint32 tvect, uint32 arg1); extern uint32 call_macos2(uint32 tvect, uint32 arg1, uint32 arg2); extern uint32 call_macos3(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3); extern uint32 call_macos4(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4); extern uint32 call_macos5(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5); extern uint32 call_macos6(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6); extern uint32 call_macos7(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6, uint32 arg7); #ifdef __cplusplus } #endif // Construct four-character-code from string #define FOURCC(a,b,c,d) (((uint32)(a) << 24) | ((uint32)(b) << 16) | ((uint32)(c) << 8) | (uint32)(d)) extern uint32 FindLibSymbol(char *lib, char *sym); // Find symbol in shared library extern uint32 Mac_sysalloc(uint32 size); // Allocate block in MacOS system heap zone extern void Mac_sysfree(uint32 addr); // Release block occupied by the nonrelocatable block p #endif /* MACOS_UTIL_H */ \ No newline at end of file diff --git a/SheepShaver/src/EthernetDriver/sysdeps.h b/SheepShaver/src/EthernetDriver/sysdeps.h new file mode 100644 index 00000000..ffd90e06 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/sysdeps.h @@ -0,0 +1 @@ +#ifndef SYSDEPS_H #define SYSDEPS_H #include #define SIZEOF_VOID_P 4 #define WORDS_BIGENDIAN 1 #define REAL_ADDRESSING 1 /* Define to build the Ethernet driver completly in MacOS space */ #define BUILD_ETHER_FULL_DRIVER 1 #define ntohl(x) ((uint32)(x)) #define ntohs(x) ((uint16)(x)) #define assert(expr) typedef int bool; typedef signed char int8; typedef signed short int16; typedef signed int int32; typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef uint32 uintptr; #endif /* SYSDEPS_H */ \ No newline at end of file diff --git a/SheepShaver/src/EthernetDriver/xlowmem.h b/SheepShaver/src/EthernetDriver/xlowmem.h new file mode 100644 index 00000000..a37282b3 --- /dev/null +++ b/SheepShaver/src/EthernetDriver/xlowmem.h @@ -0,0 +1,64 @@ +/* + * xlowmem.h - Definitions for extra Low Memory globals (0x2800..) + * + * SheepShaver (C) 1997-2005 Christian Bauer and Marc Hellwig + * + * 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 + */ + +#ifndef XLOWMEM_H +#define XLOWMEM_H + +// Modes for XLM_RUN_MODE +#define MODE_68K 0 // 68k emulator active +#define MODE_NATIVE 1 // Switched to native mode +#define MODE_EMUL_OP 2 // 68k emulator active, within EMUL_OP routine + +#define XLM_SIGNATURE 0x2800 // SheepShaver signature +#define XLM_KERNEL_DATA 0x2804 // Pointer to Kernel Data +#define XLM_TOC 0x2808 // TOC pointer of emulator +#define XLM_SHEEP_OBJ 0x280c // Pointer to SheepShaver object +#define XLM_RUN_MODE 0x2810 // Current run mode, see enum above +#define XLM_68K_R25 0x2814 // Contents of the 68k emulator's r25 (which contains the interrupt level), saved upon entering EMUL_OP mode, used by Execute68k() and the USR1 signal handler +#define XLM_IRQ_NEST 0x2818 // Interrupt disable nesting counter (>0: disabled) +#define XLM_PVR 0x281c // Theoretical PVR +#define XLM_BUS_CLOCK 0x2820 // Bus clock speed in Hz (for DriverServicesLib patch) +#define XLM_EMUL_RETURN_PROC 0x2824 // Pointer to EMUL_RETURN routine +#define XLM_EXEC_RETURN_PROC 0x2828 // Pointer to EXEC_RETURN routine +#define XLM_EMUL_OP_PROC 0x282c // Pointer to EMUL_OP routine +#define XLM_EMUL_RETURN_STACK 0x2830 // Stack pointer for EMUL_RETURN +#define XLM_RES_LIB_TOC 0x2834 // TOC pointer of Resources library +#define XLM_GET_RESOURCE 0x2838 // Pointer to native GetResource() routine +#define XLM_GET_1_RESOURCE 0x283c // Pointer to native Get1Resource() routine +#define XLM_GET_IND_RESOURCE 0x2840 // Pointer to native GetIndResource() routine +#define XLM_GET_1_IND_RESOURCE 0x2844 // Pointer to native Get1IndResource() routine +#define XLM_R_GET_RESOURCE 0x2848 // Pointer to native RGetResource() routine +#define XLM_EXEC_RETURN_OPCODE 0x284c // EXEC_RETURN opcode for Execute68k() +#define XLM_ZERO_PAGE 0x2850 // Pointer to read-only page with all bits set to 0 +#define XLM_R13 0x2854 // Pointer to .sdata section (Linux) + +#define XLM_ETHER_AO_GET_HWADDR 0x28b0 // Pointer to ethernet A0_get_ethernet_address() function +#define XLM_ETHER_AO_ADD_MULTI 0x28b4 // Pointer to ethernet A0_enable_multicast() function +#define XLM_ETHER_AO_DEL_MULTI 0x28b8 // Pointer to ethernet A0_disable_multicast() function +#define XLM_ETHER_AO_SEND_PACKET 0x28bc // Pointer to ethernet A0_transmit_packet() function +#define XLM_ETHER_INIT 0x28c0 // Pointer to ethernet InitStreamModule() function +#define XLM_ETHER_TERM 0x28c4 // Pointer to ethernet TerminateStreamModule() function +#define XLM_ETHER_OPEN 0x28c8 // Pointer to ethernet ether_open() function +#define XLM_ETHER_CLOSE 0x28cc // Pointer to ethernet ether_close() function +#define XLM_ETHER_WPUT 0x28d0 // Pointer to ethernet ether_wput() function +#define XLM_ETHER_RSRV 0x28d4 // Pointer to ethernet ether_rsrv() function +#define XLM_VIDEO_DOIO 0x28d8 // Pointer to video DoDriverIO() function + +#endif