Switched Driver to baxnet-structure

This commit is contained in:
MHeinrichs
2018-06-09 21:41:07 +02:00
parent b714ded89e
commit 95bc477444
95 changed files with 31505 additions and 17964 deletions
+180
View File
@@ -0,0 +1,180 @@
###############################################################################
#
# Common.mk
#
# author: Henryk Richter <henryk.richter@gmx.net>
#
# note: when switching between different hardware targets, don`t forget
# "make clean" in between
#
# tools required:
# GNU Make, either VBCC or GCC for AmigaOS/68k, VASM
# recent sana2.h , e.g. from RoadShow SDK
#
# porting:
# you might need to adapt the paths (prefix for Compiler/Includes)
# Some installations have ADE: instead of GG: on Amigaos
# Recent work on AmigaOS cross toolchains suggest /opt/m68k-amigaos/sys-include
# instead of the traditional os-include
#
###############################################################################
# debug = 1 will include string debugging for terminal/sushi/sashimi
debug = 0
# compiler_vcc = 1 will trigger VBCC, else GCC
compiler_vcc = 0
###############################################################################
# prefix for system includes (ASM)
#
###############################################################################
PREFX = /opt/amigaos-68k/
PREFX = gg:
SYSINC = $(PREFX)os-include
###############################################################################
#
# compiler executables (choice of gcc or vbcc)
#
###############################################################################
ifeq ($(compiler_vcc),1)
# VBCC (use explicit vlink line for LINK= if complaints about R_PC happen)
CCX = vc
LINK = vlink -bamigahunk -x -s -mrel -Cvbcc -Bstatic -nostdlib #-Rshort
#LINK = $(CCX) -nostdlib
CFLAGS = -Os -+ -sc -c99 -cpu=68020
else
# GCC
CCX = m68k-amigaos-gcc
LINK = $(CCX) -nostartfiles -s
CFLAGS = -O3 -s -m68020 -Wall -noixemul -mregparm=4 -fomit-frame-pointer -msoft-float -noixemul
endif
VASM = vasmm68k_mot
VASMFORMAT = -m68020 -Fhunk -nowarn=2064 -quiet -I$(SYSINC)
# unused here
#HOST = $(shell uname)
###############################################################################
#
# paths to the local includes
#
###############################################################################
IPATH =
###############################################################################
#
# compile-level feature definitions
#
###############################################################################
ifeq ($(compiler_vcc),1)
# skip quotes with VCC, the AmigaOS native version doesn't like them
DEFINES += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
DEFINES += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME="$(DEVICEID)"
DEFINES += -DDEVICEEXTRA=$(DEVICEEXTRA)
ASMDEFS += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
#ASMDEFS += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME=$(DEVICEID)
DEFINES2 += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
DEFINES2 += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME="$(DEVICEID2)"
DEFINES2 += -DDEVICEEXTRA=$(DEVICEEXTRA)
ASMDEFS2 += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
else
DEFINES += -D"DEVICEVERSION=$(DEVICEVERSION)" -D"DEVICEREVISION=$(DEVICEREVISION)"
DEFINES += -D"DEVICEDATE=$(DEVICEDATE)" -D"DEVICENAME="$(DEVICEID)""
DEFINES += -D"DEVICEEXTRA=$(DEVICEEXTRA)"
ASMDEFS += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
#ASMDEFS += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME=$(DEVICEID)
DEFINES2 += -D"DEVICEVERSION=$(DEVICEVERSION)" -D"DEVICEREVISION=$(DEVICEREVISION)"
DEFINES2 += -D"DEVICEDATE=$(DEVICEDATE)" -D"DEVICENAME="$(DEVICEID2)""
DEFINES2 += -D"DEVICEEXTRA=$(DEVICEEXTRA)"
ASMDEFS2 += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
endif
###############################################################################
#
# debug
#
###############################################################################
ifeq ($(debug),1)
CFLAGS += -DDEBUG -g
LINKLIBS = -L$(PREFX)/lib -ldebug
endif
###############################################################################
#
# compiler flags and optimization levels
#
###############################################################################
CFLAGS += -I.
CFLAGS += -I$(SUBDIR)
LDFLAGS =
###############################################################################
#
# objects to build
#
###############################################################################
# ASM based alternative to deviceheader.o would be romtag.o
OBJECTS = deviceheader.o deviceinit.o device.o server.o $(SUBDIR)/hw.o
OBJECTS += $(ASMOBJECTS)
# used for secondary build
OBJECTS2 = $(patsubst %.o,%.2o,$(OBJECTS))
###############################################################################
#
# rules and commands
#
###############################################################################
all: $(DEVICEID) $(DEVICEID2)
clean:
rm -f $(OBJECTS) $(OBJECTS2)
rm -f $(DEVICEID) $(DEVICEID2)
# not for cross compile :-)
install: $(DEVICEID) $(DEVICEID2)
copy $(DEVICEID) $(DEVICEID2) DEVS:Networks
#sdnet: $(DEVICEID)
#expnet: $(DEVICEID2)
$(DEVICEID) : $(OBJECTS)
$(LINK) $(LDFLAGS) -o $@ $(OBJECTS) $(LINKLIBS) $(LINKOPTS)
# separate ruleset for each subdirectory, ./src overrides all other paths for priority
# of platform-optimized routines
%.o : %.c
$(CCX) -c $(CFLAGS) $(DEFINES) $(IPATH) $< -o $@
%.o : %.asm
${VASM} $(VASMFORMAT) $(ASMDEFS) -o $@ $<
# secondary ruleset (used for expnet, will be ignored if DEVICEID2 is empty)
$(DEVICEID2) : $(OBJECTS2)
$(LINK) $(LDFLAGS) -o $@ $(OBJECTS2) $(LINKLIBS)
%.2o : %.c
$(CCX) -c $(CFLAGS) $(DEFINES2) $(IPATH) $< -o $@
%.2o : %.asm
${VASM} $(VASMFORMAT) $(ASMDEFS2) -o $@ $<
+172
View File
@@ -0,0 +1,172 @@
###############################################################################
#
# Common.mk
#
# author: Henryk Richter <henryk.richter@gmx.net>
#
# note: when switching between different hardware targets, don`t forget
# "make clean" in between
#
# tools required:
# GNU Make, either VBCC or GCC for AmigaOS/68k, VASM
# recent sana2.h , e.g. from RoadShow SDK
#
# porting:
# you might need to adapt the paths (prefix for Compiler/Includes)
# Some installations have ADE: instead of GG: on Amigaos
# Recent work on AmigaOS cross toolchains suggest /opt/m68k-amigaos/sys-include
# instead of the traditional os-include
#
###############################################################################
debug = 0
compiler_vcc = 1
###############################################################################
# prefix for system includes (ASM)
#
###############################################################################
PREFX = /opt/amigaos-68k/
PREFX = gg:
SYSINC = $(PREFX)os-include
###############################################################################
#
# compiler executables (choice of gcc or vbcc)
#
###############################################################################
ifeq ($(compiler_vcc),1)
# VBCC (use explicit vlink line for LINK= if complaints about R_PC happen)
CCX = vc
LINK = vlink -bamigahunk -x -s -mrel -Cvbcc -Bstatic -nostdlib #-Rshort
#LINK = $(CCX) -nostdlib
CFLAGS = -Os -+ -sc -c99 -cpu=68020
else
# GCC
CCX = m68k-amigaos-gcc
LINK = $(CCX) -nostartfiles -s
CFLAGS = -O3 -s -m68060 -Wall -noixemul -mregparm=4 -fomit-frame-pointer -msoft-float -noixemul
endif
VASM = vasmm68k_mot
VASMFORMAT = -m68020 -Fhunk -nowarn=2064 -quiet -I$(SYSINC)
# unused here
#HOST = $(shell uname)
###############################################################################
#
# paths to the local includes
#
###############################################################################
IPATH =
###############################################################################
#
# compile-level feature definitions
#
###############################################################################
ifeq ($(compiler_vcc),1)
DEFINES += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
DEFINES += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME="$(DEVICEID)"
DEFINES += -DDEVICEEXTRA=$(DEVICEEXTRA)
ASMDEFS += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
#ASMDEFS += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME=$(DEVICEID)
DEFINES2 += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
DEFINES2 += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME="$(DEVICEID2)"
DEFINES2 += -DDEVICEEXTRA=$(DEVICEEXTRA)
ASMDEFS2 += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
else
DEFINES += -D"DEVICEVERSION=$(DEVICEVERSION)" -D"DEVICEREVISION=$(DEVICEREVISION)"
DEFINES += -D"DEVICEDATE=$(DEVICEDATE)" -D"DEVICENAME="$(DEVICEID)""
DEFINES += -D"DEVICEEXTRA=$(DEVICEEXTRA)"
ASMDEFS += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
#ASMDEFS += -DDEVICEDATE=$(DEVICEDATE) -DDEVICENAME=$(DEVICEID)
DEFINES2 += -D"DEVICEVERSION=$(DEVICEVERSION)" -D"DEVICEREVISION=$(DEVICEREVISION)"
DEFINES2 += -D"DEVICEDATE=$(DEVICEDATE)" -D"DEVICENAME="$(DEVICEID2)""
DEFINES2 += -D"DEVICEEXTRA=$(DEVICEEXTRA)"
ASMDEFS2 += -DDEVICEVERSION=$(DEVICEVERSION) -DDEVICEREVISION=$(DEVICEREVISION)
endif
###############################################################################
#
# debug
#
###############################################################################
ifeq ($(debug),1)
CFLAGS += -DDEBUG -g
LINKLIBS = -L$(PREFX)/lib -ldebug
endif
###############################################################################
#
# compiler flags and optimization levels
#
###############################################################################
CFLAGS += -I.
CFLAGS += -I$(SUBDIR)
LDFLAGS =
###############################################################################
#
# objects to build
#
###############################################################################
# ASM based alternative to deviceheader.o would be romtag.o
OBJECTS = deviceheader.o deviceinit.o device.o server.o $(SUBDIR)/hw.o
OBJECTS += $(ASMOBJECTS)
# used for secondary build
OBJECTS2 = $(patsubst %.o,%.2o,$(OBJECTS))
###############################################################################
#
# rules and commands
#
###############################################################################
all: $(DEVICEID) $(DEVICEID2)
clean:
rm -f $(OBJECTS) $(OBJECTS2)
rm -f $(DEVICEID) $(DEVICEID2)
#sdnet: $(DEVICEID)
#expnet: $(DEVICEID2)
$(DEVICEID) : $(OBJECTS)
$(LINK) $(LDFLAGS) -o $@ $(OBJECTS) $(LINKLIBS) $(LINKOPTS)
# separate ruleset for each subdirectory, ./src overrides all other paths for priority
# of platform-optimized routines
%.o : %.c
$(CCX) -c $(CFLAGS) $(DEFINES) $(IPATH) $< -o $@
%.o : %.asm
${VASM} $(VASMFORMAT) $(ASMDEFS) -o $@ $<
# secondary ruleset (used for expnet, will be ignored if DEVICEID2 is empty)
$(DEVICEID2) : $(OBJECTS2)
$(LINK) $(LDFLAGS) -o $@ $(OBJECTS2) $(LINKLIBS)
%.2o : %.c
$(CCX) -c $(CFLAGS) $(DEFINES2) $(IPATH) $< -o $@
%.2o : %.asm
${VASM} $(VASMFORMAT) $(ASMDEFS2) -o $@ $<
+39
View File
@@ -0,0 +1,39 @@
###############################################################################
#
# VAMPIREGFX makefile for vbcc
#
# author: Henryk Richter <henryk.richter@gmx.net>
#
# concept:
#
# tools required:
# - vbcc, defaulting to m68k-amigaos
# - vlink
# - vasm
#
# porting:
#
###############################################################################
###############################################################################
# Name, version
#
###############################################################################
DEVICEVERSION=1
DEVICEREVISION=91
DEVICEDATE=06.06.2018
ASMOBJECTS = $(SUBDIR)/enc624j6l.o $(SUBDIR)/intervaltimer.o
SUBDIR=enc624j6net
DEVICEID=enc624j6net.device
DEFINES = # -DPROTO_V2EXPNET
ASMDEFS = # -DPROTO_V2EXPNET
###############################################################################
# import generic ruleset
#
###############################################################################
include Common.mk
+47
View File
@@ -0,0 +1,47 @@
###############################################################################
#
# SDNet and V2EXPETH build instructions
#
# author: Henryk Richter <henryk.richter@gmx.net>
#
# concept:
#
# tools required:
# - vbcc or m68k-amigaos-gcc (Default: gcc)
# - vlink or GNU ld
# - vasm (vasmm68k_mot)
# - system headers (default: /opt/amigaos-68k/os-include, see Common.mk)
#
# porting:
#
###############################################################################
###############################################################################
# Name, version
#
###############################################################################
DEVICEVERSION=1
DEVICEREVISION=91
DEVICEDATE=06.06.2018
DEVICEEXTRA=Beta
ASMOBJECTS = $(SUBDIR)/vampuuid.o $(SUBDIR)/intervaltimer.o $(SUBDIR)/enc28j60.o $(SUBDIR)/enc28j60l.o
SUBDIR=sdnet
# two flavours of the same code base may be build with this setup
# leave DEVICEID2 empty to disable second build
DEVICEID=sdnet.device
DEFINES = # -DPROTO_V2EXPNET
ASMDEFS = # -DPROTO_V2EXPNET
DEVICEID2=v2expeth.device
DEFINES2 = -DPROTO_V2EXPNET
ASMDEFS2 = -DPROTO_V2EXPNET
###############################################################################
# import generic ruleset
#
###############################################################################
include Common.mk
+64
View File
@@ -0,0 +1,64 @@
/*
compiler.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Interface macros for ASM subroutines for VBCC, GCC and SAS/C
syntax:
ASM SAVEDS int some_asm_subroutine(
ASMR(d3) unsigned int some_data ASMREG(d3),
ASMR(a0) unsigned char *some_address ASMREG(a0)
);
Reason for the double spec of An/Dn: some compilers (SAS) require the register
on the left hand side, gcc on the right hand side. I wanted to avoid a big macro
for the whole argument and moved the stuff into two macros per argument.
*/
#ifndef _INC_ASMINTERFACE_H
#define _INC_ASMINTERFACE_H
#ifdef __SASC
#define ASM __asm
#define ASMR(x) register __ ## x
#define ASMREG(x)
#define SAVEDS __saveds
#define STRUCTOFFSET OFFSET /* exec/initializers.h */
#define INLINE __inline static
#else /* __SASC */
#ifdef __GNUC__
#define ASM
#define ASMR(x) register
#define ASMREG(x) __asm("" #x "")
#define SAVEDS __saveds
#define STRUCTOFFSET OFFSET /* exec/initializers.h */
#define INLINE static inline
#else /* __GNUC__ */
#ifdef __VBCC__
#define ASM
#define ASMR(x) __reg("" #x "")
#define ASMREG(x)
#define SAVEDS __saveds
#define STRUCTOFFSET(_a_,_b_) offsetof(struct _a_, _b_) /* stddef.h */
#include <stddef.h>
/* sorry, I ran into some issues inlining stuff with VBCC, disabling it */
#define INLINE
#else /* __VBCC__ */
#error "Compiler not supported yet in asminterface.h"
#endif /* __VBCC__ */
#endif /* __GNUC__ */
#endif /* __SASC */
#endif /* _INC_ASMINTERFACE_H */
+25
View File
@@ -0,0 +1,25 @@
/*
debug.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Debugging Macros
*/
#ifndef _INC_DEBUG_H
#define _INC_DEBUG_H
#ifdef DEBUG
extern void KPrintF(char *, ...), KGetChar(void);
#define D(_x_) do { KPrintF("%s:%ld:",__FILE__,__LINE__); KPrintF _x_; } while(0)
#else /* DEBUG */
#define D(x)
#endif /* DEBUG */
#endif /* _INC_DEBUG_H */
+995
View File
@@ -0,0 +1,995 @@
/*
device.c
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Device Functions
the device core consists of three files, to be linked
in the following order
- deviceheader.c - ROMTAG
- deviceinit.c - strings and init tables
- device.c - functions
I've had seen quite creative ways the different compilers
handled the order of objects in the linked result and that's
why I've chosen to break it down in the way you see here.
The request handling and hardware interaction is carried out
in server.c as a separate process. Interrupts are forwarded
as signals to server.c as well (depending on target hardware).
*/
#define DEVICE_MAIN
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <exec/lists.h>
#include "device.h"
#include "hw.h"
#include "macros.h"
ASM SAVEDS struct Device *DevInit( ASMR(d0) DEVBASEP ASMREG(d0),
ASMR(a0) BPTR seglist ASMREG(a0),
ASMR(a6) struct Library *_SysBase ASMREG(a6) )
{
UBYTE*p;
ULONG i;
LONG ok;
p = ((UBYTE*)db) + sizeof(struct Library);
i = sizeof(DEVBASETYPE)-sizeof(struct Library);
while( i-- )
*p++ = 0;
db->db_SysBase = _SysBase;
db->db_SegList = seglist;
db->db_Flags = 0;
dbNewList( &db->db_BufferManagement );
for(i=0 ; i < MAX_UNITS ; i++ )
{
dbNewList( (struct List*)&db->db_Units[i].du_ReadTypes );
dbNewList( (struct List*)&db->db_Units[i].du_ReadOrphans );
dbNewList( (struct List*)&db->db_Units[i].du_WriteQueue );
dbNewList( (struct List*)&db->db_Units[i].du_EventQueue );
InitSemaphore( &db->db_Units[i].du_Sem );
db->db_Units[i].du_MTU = DEF_MTU;
db->db_Units[i].du_BitPerSec = DEF_BPS;
}
ok = 0;
if( (DOSBase = OpenLibrary("dos.library", 36)) )
{
if( (UtilityBase = OpenLibrary("utility.library", 37)) )
{
/* Search HW */
ok = hw_Find_Boards( db );
if( ok > MAX_UNITS )
ok = MAX_UNITS;
if( !ok )
{
D(("No Hardware\n"));
CloseLibrary(DOSBase);
CloseLibrary(UtilityBase);
}
db->db_NBoards = ok;
}
else
{
D(("No Utility\n"));
CloseLibrary(DOSBase);
}
}
else
{
D(("No DOS\n"));
}
/* no hardware found, reject init */
return (ok > 0) ? (struct Device*)db : (0);
}
ASM SAVEDS LONG DevOpen( ASMR(a1) struct IOSana2Req *ioreq ASMREG(a1),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(d1) ULONG flags ASMREG(d1),
ASMR(a6) DEVBASEP ASMREG(a6) )
{
LONG ok,ret = IOERR_OPENFAIL;
D(("DevOpen for %ld\n",unit));
db->db_Lib.lib_OpenCnt++; /* avoid Expunge, see below for separate "unit" open count */
db->db_ID++; /* opener ID (always >0) */
if( unit < db->db_NBoards )
{
ObtainSemaphore( &db->db_Units[unit].du_Sem ); /* lock current board */
ok = 1;
if( (db->db_Units[unit].du_OpenCount > 0) &&
(flags & SANA2OPF_MINE) )
ok = 0;
if( (db->db_Units[unit].du_OpenCount > 0) &&
(flags & SANA2OPF_PROM) )
ok = 0;
if( db->db_Units[unit].du_Flags & DUF_EXCLUSIVE )
ok = 0;
if( ok )
ok = hw_AllocBoard( db, unit );
if( ok )
{
struct db_BufferManagement *dbm;
db->db_Units[unit].du_OpenCount++;
db->db_Units[unit].du_Flags &= ~DUF_EXCLUSIVE;
if( flags & SANA2OPF_MINE )
db->db_Units[unit].du_Flags |= DUF_EXCLUSIVE;
if( flags & SANA2OPF_PROM ) /* implies _MINE by spec */
db->db_Units[unit].du_Flags |= DUF_PROMISC;
hw_GetMacAddress( db, unit, &db->db_Units[unit].du_HWAddr[0] );
hw_GetMacAddress( db, unit, &db->db_Units[unit].du_CFGAddr[0] );
ok = 0;
dbm = (struct db_BufferManagement *)AllocMem( sizeof(struct db_BufferManagement),MEMF_ANY|MEMF_CLEAR);
if( dbm )
{
dbm->dbm_CopyToBuffer = (BMCALL)((void (*)())(GetTagData(S2_CopyToBuff, 0,
(struct TagItem *)ioreq->ios2_BufferManagement)));
dbm->dbm_CopyFromBuffer = (BMCALL)((void (*)())(GetTagData(S2_CopyFromBuff, 0,
(struct TagItem *)ioreq->ios2_BufferManagement)));
#if 0
dbm->dbm_CopyToBuffer32 = (0); /* DEBUG */
dbm->dbm_CopyFromBuffer32 = (0);
#else
dbm->dbm_CopyToBuffer32 = (BMCALL)((void (*)())(GetTagData(S2_CopyToBuff32, 0,
(struct TagItem *)ioreq->ios2_BufferManagement)));
dbm->dbm_CopyFromBuffer32 = (BMCALL)((void (*)())(GetTagData(S2_CopyFromBuff32, 0,
(struct TagItem *)ioreq->ios2_BufferManagement)));
#endif
if( !dbm->dbm_CopyToBuffer32 )
dbm->dbm_CopyToBuffer32 = dbm->dbm_CopyToBuffer;
if( !dbm->dbm_CopyFromBuffer32 )
dbm->dbm_CopyFromBuffer32 = dbm->dbm_CopyFromBuffer;
dbm->dbm_ID = db->db_ID;
ok = 1;
}
if( ok )
{
/* start server if necessary */
ok = dbStartServer( db, unit );
}
if( ok )
{
ret = 0;
AddTail((struct List *)&db->db_BufferManagement,(struct Node *)dbm);
db->db_Lib.lib_Flags &= ~LIBF_DELEXP;
ioreq->ios2_BufferManagement = (VOID *)dbm;
ioreq->ios2_Req.io_Error = 0;
ioreq->ios2_Req.io_Unit = (struct Unit *)unit;
ioreq->ios2_Req.io_Device = (struct Device *)db;
dbAddType( db, unit, 0x800 ); /* register IP by default */
}
else
{
db->db_Units[unit].du_OpenCount--;
db->db_Units[unit].du_Flags &= ~DUF_EXCLUSIVE;
hw_ReleaseBoard( db, unit );
if( dbm ) FreeMem( dbm, sizeof(struct db_BufferManagement) );
}
}
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
}
if( ret == IOERR_OPENFAIL )
{
ioreq->ios2_Req.io_Unit = (0);
ioreq->ios2_Req.io_Device = (0);
ioreq->ios2_Req.io_Error = ret;
db->db_Lib.lib_OpenCnt--;
}
ioreq->ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
return ret;
}
ASM SAVEDS BPTR DevClose( ASMR(a1) struct IOSana2Req *ioreq ASMREG(a1),
ASMR(a6) DEVBASEP ASMREG(a6) )
{
ULONG unit;
BPTR ret = (0);
D(("DevClose open count %ld\n",db->db_Lib.lib_OpenCnt));
/* request valid ? */
if( !ioreq )
return ret;
if( (unit = (ULONG)ioreq->ios2_Req.io_Unit) > db->db_NBoards)
return ret;
ObtainSemaphore( &db->db_Units[unit].du_Sem ); /* lock current board */
dbDeleteBuffers( db, ioreq ); /* buffer management and internal buffers */
db->db_Lib.lib_OpenCnt--;
if( db->db_Units[unit].du_OpenCount ) /* sanity */
db->db_Units[unit].du_OpenCount--;
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
/* give server time to offline down the hardware */
if( (db->db_ServerProc) &&
!(db->db_Units[unit].du_OpenCount) &&
(db->db_Units[unit].du_Flags & DUF_ONLINE )
)
{
Signal( (struct Task*)db->db_ServerProc, SIGBREAKF_CTRL_D);
Delay(1);
}
ObtainSemaphore( &db->db_Units[unit].du_Sem ); /* lock current board */
hw_ReleaseBoard( db, unit );
ioreq->ios2_Req.io_Device = (0);
ioreq->ios2_Req.io_Unit = (struct Unit *)(-1);
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
if (db->db_Lib.lib_Flags & LIBF_DELEXP)
ret = DevExpunge(db);
return ret;
}
ASM SAVEDS BPTR DevExpunge( ASMR(a6) DEVBASEP ASMREG(a6) )
{
BPTR seglist = db->db_SegList;
ULONG i;
struct SanaReadType *type;
if( db->db_Lib.lib_OpenCnt )
{
db->db_Lib.lib_Flags |= LIBF_DELEXP;
return (0);
}
Forbid();
Remove((struct Node*)db); /* remove device node from system */
Permit();
dbStopServer(db,0);
for( i=0 ; i < db->db_NBoards ; i++ )
{
while( (type = GetHead( &db->db_Units[i].du_ReadTypes ) ))
{
dbDeleteType( db, i, type->srt_Type );
}
}
/* last steps: libraries */
CloseLibrary(DOSBase);
CloseLibrary(UtilityBase);
FreeMem( ((BYTE*)db)-db->db_Lib.lib_NegSize,(ULONG)(db->db_Lib.lib_PosSize + db->db_Lib.lib_NegSize));
return seglist;
}
ASM SAVEDS VOID DevBeginIO( ASMR(a1) struct IOSana2Req *ioreq ASMREG(a1),
ASMR(a6) DEVBASEP ASMREG(a6) )
{
ULONG unit = (ULONG)ioreq->ios2_Req.io_Unit;
ioreq->ios2_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
/*ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;*/
D(("BeginIO command %ld unit %ld\n",(LONG)ioreq->ios2_Req.io_Command,unit));
switch( ioreq->ios2_Req.io_Command )
{
case CMD_READ:
if( !ioreq->ios2_BufferManagement )
{
ioreq->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ioreq->ios2_WireError = S2WERR_BUFF_ERROR;
}
else
{
if( !(db->db_Units[unit].du_Flags & DUF_ONLINE ))
{
ioreq->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ioreq->ios2_WireError = S2WERR_UNIT_OFFLINE;
}
else
{
ioreq->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
ObtainSemaphore(&db->db_Units[unit].du_Sem);
dbAddReadReq( db, unit, ioreq );
ReleaseSemaphore(&db->db_Units[unit].du_Sem);
ioreq = (0);
}
}
break;
/* Note: Orphans are not distributed to all listeners.
Strictly speaking, nobody actually asked for these
frame types, anyway. */
case S2_READORPHAN:
if( !ioreq->ios2_BufferManagement )
{
ioreq->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ioreq->ios2_WireError = S2WERR_BUFF_ERROR;
}
else
{
if( !(db->db_Units[unit].du_Flags & DUF_ONLINE ))
{
ioreq->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ioreq->ios2_WireError = S2WERR_UNIT_OFFLINE;
}
else
{
ioreq->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
ObtainSemaphore(&db->db_Units[unit].du_Sem);
ADDTAIL((struct List*)&db->db_Units[unit].du_ReadOrphans,(struct Node*)ioreq);
ReleaseSemaphore(&db->db_Units[unit].du_Sem);
ioreq = (0);
}
}
break;
/* same routine for BC, MC, UC */
case S2_BROADCAST:
{
UWORD *ptr = (UWORD*)ioreq->ios2_DstAddr;
ptr[0] = ptr[1] = ptr[2] = 0xffff;
}
case S2_MULTICAST:
case CMD_WRITE:
{
ULONG mtu = db->db_Units[unit].du_MTU;
if(ioreq->ios2_Req.io_Flags & SANA2IOF_RAW)
mtu += 18; /* DEST MAC, SRC MAC, Type/Length, CRC */
if(ioreq->ios2_DataLength > mtu)
ioreq->ios2_Req.io_Error = S2ERR_MTU_EXCEEDED;
else
{
if( !ioreq->ios2_BufferManagement )
{
ioreq->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ioreq->ios2_WireError = S2WERR_BUFF_ERROR;
}
else
{
if( !(db->db_Units[unit].du_Flags & DUF_ONLINE ))
{
ioreq->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ioreq->ios2_WireError = S2WERR_UNIT_OFFLINE;
}
else
{
ioreq->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
ObtainSemaphore(&db->db_Units[unit].du_Sem);
ADDTAIL((struct List*)&db->db_Units[unit].du_WriteQueue,(struct Node*)ioreq);
ReleaseSemaphore(&db->db_Units[unit].du_Sem);
Signal( (struct Task*)db->db_ServerProc, SIGBREAKF_CTRL_F );
ioreq = (0);
}
}
}
}
break;
case S2_ONEVENT:
if (((ioreq->ios2_WireError&S2EVENT_ONLINE)&&(db->db_Units[unit].du_Flags&DUF_ONLINE)) ||
((ioreq->ios2_WireError&S2EVENT_OFFLINE)&&!(db->db_Units[unit].du_Flags&DUF_ONLINE)))
{
ioreq->ios2_Req.io_Error = 0;
ioreq->ios2_WireError &= (S2EVENT_ONLINE|S2EVENT_OFFLINE);
dbTermIO(db,ioreq);
ioreq = (0);
}
else if ((ioreq->ios2_WireError & (S2EVENT_ERROR|S2EVENT_TX|S2EVENT_RX|S2EVENT_ONLINE|
S2EVENT_OFFLINE|S2EVENT_BUFF|S2EVENT_HARDWARE|S2EVENT_SOFTWARE))
!= ioreq->ios2_WireError)
{
ioreq->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
ioreq->ios2_WireError = S2WERR_BAD_EVENT;
}
else
{
/* handle request in Server task */
ioreq->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
ObtainSemaphore(&db->db_Units[unit].du_Sem);
AddTail((struct List*)&db->db_Units[unit].du_EventQueue, (struct Node*)ioreq);
ReleaseSemaphore(&db->db_Units[unit].du_Sem);
Signal((struct Task*)db->db_ServerProc, SIGBREAKF_CTRL_F );
ioreq = (0);
}
break;
case S2_ONLINE:
case S2_OFFLINE:
case S2_CONFIGINTERFACE:
dbForwardIO( db, ioreq );
ioreq = (0);
break;
case S2_GETSTATIONADDRESS:
D(("CFGAddr0 %lx %lx %lx...\n",\
(LONG)db->db_Units[unit].du_CFGAddr[0],\
(LONG)db->db_Units[unit].du_CFGAddr[1],\
(LONG)db->db_Units[unit].du_CFGAddr[2] ));
CopyMem( db->db_Units[unit].du_CFGAddr, ioreq->ios2_SrcAddr, 6);
CopyMem( db->db_Units[unit].du_HWAddr, ioreq->ios2_DstAddr, 6);
D(("CFGAddr1 %lx %lx %lx...\n",(LONG)*(ioreq->ios2_SrcAddr), (LONG)*(ioreq->ios2_SrcAddr+1), (LONG)*(ioreq->ios2_SrcAddr+2)));
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
break;
case S2_DEVICEQUERY:
{
struct Sana2DeviceQuery *query = ioreq->ios2_StatData;
query->DevQueryFormat = 0;
query->DeviceLevel = 0;
D(("DeviceQuery size %ld\n",query->SizeAvailable));
if(query->SizeAvailable >= 18) query->AddrFieldSize = 48;
if(query->SizeAvailable >= 22) query->MTU = db->db_Units[unit].du_MTU;
if(query->SizeAvailable >= 26) query->BPS = db->db_Units[unit].du_BitPerSec;
if(query->SizeAvailable >= 30) query->HardwareType = S2WireType_Ethernet;
if(query->SizeAvailable >= 34)
{ /* RawMTU */
ULONG *tmp = (ULONG*)&query->HardwareType;
*(tmp+1) = 1518; /* DMAC,SMAC,TYPE,1500,CRC32 */
}
query->SizeSupplied = min( (ULONG)query->SizeAvailable, 34);
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
case S2_TRACKTYPE:
if( !dbTrackType( db, unit, ioreq->ios2_PacketType, 1 ))
ioreq->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
else ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
break;
case S2_UNTRACKTYPE:
if( !dbTrackType( db, unit, ioreq->ios2_PacketType, 0 ))
{
ioreq->ios2_Req.io_Error = S2ERR_BAD_STATE;
ioreq->ios2_WireError = S2WERR_NOT_TRACKED;
}
else
{
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
case S2_GETTYPESTATS:
if( !dbTrackTypeStats( db, unit, ioreq->ios2_PacketType, (struct Sana2PacketTypeStats *)ioreq->ios2_StatData ))
{
ioreq->ios2_Req.io_Error = S2ERR_BAD_STATE;
ioreq->ios2_WireError = S2WERR_NOT_TRACKED;
}
else
{
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
case S2_GETGLOBALSTATS:
CopyMem( &db->db_Units[unit].du_DevStats, ioreq->ios2_StatData, sizeof(struct Sana2DeviceStats) );
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
break;
case CMD_RESET:
case CMD_UPDATE:
case CMD_CLEAR:
case CMD_STOP:
case CMD_START:
case CMD_FLUSH:
ioreq->ios2_Req.io_Error = IOERR_NOCMD;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
break;
case S2_ADDMULTICASTADDRESS:
case S2_DELMULTICASTADDRESS:
#if 1
dbForwardIO( db, ioreq );
ioreq = (0);
#else
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
#endif
break;
/*case S2_GETEXTENDEDGLOBALSTATS: */ /* S2R4 */
case S2_GETSPECIALSTATS:
default:
ioreq->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
break;
}
if( ioreq )
dbTermIO( db, ioreq );
}
ASM SAVEDS LONG DevAbortIO( ASMR(a1) struct IOSana2Req *ioreq ASMREG(a1),
ASMR(a6) DEVBASEP ASMREG(a6) )
{
/* we store ioreqs in various places, search them all */
struct SanaReadType *type;
struct SanaReader *rd;
LONG ret = 0;
ULONG unit = (ULONG)ioreq->ios2_Req.io_Unit;
ObtainSemaphore(&db->db_Units[unit].du_Sem);
/* start with the read list -> types -> readers -> reqlist */
type = (struct SanaReadType *)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
rd = (struct SanaReader *)GetHead( &type->srt_Readers );
while( rd )
{
if( dbIsInList( &rd->snr_Requests, (struct Node*)ioreq ))
goto abort;
rd = GetSucc( rd );
}
type = GetSucc( type );
}
if( dbIsInList( (struct List*)&db->db_Units[unit].du_WriteQueue, (struct Node*)ioreq ))
goto abort;
if( dbIsInList( (struct List*)&db->db_Units[unit].du_EventQueue, (struct Node*)ioreq ))
goto abort;
if( dbIsInList( (struct List*)&db->db_Units[unit].du_ReadOrphans, (struct Node*)ioreq ))
goto abort;
ret = -1;
goto end;
abort:
Remove( (struct Node*)ioreq );
ioreq->ios2_Req.io_Error = IOERR_ABORTED;
ioreq->ios2_WireError = 0;
ReplyMsg((struct Message*)ioreq);
end:
ReleaseSemaphore(&db->db_Units[unit].du_Sem);
return ret;
}
void dbTermIO( DEVBASEP, struct IOSana2Req *ioreq )
{
D(("dbTermIO flags %ld\n",(LONG)ioreq->ios2_Req.io_Flags));
if( ioreq->ios2_Req.io_Flags & SANA2IOF_QUICK )
ioreq->ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
else
ReplyMsg( (struct Message *)ioreq );
}
/* private functions */
static void dbForwardIO( DEVBASEP, struct IOSana2Req *ioreq )
{
ioreq->ios2_Req.io_Flags &= ~SANA2IOF_QUICK;
PutMsg(db->db_ServerPort, (struct Message*)ioreq);
D(("Forward Request %ld\n",ioreq->ios2_Req.io_Command));
}
static void dbNewList( struct List *l )
{
l->lh_TailPred = (struct Node *)l;
l->lh_Head = (struct Node *)&l->lh_Tail;
l->lh_Tail = 0;
}
static LONG dbIsInList( struct List *l, struct Node *entry )
{
struct Node *nd;
nd = GetHead( l );
while( nd )
{
if( nd == entry )
return 1;
nd = GetSucc(nd);
}
return 0;
}
/* start Server function and keep it up */
/* note: server is global right now */
static LONG dbStartServer( DEVBASEP, LONG unit )
{
struct MsgPort *port;
LONG ret = 0,i;
struct TagItem sv_tags[5];
/* server running ? */
if( db->db_ServerProc )
return 1;
if(!(port = CreateMsgPort() ))
return 0;
sv_tags[i=0].ti_Tag = NP_Entry;
sv_tags[i++].ti_Data = (ULONG)ServerTask;
sv_tags[i ].ti_Tag = NP_Name;
sv_tags[i++].ti_Data = (ULONG)db->db_Lib.lib_Node.ln_Name;
sv_tags[i ].ti_Tag = NP_Priority;
sv_tags[i++].ti_Data = (ULONG)0;
sv_tags[i ].ti_Tag = TAG_DONE;
if( (db->db_ServerProc = CreateNewProc(sv_tags)))
{
/* server is running, wait for response */
volatile struct ServerMsg msg;
msg.sm_devbase = db;
msg.sm_result = 0;
msg.sm_msg.mn_Length = sizeof(msg);
msg.sm_msg.mn_ReplyPort = port;
PutMsg( (struct MsgPort*)&db->db_ServerProc->pr_MsgPort, (struct Message*)&msg.sm_msg );
WaitPort( port );
ret = msg.sm_result; /* should be 1 */
D(("Server answered with %ld\n",ret));
}
if( !ret )
db->db_ServerProc = (0);
DeleteMsgPort( port );
return ret;
}
/* stop Server function */
/* note: server is global right now */
static LONG dbStopServer( DEVBASEP, LONG unit )
{
ULONG i=1;
volatile struct Task *P;
/* server deletes itself from devbase */
while( (P=(volatile struct Task*)db->db_ServerProc) )
{
Signal( (struct Task*)P, SIGBREAKF_CTRL_C);
Delay(i);
i = (i & 0xf) + 1;
}
return 0;
}
/*
delete all buffers (type references) associated with device opener
*/
static void dbDeleteBuffers( DEVBASEP, struct IOSana2Req *ioreq )
{
struct db_BufferManagement *dbm;
ULONG ID = 0;
for( dbm = (struct db_BufferManagement *)db->db_BufferManagement.lh_Head;
(dbm->dbm_n.mln_Succ) && (ioreq->ios2_BufferManagement != (VOID*)dbm);
dbm = (struct db_BufferManagement *)dbm->dbm_n.mln_Succ ) {}
/* delete BufferManagement structure if it was allocated by us (should be) */
if( dbm->dbm_n.mln_Succ )
{
ID = dbm->dbm_ID;
Forbid();
REMOVE( dbm );
Permit();
FreeMem( dbm, sizeof(struct db_BufferManagement) );
ioreq->ios2_BufferManagement = (0);
}
/* scan all buffers of current unit for opener ID and discard associated data */
if( ID )
{
ULONG unit = (ULONG)ioreq->ios2_Req.io_Unit;
dbDeleteReader( db, unit, ID );
}
/* we assume that no requests were left in the queues -> TODO: add some debug info to verify */
}
/*
before calling:
- get Semaphore
- check BufferManagement
- make sure ioreq is != NULL
*/
static void dbAddReadReq( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq )
{
struct SanaReadType *type;
struct SanaReader *rd;
ULONG frametype = ioreq->ios2_PacketType;
ULONG ID = ((struct db_BufferManagement*)ioreq->ios2_BufferManagement)->dbm_ID;
/* find type or add it (typically 2 types present: IP,ARP) */
type = (struct SanaReadType *)db->db_Units[unit].du_ReadTypes.lh_Head;
while( (type->srt_Node.mln_Succ) )
{
if( type->srt_Type == frametype )
goto havetype;
type = (struct SanaReadType *)type->srt_Node.mln_Succ;
}
#if 0
type = (struct SanaReadType *)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
if( type->srt_Type == frametype )
goto havetype;
type = GetSucc( type );
}
#endif
if( !(type = dbAddType( db, unit, frametype ) ))
return; /* bail out, no mem left */
havetype:
/* traverse readers on type (usually 1 reader) */
rd = (struct SanaReader *)GetHead( &type->srt_Readers );
while( rd )
{
if( rd->snr_ID == ID )
goto havereader;
rd = GetSucc(rd);
}
if( !(rd = dbAddReader(db,unit,frametype,type,ID ) ))
return; /* bail out, no mem left */
havereader:
ADDTAIL( &rd->snr_Requests , (struct Node*)ioreq );
}
/* add reader for frame type or directly to a type's list */
static struct SanaReader *dbAddReader( DEVBASEP, ULONG unit, ULONG frametype, struct SanaReadType *type, ULONG ID )
{
struct SanaReader *rd;
if( !type ) /* search types for ID if not explicitly given */
{
type = (struct SanaReadType *)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
if( type->srt_Type == frametype )
break;
type = GetSucc( type );
}
if( !type )
return (0); /* error: no such type */
}
rd = (struct SanaReader *)GetHead( &type->srt_Readers );
while( rd )
{
if( rd->snr_ID == ID )
{
return rd;
}
rd = GetSucc(rd);
}
rd = (struct SanaReader *)AllocMem( sizeof( struct SanaReader ), MEMF_PUBLIC|MEMF_CLEAR );
if( !rd )
return (0);
rd->snr_ID = ID;
dbNewList( &rd->snr_Requests );
if( (GetHead( &type->srt_Readers ) ))
type->srt_Flags |= SRTF_MULTREADER;
else type->srt_Flags |= SRTF_ONEREADER;
AddTail( &type->srt_Readers, (struct Node*)rd );
return rd;
}
/* delete all references to reader among all types of specified unit */
static void dbDeleteReader( DEVBASEP, ULONG unit, ULONG ID )
{
/* traverse types */
struct SanaReadType *type,*t2;
struct SanaReader *rd;
type = (struct SanaReadType*)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
rd = (struct SanaReader *)GetHead( &type->srt_Readers );
while( rd )
{
if( rd->snr_ID == ID )
{
REMOVE(rd);
FreeMem(rd, sizeof( struct SanaReader ) );
if( (t2 = GetHead( &type->srt_Readers ) ) )
{
if( !(GetSucc(t2) ))
t2->srt_Flags &= ~SRTF_MULTREADER;
}
else t2->srt_Flags &= ~(SRTF_ONEREADER|SRTF_MULTREADER);
break;
}
rd = GetSucc(rd);
}
type = GetSucc( type );
}
}
/*
add frame type to Read Queue
note: the lifetime of read types is "infinite" right now, the
type queues are deleted only on Device Expunge
*/
static struct SanaReadType *dbAddType( DEVBASEP, ULONG unit, ULONG frametype )
{
struct SanaReadType *type;
D(("dbAddType %ld to unit %ld\n",frametype,unit));
/* check whether type is existing */
type = (struct SanaReadType*)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
if( type->srt_Type == frametype ) /* type present: do nothing */
return type;
type = GetSucc( type );
}
/* */
type = (struct SanaReadType*)AllocMem( sizeof( struct SanaReadType ), MEMF_PUBLIC|MEMF_CLEAR );
if( type )
{
D(("dbAddType new type %ld to unit %ld\n",frametype,unit));
type->srt_Type = frametype;
dbNewList( &type->srt_Readers );
AddTail( (struct List*)&db->db_Units[unit].du_ReadTypes, (struct Node*)type );
}
return type;
}
/*
Delete Type and readers on that type
*/
static void dbDeleteType( DEVBASEP, ULONG unit, ULONG frametype )
{
struct SanaReadType *type;
struct SanaReader *rd;
type = (struct SanaReadType*)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
if( type->srt_Type == frametype )
break;
type = GetSucc( type );
}
if( !type )
return;
while( (rd = GetHead( &type->srt_Readers ) ))
{
REMOVE(rd);
FreeMem(rd, sizeof( struct SanaReader ) );
}
REMOVE(type);
FreeMem( type, sizeof( struct SanaReadType ) );
}
/* do track type statistics (1) or toggle (0) */
static LONG dbTrackType( DEVBASEP, ULONG unit, ULONG frametype, ULONG onoff )
{
struct SanaReadType *type = dbAddType( db, unit, frametype );
LONG ret = 1;
ULONG flg = 0;
if( !type )
return 0;
if( onoff )
{
type->srt_Flags |= SRTF_TRACKTYPE;
db->db_Units[unit].du_Flags |= DUF_TYPETRACK;
return ret;
}
if( !(type->srt_Flags & SRTF_TRACKTYPE ) )
ret = 0;
else
type->srt_Flags ^= SRTF_TRACKTYPE;
/* set TYPETRACK flag if any type on unit is still tracked */
/* well, actually this breaks if more than one stack is active. Duh. */
type = (struct SanaReadType *)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
if( type->srt_Flags & SRTF_TRACKTYPE )
flg = DUF_TYPETRACK;
type = GetSucc( type );
}
db->db_Units[unit].du_Flags &= ~DUF_TYPETRACK;
db->db_Units[unit].du_Flags |= flg;
return ret;
}
/* return type statistics */
static LONG dbTrackTypeStats( DEVBASEP, ULONG unit, ULONG frametype, struct Sana2PacketTypeStats *statptr )
{
struct SanaReadType *type = dbAddType( db, unit, frametype );
if( !type )
return 0;
if( !(type->srt_Flags & SRTF_TRACKTYPE ) )
return 0;
CopyMem( &type->srt_Sana2PacketTypeStats, statptr, sizeof(struct Sana2PacketTypeStats) );
return 1;
}
+266
View File
@@ -0,0 +1,266 @@
/*
device.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Device Functions and Definitions
*/
#ifndef _INC_DEVICE_H
#define _INC_DEVICE_H
/* defaults */
#define MAX_UNITS 4
#define DEF_BPS 10000000
#define DEF_MTU 1500
/* includes */
#include "compiler.h"
#include <dos/dos.h>
#include <exec/lists.h>
#include <exec/libraries.h>
#include <exec/devices.h>
#include <exec/semaphores.h>
#include <devices/sana2.h>
#include "hw.h"
#include "debug.h"
/* reassign Library bases from global definitions to own struct */
#define SysBase db->db_SysBase
#define DOSBase db->db_DOSBase
#define UtilityBase db->db_UtilityBase
//#define VampireBase db->db_VampireBase
/* old sana2.h ? */
#ifndef S2_CopyToBuff32
#define S2_CopyToBuff32 (S2_Dummy + 6)
#endif
#ifndef S2_CopyFromBuff32
#define S2_CopyFromBuff32 (S2_Dummy + 7)
#endif
struct ServerData {
ULONG sv_flags;
UBYTE *sv_frame; /* temporary frame for rx/tx */
ULONG sv_framesize; /* allocated bytes */
struct List sv_mclist; /* list of multicast addresses to accept */
};
#define DUB_OPEN 0
#define DUB_EXCLUSIVE 1
#define DUB_ONLINE 2
#define DUB_TYPETRACK 3
#define DUB_PROMISC 4
#define DUF_OPEN (1<<DUB_OPEN)
#define DUF_EXCLUSIVE (1<<DUB_EXCLUSIVE)
#define DUF_ONLINE (1<<DUB_ONLINE)
#define DUF_TYPETRACK (1<<DUB_TYPETRACK)
#define DUF_PROMISC (1<<DUB_PROMISC)
struct DevUnit {
volatile struct List du_ReadTypes; /* RX queues are sorted by type: find RX buffers
by type, afterwards traverse active readers =
networking stacks */
volatile struct List du_ReadOrphans; /* not claimed by RX types or no buffers available -> orphan */
volatile struct List du_WriteQueue; /* TX queue */
volatile struct List du_EventQueue; /* Event queue */
struct SignalSemaphore du_Sem; /* list locking (global per unit) */
ULONG du_OpenCount; /* unit openers */
ULONG du_Flags; /* unit flags */
ULONG du_MTU; /* maximum transmission unit (1500 for regular Ethernet) */
ULONG du_BitPerSec; /* speed in BPS */
UBYTE du_HWAddr[6]; /* default MAC address */
UBYTE du_CFGAddr[6]; /* configured MAC address */
struct Sana2DeviceStats du_DevStats; /* collect device stats per unit */
/* HW Data (generic for now) */
ULONG du_hwl0;
ULONG du_hwl1;
ULONG du_hwl2;
APTR du_hwp0;
APTR du_hwp1;
APTR du_hwp2;
};
struct devbase {
struct Library db_Lib;
BPTR db_SegList; /* from Device Init */
ULONG db_Flags; /* misc */
struct Library *db_SysBase; /* Exec Base */
struct Library *db_DOSBase;
struct Library *db_UtilityBase;
ULONG db_NBoards;
ULONG db_ID; /* device opener ID enumeration */
struct DevUnit db_Units[MAX_UNITS]; /* don't bother with lists, who's gonna have more
than 1 network card of the same type, anyway ? */
/* server process data */
volatile struct Process *db_ServerProc; /* process of main queue server */
struct Task *db_Task; /* server shutdown task address (non permanently available) */
ULONG db_ServerSigMask; /* shutdown signal */
struct MsgPort *db_ServerPort; /* request forwarding (initialized by server) */
/* housekeeping */
struct List db_BufferManagement; /* remember allocated instances */
/* services for other instances (server task, hw.c) */
struct ServerData db_svdat; /* private data for main server */
struct HWData db_hwdat; /* private data for hardware interface */
};
typedef BOOL (* ASM BMCALL)( ASMR(a0) void * ASMREG(a0), ASMR(a1) void * ASMREG(a1), ASMR(d0) LONG ASMREG(d0) );
struct db_BufferManagement {
struct MinNode dbm_n;
BMCALL dbm_CopyFromBuffer32;
BMCALL dbm_CopyToBuffer32;
BMCALL dbm_CopyFromBuffer;
BMCALL dbm_CopyToBuffer;
ULONG dbm_ID;
};
#ifndef DEVBASETYPE
#define DEVBASETYPE struct devbase
#endif
#ifndef DEVBASEP
#define DEVBASEP DEVBASETYPE *db
#endif
struct ServerMsg {
struct Message sm_msg;
DEVBASETYPE *sm_devbase;
LONG sm_result;
};
/* frame type management:
- every network stack who is interested in a specific type gets a "reader" assigned to
that type. The ID tracked here is the same that is assigned on OpenDevice and stored
in the buffer management
-
*/
struct SanaReader
{
struct MinNode snr_Node;
ULONG snr_ID; /* assigned to any reader on Device open */
struct List snr_Requests;
};
/*
read list(s)
- multiple readers supported (i.e. multiple active Networking stacks)
- one read list per type (shouldn't be too many)
- two major thoughts behind this
- track type statistics without additional search
- received frame copy to additional reader(s) without searching through
their read request lists
+ avoid long searches for orphans (unexpected received frames)
*/
struct SanaReadType
{
struct MinNode srt_Node;
ULONG srt_Type; /* Type ID - matches Ethernet Types */
ULONG srt_Flags; /* Flags: multiple reader mode, do track type */
struct List srt_Readers; /* active readers */
/* SANA-II Type statistics */
struct Sana2PacketTypeStats srt_Sana2PacketTypeStats;
ULONG srt_FramesSent;
ULONG srt_FramesRcvd;
ULONG srt_BytesSent;
ULONG srt_BytesRcvd;
ULONG srt_FramesDropped;
};
/* SanaReadList flags */
#define SRTB_ONEREADER 0 /* at least one reader present ? */
#define SRTB_MULTREADER 1 /* more than one reader */
#define SRTB_TRACKTYPE 2 /* perform track type statistics */
#define SRTF_ONEREADER (1<<SRTB_ONEREADER)
#define SRTF_MULTREADER (1<<SRTB_MULTREADER)
#define SRTF_TRACKTYPE (1<<SRTB_TRACKTYPE)
/* server maintains list of multicast addresses */
struct MCastEntry
{
struct MinNode mce_Node;
UBYTE mce_start[6];
UBYTE mce_stop[6];
ULONG mce_refcount; /* ok, this is poor. Proper would mean "track by caller". */
/* But hey, how much multicast do you encounter on Amiga? */
};
/* PROTOS */
ASM LONG LibNull( void );
ASM SAVEDS struct Device *DevInit(ASMR(d0) DEVBASEP ASMREG(d0),
ASMR(a0) BPTR seglist ASMREG(a0),
ASMR(a6) struct Library *_SysBase ASMREG(a6) );
ASM SAVEDS LONG DevOpen( ASMR(a1) struct IOSana2Req *ios2 ASMREG(a1),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(d1) ULONG flags ASMREG(d1),
ASMR(a6) DEVBASEP ASMREG(a6) );
ASM SAVEDS BPTR DevClose( ASMR(a1) struct IOSana2Req *ios2 ASMREG(a1),
ASMR(a6) DEVBASEP ASMREG(a6) );
ASM SAVEDS BPTR DevExpunge( ASMR(a6) DEVBASEP ASMREG(a6) );
ASM SAVEDS VOID DevBeginIO( ASMR(a1) struct IOSana2Req *ios2 ASMREG(a1),
ASMR(a6) DEVBASEP ASMREG(a6) );
ASM SAVEDS LONG DevAbortIO( ASMR(a1) struct IOSana2Req *ios2 ASMREG(a1),
ASMR(a6) DEVBASEP ASMREG(a6) );
void dbTermIO( DEVBASETYPE*, struct IOSana2Req * );
/* server.c */
VOID SAVEDS ServerTask(void);
/* private functions */
#ifdef DEVICE_MAIN
static void dbNewList( struct List * );
static LONG dbIsInList( struct List *, struct Node * );
static LONG dbStartServer( DEVBASETYPE*, LONG );
static LONG dbStopServer( DEVBASETYPE*, LONG );
static void dbDeleteBuffers( DEVBASETYPE*, struct IOSana2Req * );
static void dbAddReadReq( DEVBASETYPE*, ULONG , struct IOSana2Req * );
static struct SanaReader*dbAddReader( DEVBASETYPE*, ULONG, ULONG, struct SanaReadType *, ULONG );
static void dbDeleteReader( DEVBASETYPE*, ULONG, ULONG );
static struct SanaReadType *dbAddType( DEVBASETYPE*, ULONG, ULONG );
static void dbDeleteType( DEVBASETYPE*, ULONG, ULONG );
static void dbForwardIO( DEVBASETYPE*, struct IOSana2Req * );
static LONG dbTrackType(DEVBASETYPE*, ULONG, ULONG, ULONG );
static LONG dbTrackTypeStats( DEVBASETYPE*, ULONG, ULONG, struct Sana2PacketTypeStats *);
#endif /* DEVICE_MAIN */
#endif /* _INC_DEVICE_H */
+50
View File
@@ -0,0 +1,50 @@
/*
Device Header - ROMTAG
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
taken from commandline (compiler options)
-DDEVICENAME=blah.device
-DDEVICEVERSION=45
-DDEVICEREVISION=36
-DDEVICEDATE=2.12.2012
*/
#include <exec/resident.h>
#include <exec/nodes.h>
#include <exec/initializers.h>
#include <exec/libraries.h>
#include "compiler.h"
#include "device.h"
/* Enable this if you want pure C for the device. (disable compilation of romtag.asm in that case)
I personally prefer the small ASM blob to steer away from linking challenges.
*/
#if 1
ASM LONG LibNull( void )
{
return 0;
}
extern const BYTE DeviceName[];
extern const BYTE DeviceVersionString[];
extern const APTR DeviceInitTab[];
const struct Resident _00RomTag = {
RTC_MATCHWORD,
( struct Resident* ) &_00RomTag,
( struct Resident* ) &_00RomTag + 1,
RTF_AUTOINIT,
DEVICEVERSION,
NT_DEVICE,
0,
(BYTE*)DeviceName,
(BYTE*)DeviceVersionString+6,
(APTR)DeviceInitTab
};
#endif
+72
View File
@@ -0,0 +1,72 @@
/*
devinit.c
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
initialization structures and data
*/
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <exec/lists.h>
#include "device.h"
#include "hw.h"
#include "macros.h"
#include <exec/initializers.h>
#define xstr(a) str(a)
#define str(a) #a
#ifndef DEVICEEXTRA
#define DEVICEEXTRA
#endif
const BYTE DeviceVersionString[] = "$VER: " xstr(DEVICENAME) " " xstr(DEVICEVERSION) "." xstr(DEVICEREVISION) xstr(DEVICEEXTRA) " (" xstr(DEVICEDATE) ")";
const BYTE DeviceName[] = xstr(DEVICENAME);
const APTR DeviceFunctions[] = {
(APTR) DevOpen,
(APTR) DevClose,
(APTR) DevExpunge,
(APTR) LibNull,
(APTR) DevBeginIO,
(APTR) DevAbortIO,
(APTR) -1
};
#define WORDINIT(_a_) UWORD _a_ ##W1; UWORD _a_ ##W2; UWORD _a_ ##ARG;
#define LONGINIT(_a_) UBYTE _a_ ##A1; UBYTE _a_ ##A2; ULONG _a_ ##ARG;
struct DeviceInitData
{
WORDINIT(w1)
LONGINIT(l1)
WORDINIT(w2)
WORDINIT(w3)
WORDINIT(w4)
LONGINIT(l2)
ULONG end_initlist;
} DeviceInitializers =
{
INITBYTE( STRUCTOFFSET( Node, ln_Type), NT_DEVICE),
0x80, (UBYTE) STRUCTOFFSET( Node, ln_Name), (ULONG) &DeviceName[0],
INITBYTE( STRUCTOFFSET(Library,lib_Flags), LIBF_SUMUSED|LIBF_CHANGED ),
INITWORD( STRUCTOFFSET(Library,lib_Version), DEVICEVERSION ),
INITWORD( STRUCTOFFSET(Library,lib_Revision), DEVICEREVISION ),
0x80, (UBYTE) STRUCTOFFSET(Library,lib_IdString), (ULONG) &DeviceVersionString[6],
(ULONG) 0
};
const APTR DeviceInitTab[] = {
(APTR) sizeof( DEVBASETYPE ),
(APTR) &DeviceFunctions,
(APTR) &DeviceInitializers,
(APTR) DevInit
};
+92
View File
@@ -0,0 +1,92 @@
/*
Device Header
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
taken from commandline (compiler options)
-DDEVICENAME=blah.device
-DDEVICEVERSION=45
-DDEVICEREVISION=36
-DDEVICEDATE=2.12.2012
*/
#include <exec/resident.h>
#include <exec/nodes.h>
#include <exec/initializers.h>
#include <exec/libraries.h>
#include "compiler.h"
#include "device.h"
#define xstr(a) str(a)
#define str(a) #a
#if 1
/* Enable this if you want pure C for the device. (disable compilation of romtag.asm in that case)
I personally prefer the small ASM blob to steer away from linking challenges.
*/
const BYTE DeviceName[];
const BYTE DeviceVersionString[];
const APTR DeviceInitTab[];
const struct Resident RomTag = {
RTC_MATCHWORD,
( struct Resident* ) &RomTag,
( struct Resident* ) &RomTag + 1,
RTF_AUTOINIT,
DEVICEVERSION,
NT_DEVICE,
0,
(BYTE*)DeviceName,
(BYTE*)DeviceVersionString+6,
(APTR)DeviceInitTab
};
#endif
const BYTE DeviceVersionString[] = "$VER: " xstr(DEVICENAME) " " xstr(DEVICEVERSION) "." xstr(DEVICEREVISION) " (" xstr(DEVICEDATE) ")";
const BYTE DeviceName[] = xstr(DEVICENAME);
const APTR DeviceFunctions[] = {
(APTR) DevOpen,
(APTR) DevClose,
(APTR) DevExpunge,
(APTR) LibNull,
(APTR) DevBeginIO,
(APTR) DevAbortIO,
(APTR) -1
};
#define WORDINIT(_a_) UWORD _a_ ##W1; UWORD _a_ ##W2; UWORD _a_ ##ARG;
#define LONGINIT(_a_) UBYTE _a_ ##A1; UBYTE _a_ ##A2; ULONG _a_ ##ARG;
struct DeviceInitData
{
WORDINIT(w1)
LONGINIT(l1)
WORDINIT(w2)
WORDINIT(w3)
WORDINIT(w4)
LONGINIT(l2)
ULONG end_initlist;
} DeviceInitializers =
{
INITBYTE( STRUCTOFFSET( Node, ln_Type), NT_DEVICE),
0x80, (UBYTE) STRUCTOFFSET( Node, ln_Name), (ULONG) &DeviceName[0],
INITBYTE( STRUCTOFFSET(Library,lib_Flags), LIBF_SUMUSED|LIBF_CHANGED ),
INITWORD( STRUCTOFFSET(Library,lib_Version), DEVICEVERSION ),
INITWORD( STRUCTOFFSET(Library,lib_Revision), DEVICEREVISION ),
0x80, (UBYTE) STRUCTOFFSET(Library,lib_IdString), (ULONG) &DeviceVersionString[6],
(ULONG) 0
};
const APTR DeviceInitTab[] = {
(APTR) sizeof( DEVBASETYPE ),
(APTR) &DeviceFunctions,
(APTR) &DeviceInitializers,
(APTR) DevInit
};
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+318
View File
@@ -0,0 +1,318 @@
/*
enc624j6l.h
author: Henryk Richter <henryk.richter@gmx.net>
purpose: header for low-level board access
to memory mapped ENC624J600 networking
device
note: all functions are relative to the base pointer,
determine IO space by a respective expansion.library
call
*/
#ifndef _INC_ENC624J6L_H
#define _INC_ENC624J6L_H
#include "compiler.h"
/*
Defines: flags copied from PlipBox
*/
/* result values */
#define PIO_OK 0
#define PIO_NOT_FOUND 1
#define PIO_TOO_LARGE 2
#define PIO_IO_ERR 3
/* init flags */
#define PIO_INIT_FULL_DUPLEX 1
#define PIO_INIT_LOOP_BACK 2
#define PIO_INIT_BROAD_CAST 4
#define PIO_INIT_FLOW_CONTROL 8
#define PIO_INIT_MULTI_CAST 16
#define PIO_INIT_PROMISC 32
/* status flags */
#define PIO_STATUS_VERSION 0
#define PIO_STATUS_LINK_UP 1
/* control ids */
#define PIO_CONTROL_FLOW 0
/*-- --*/
/*------------- <DANGER! APPLY CHANGES IN HERE ALSO TO enc624j6l.asm !!> ----------*/
/*-- --*/
/* buffers and locations */
#define PSTART_INIT 0x0BF0 /* 16 Bytes private storage for driver */
#define PSTOP_INIT 0x0BFF /* (see below for definitions) */
#define RXSTART_INIT 0x2000 /* start of RX buffer, room for 14 packets */
#define RXSTOP_INIT 0x5fff /* end of RX buffer */
#define TXSTART_INIT 0x0000 /* start of TX buffer, room for 2 packets */
#define TXSTOP_INIT 0x0BEF /* end of TX buffer (1536+1520 bytes) */
/* max frame length which the conroller will accept:
(note: maximum ethernet frame length would be 1518 w/o 802.1Q) */
#define MAX_FRAMELEN 1518
/* ------------ driver private storage ------------------------------ */
#define PNextPacket PSTART_INIT /* 2 Bytes - private "next packet" pointer for RX */
#define PNextTX PSTART_INIT+2 /* 2 Bytes - private "next packet" pointer for TX (init as 0) */
#define PSigBit PSTART_INIT+4 /* 2 Bytes interrupt bit */
#define PSigTask PSTART_INIT+6 /* 4 Bytes signaled task */
#define PLinkChange PSTART_INIT+10 /* 2 Bytes Link Change (Bit0 used right now) */
#define Punused PSTART_INIT+12 /* */
/* ------------- TX: double buffering -------------------------------- */
#define PSwapTX 0x600 /* swap between two TX buffers (one active, one activated after last TX done) */
/*-- --*/
/*------------- </DANGER! APPLY CHANGES IN HERE ALSO TO enc624j6l.asm !!> ---------*/
/*-- --*/
/*
Purpose: check whether the board is operating as needed
input: board - mmapped board base address
output: success/failure
>0 - all fine
<=0 - problem (codes undefined yet)
notes: The Olimex module on the Matze/Scrat ZII IDE LAN CP prototype
tends to boot in SPI mode instead of PSP (parallel) mode.
That case is detected by writing/reading data. If both bytes
of the words are equal to the last written byte, the board is likely
showing this issue.
Please note that this call will overwrite Board registers and
buffer memory. Call before initialization.
*/
ASM SAVEDS int enc624j6l_CheckBoard( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: obtain current MAC address
input: board - mmapped board base address
buffer - 6 bytes space to write into (network notation, big endian)
output: success/failure
>0 - all fine
<=0 - problem (codes undefined yet)
*/
ASM SAVEDS int enc624j6l_GetMAC( ASMR(a0) void *board ASMREG(a0),
ASMR(a1) unsigned char *buffer ASMREG(a1) );
/*
Purpose: change current MAC address
input: board - mmapped board base address
buffer - 6 bytes space to read from (network notation, big endian)
output: success/failure
>0 - all fine
<=0 - problem (codes undefined yet)
*/
ASM SAVEDS int enc624j6l_SetMAC( ASMR(a0) void *board ASMREG(a0),
ASMR(a1) unsigned char *buffer ASMREG(a1) );
/*
Purpose: delay at least the given amount of microseconds
input: delay time in microseconds
output: -
*/
ASM SAVEDS void enc28j60l_UMinDelay(
ASMR(d0) unsigned long udelay ASMREG(d0) );
/*
Purpose: initialize a board
input: board - mmapped board base address
flags - init flags (0 if in doubt)
output: success/failure
>0 - all fine
<=0 - problem (codes undefined yet)
notes:
*/
ASM SAVEDS int enc624j6l_Init( ASMR(a0) void *board ASMREG(a0),
ASMR(d1) unsigned long flags ASMREG(d1) );
/*
Purpose: stop a board
input: board - mmapped board base address
output: success/failure
>0 - all fine
<=0 - problem (codes undefined yet)
notes: Shutdown means to disconnect the PHY, put the chip
into low power mode and disable interrupts
*/
ASM SAVEDS int enc624j6l_Shutdown( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: check for pending receive frames
input: board - mmapped board base address
output: <0 - error
0 - no pending frames
>0 - number of pending frames
notes:
*/
ASM SAVEDS int enc624j6l_HaveRecv( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: copy received frame to supplied buffer
input: board - mmapped board base address
buffer - output buffer (should have >=1514 bytes)
buffersize - size of buffer
output: <0 - error
0 - nothing in receive buffer
>0 - size of received frame
notes:
*/
ASM SAVEDS int enc624j6l_RecvFrame( ASMR(a0) void *board ASMREG(a0),
ASMR(a1) unsigned char* buffer ASMREG(a1),
ASMR(d0) short buffersize ASMREG(d0) );
/*
Purpose: send single frame out
input: board - mmapped board base address
buffer - output buffer (should have >=1514 bytes)
sendsize - size of buffer in bytes ( no CRC appended (!) )
output: <0 - error
0 - nothing in receive buffer
>0 - size of received frame
notes:
*/
ASM SAVEDS int enc624j6l_TransmitFrame( ASMR(a0) void *board ASMREG(a0),
ASMR(a1) unsigned char* buffer ASMREG(a1),
ASMR(d0) short sendsize ASMREG(d0) );
/*
Purpose: enable RX/TX, perform autonegotiation etc.
input: board - mmapped board base address
output: <0 - error
>=0 - OK
notes: in current implementation, enc624j6l_Init() will
also attach the card to the line. So this
call is not exactly needed in the init phase.
*/
ASM SAVEDS int enc624j6l_SetOnline( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: disable RX/TX, power down
input: board - mmapped board base address
output: <0 - error
>=0 - OK
notes:
*/
ASM SAVEDS int enc624j6l_SetOffline( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: enable HW interrupt
input: board - mmapped board base address
SigTask - task to signal upon interrupt (or NULL)
SigBit - Signal bit to send upon interrupt (or -1)
IntMask - ENC HW interrupts to enable (set 0, for now) = RX PACKET only
output: <0 - error
>=0 - OK
notes:
*/
ASM SAVEDS int enc624j6l_EnableInterrupt( ASMR(a0) void *board ASMREG(a0),
ASMR(a1) void* SigTask ASMREG(a1),
ASMR(d0) int SigBit ASMREG(d0),
ASMR(d1) unsigned int IntMask ASMREG(d1) );
/*
Purpose: disable HW interrupt
input: board - mmapped board base address
output: <0 - error
>=0 - OK
notes:
*/
ASM SAVEDS int enc624j6l_DisableInterrupt( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: enable global HW interrupt flag on the LAN-Chip
input: board - mmapped board base address
notes:
*/
ASM SAVEDS void enc624j6l_EnableGlobalInterrupt( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: disable global HW interrupt flag on the LAN-Chip
input: board - mmapped board base address
notes:
*/
ASM SAVEDS void enc624j6l_DisableGlobalInterrupt( ASMR(a0) void *board ASMREG(a0) );
/*
Purpose: interrupt service routine ( after EnableInterrupt() )
This function will signal the task passed at EnableInterrupt time with
the given sigbit when at least one frame is pending.
input: -
output: -
*/
ASM SAVEDS void enc624j6l_IntServer_List( void );
/*
Purpose: check for link change events
input: board - mmapped board base address
output: <0 - error
0 - ok, no link change handled
>0 - ok, link change handled
notes:
*/
ASM SAVEDS int enc624j6l_CheckLinkChange( ASMR(a0) void *board ASMREG(a0) );
#endif /* _INC_ENC624J6L_H */
+436
View File
@@ -0,0 +1,436 @@
/*
hw.c
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
SDNet hardware interface to device
*/
#include <exec/libraries.h>
#include <proto/exec.h>
/*#include <clib/expansion_protos.h>
#include <pragmas/expansion_pragmas.h>*/
#include <proto/expansion.h>
#include <libraries/configvars.h>
#include <hardware/intbits.h>
#include "device.h"
#include "hw.h"
#include "enc624j6l.h"
#include "registers.h"
extern const BYTE DeviceName[];
#define ENC_MANUFACTURER 2588
#define ENC_BOARD 123
/* use generic board fields in current unit */
#define duh_online du_hwl0
#define duh_BASE du_hwp0
#define HW_INTERVALDEF 100000L
/* this is a BIG TODO! Make plipbox source multi-board aware */
#define BOARD hwb->hwb_boards[0]
/* interrupt (choice here is INTB_EXTER or INTB_PORTS, depending on solder blob */
/*#define HW_INTSOURCE INTB_EXTER */ /* Int 6 */
#define HW_INTSOURCE INTB_PORTS /* Int 2 */
#define READREG(_x_,_off_) *( (volatile USHORT*)( (volatile UBYTE*)(_x_) + (_off_)) )
#define WRITEREG(_x_,_off_,_y_) *( (volatile USHORT*)( (volatile UBYTE*)(_x_) + (_off_)) ) = (_y_);
#define SETREG(_x_,_off_,_y_) *( (volatile USHORT*)( (volatile UBYTE*)(_x_) + (_off_) + SET_OFFSET )) = (_y_);
#define CLRREG(_x_,_off_,_y_) *( (volatile USHORT*)( (volatile UBYTE*)(_x_) + (_off_) + CLR_OFFSET )) = (_y_);
void myhw_ControlInterrupts( DEVBASEP );
void myhw_ControlIntervalTimer( DEVBASEP );
/* these calls might not run within the context of a process and
will be called from varying tasks and without prior init:
hw_Find_Boards()
hw_AllocBoard()
hw_ReleaseBoard()
hw_GetMacAddress()
these calls are in the context of the server task:
hw_Setup() - general init (board allocated already)
hw_Shutdown() - general shutdown (board still allocated)
hw_Attach() - make hardware ready for action (online)
hw_Detach() - put hardware into sleep mode (offline)
hw_recv_sigmask() - get signals for hardware (if any)
hw_recv_pending() - check for pending receive frames
hw_recv_frame() - receive one frame
hw_send_frame() - send one frame
hw_check_link_change() - check if HW link parameters need update
and apply them (called only when no RX/TX
in progress)
hw_config_init() - set defaults for HW
hw_config_update() - update config of HW
*/
const BYTE exp_name[] = "expansion.library";
const BYTE intname[] = "enc264j6net SanaII driver";
/* return number of boards present in system */
ASM SAVEDS LONG hw_Find_Boards( ASMR(a0) DEVBASEP ASMREG(a0) )
{
LONG i,ret = 0;
struct Library *ExpansionBase;
struct ConfigDev *cfg = (0);
if( (ExpansionBase = OpenLibrary( exp_name, 37 )) )
{
/* TODO: check for expansion port, i.e. distinguish V500/V600 */
/* TODO: actually verify that we have a module here */
ret = 1;
for( i=0 ; i < MAX_UNITS ; i++ )
{
cfg = FindConfigDev( cfg, ENC_MANUFACTURER, ENC_BOARD );
if( !cfg )
break;
db->db_Units[i].duh_BASE = (APTR)cfg->cd_BoardAddr;
D(("Board at %lx\n",(ULONG)cfg->cd_BoardAddr ));
}
ret = i;
D(("%ld boards found\n",ret));
CloseLibrary( ExpansionBase );
}
return ret;
}
ASM SAVEDS LONG hw_AllocBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
/* is there a way to lock a ZII Board ? */
return 1;
}
ASM SAVEDS LONG hw_ReleaseBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
return 1;
}
/* store MAC address at "mac", 6 Bytes */
ASM SAVEDS LONG hw_GetMacAddress( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *mac ASMREG(a1)
)
{
APTR boardbase = db->db_Units[unit].duh_BASE;
enc624j6l_GetMAC( boardbase, mac );
return 1;
}
/* prepare all boards such that a hw_attach() will succeed */
ASM SAVEDS LONG hw_Setup( ASMR(a0) DEVBASEP ASMREG(a0) )
{
LONG ret = 0;
struct HWData *hwd = &db->db_hwdat;
InitSemaphore( &hwd->hwd_Sem );
hwd->hwd_IntSig = AllocSignal(-1);
hwd->hwd_Interrupt.is_Data = (0);
if( InitIntervalTimer( &hwd->ivtimer ) > 0 )
{
hwd->hwd_SigMask = hwd->ivtimer.IVT_SigMask | (1<<(hwd->hwd_IntSig)) ;
ret = 1;
}
return ret;
}
/* free resources, server is quitting */
ASM SAVEDS void hw_Shutdown( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
LONG i;
/* safety measure */
for( i=0 ; i < db->db_NBoards ; i++ )
db->db_Units[i].duh_online = 0;
myhw_ControlInterrupts( db );
myhw_ControlIntervalTimer( db );
if( hwd->hwd_IntSig >= 0 )
FreeSignal( hwd->hwd_IntSig );
ExitIntervalTimer( &hwd->ivtimer );
}
/* bring hardware online */
ASM SAVEDS LONG hw_Attach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0))
{
struct HWData *hwd = &db->db_hwdat;
APTR boardbase = db->db_Units[unit].duh_BASE;
LONG ret = 1;
ULONG flags;
flags = PIO_INIT_BROAD_CAST;
if( hwd->multicast )
flags |= PIO_INIT_MULTI_CAST;
if( hwd->fullduplex )
flags |= PIO_INIT_FULL_DUPLEX;
if( hwd->flowcontrol )
flags |= PIO_INIT_FLOW_CONTROL;
if( enc624j6l_Init( boardbase, flags ) <= 0 )
ret = 0; /* ERROR IN CASE OF DEVICE NOT FOUND */
else
{
enc624j6l_SetMAC( boardbase, db->db_Units[unit].du_CFGAddr );
enc624j6l_SetOnline( boardbase );
db->db_Units[unit].duh_online = 1; /* used for int server and interval timer setup */
myhw_ControlInterrupts( db ); /* start and/or stop (based on duh_online) */
myhw_ControlIntervalTimer( db ); /* start and/or stop timer (based on duh_online) */
}
return ret;
}
/* put hardware into sleep mode (offline) */
ASM SAVEDS void hw_Detach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0))
{
APTR boardbase = db->db_Units[unit].duh_BASE;
db->db_Units[unit].duh_online = 0; /* used for int server and interval timer setup */
myhw_ControlInterrupts( db ); /* start and/or stop (based on duh_online) */
myhw_ControlIntervalTimer( db ); /* start and/or stop timer (based on duh_online) */
enc624j6l_SetOffline( boardbase );
enc624j6l_Shutdown( boardbase );
}
ASM SAVEDS void hw_ConfigInit( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
hwd->timervalue = HW_INTERVALDEF;
hwd->fullduplex = 0;
hwd->flowcontrol = 0;
hwd->multicast = 0;
#if 0
USHORT i;
for( i=0 ; i < (USHORT)db->db_NBoards ; i++ )
{ /* no need, these are defaults */
db->db_Units[i].du_MTU = 1500;
db->db_Units[i].du_BitPerSec = 10000000;
}
#endif
}
ASM SAVEDS void hw_ConfigUpdate( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a1) void*argsv ASMREG(a1))
{
struct HWConfig *args = (struct HWConfig *)argsv; /* see hwprivate.h */
struct HWData *hwd = &db->db_hwdat;
if( args->timervalue )
hwd->timervalue = *args->timervalue;
if( args->fullduplex )
hwd->fullduplex = 1;
if( args->flowcontrol )
hwd->flowcontrol = 1;
if( args->multicast )
hwd->multicast = 1;
}
ASM SAVEDS LONG hw_send_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1),
ASMR(d1) ULONG framesize ASMREG(d1) )
{
enc624j6l_TransmitFrame( db->db_Units[unit].duh_BASE, frame, framesize );
return 1;
}
ASM SAVEDS LONG hw_recv_pending( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
APTR board = db->db_Units[unit].duh_BASE;
return enc624j6l_HaveRecv( board );
}
ASM SAVEDS LONG hw_recv_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1))
{
LONG len;
APTR board = db->db_Units[unit].duh_BASE;
len = enc624j6l_RecvFrame( board, frame, 1522 );
return len;
}
ASM SAVEDS ULONG hw_recv_sigmask( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
return hwd->hwd_SigMask;
}
/* check if HW link parameters need update -> only when board is idle otherwise (no RX/TX) */
/* re-enables hardware interrupt */
ASM SAVEDS LONG hw_check_link_change( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
APTR boardbase = db->db_Units[unit].duh_BASE;
enc624j6l_CheckLinkChange( boardbase ); /* somewhere $e90000 and beyond */
if( db->db_Units[unit].duh_online )
enc624j6l_EnableGlobalInterrupt( boardbase );
return 1; /* don't signal panic to the outside, everything is under control */
}
ASM SAVEDS LONG hw_change_multicast( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a0) ULONG unit ASMREG(d0),
ASMR(a1) struct List *mclist ASMREG(a1) )
{
/* TODO: implement Multicast hashing */
return 1;
}
/* global for all boards (relies on hwd_online) */
void myhw_ControlInterrupts( DEVBASEP )
{
struct HWData *hwd = &db->db_hwdat;
LONG i,flag;
ObtainSemaphore( &hwd->hwd_Sem ); /* redundant here: server is a single task -> but it looks good :-) */
flag = 0;
for( i=0 ; i < db->db_NBoards ; i++ )
{
if( db->db_Units[i].duh_online )
{
hwd->hwd_act_boards[flag] = db->db_Units[i].duh_BASE;
flag++;
}
}
hwd->hwd_act_boards[flag] = (0); /* NULL-terminate */
hwd->hwd_act_boards[flag+1] = (APTR)FindTask(0); /* append task */
hwd->hwd_act_boards[flag+2] = (APTR)hwd->hwd_IntSig; /* append sigbit */
if( flag ) /* determine on/off switch */
{
/* at least 1 board is online */
if( hwd->hwd_Interrupt.is_Data )
flag = 0; /* no change */
else flag = 1; /* start timer */
}
else
{
if( hwd->hwd_Interrupt.is_Data )
flag = -1; /* stop timer */
else flag = 0; /* no change */
}
/* flag is >0 = start, <0 = stop or 0 = no change */
if( flag > 0 )
{
hwd->hwd_Interrupt.is_Code = (void(*)())enc624j6l_IntServer_List;
hwd->hwd_Interrupt.is_Data = &hwd->hwd_act_boards[0];
hwd->hwd_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
hwd->hwd_Interrupt.is_Node.ln_Pri = 51;
hwd->hwd_Interrupt.is_Node.ln_Name = (char*)intname;
AddIntServer( HW_INTSOURCE, &hwd->hwd_Interrupt );
}
for( i=0 ; i < db->db_NBoards ; i++ )
{
if( db->db_Units[i].duh_online )
enc624j6l_EnableInterrupt( db->db_Units[i].duh_BASE, FindTask(0), (int)hwd->hwd_IntSig, 0 );
else
enc624j6l_DisableInterrupt( db->db_Units[i].duh_BASE );
}
if( flag < 0 )
{
hwd->hwd_Interrupt.is_Data = (0);
RemIntServer( HW_INTSOURCE, &hwd->hwd_Interrupt );
hwd->hwd_Interrupt.is_Code = (0);
}
ReleaseSemaphore( &hwd->hwd_Sem );
}
/* global for all boards (relies on hwd_online) */
void myhw_ControlIntervalTimer( DEVBASEP )
{
struct HWData *hwd = &db->db_hwdat;
LONG i,flag;
ObtainSemaphore( &hwd->hwd_Sem );
flag = 0;
for( i=0 ; i < db->db_NBoards ; i++ )
{
if( db->db_Units[i].duh_online )
flag = 1;
}
if( flag )
{
/* at least 1 board is online */
if( hwd->ivtimer.IVT_Runflag )
flag = 0; /* no change */
else flag = 1; /* start timer */
}
else
{
if( hwd->ivtimer.IVT_Runflag )
flag = -1; /* stop timer */
else flag = 0; /* no change */
}
if( flag > 0 )
StartIntervalTimer( &hwd->ivtimer, hwd->timervalue );
if( flag < 0 )
StopIntervalTimer( &hwd->ivtimer );
ReleaseSemaphore( &hwd->hwd_Sem );
}
+52
View File
@@ -0,0 +1,52 @@
/*
hwprivate.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
private structures for hardware instance
*/
#ifndef _INC_HWPRIVATE_H
#define _INC_HWPRIVATE_H
#include "compiler.h"
/* sdnet/v2expnet specific: polling timer */
#include "intervaltimer.h"
/* this struct is available in devicebase and should carry global information */
struct HWData {
struct Interrupt hwd_Interrupt; /* keep the interrupt at top of HWData !! */
struct SignalSemaphore hwd_Sem; /* list locking (global per unit) */
APTR hwd_act_boards[MAX_UNITS+1+2]; /* poll all active boards by ptr in interrupt, NULL terminated, followed by SigTask,Sigbit */
ULONG hwd_SigMask; /* signal mask to wait for in server main instance */
struct IVT_TimerStruct ivtimer; /* Timer interval handling (via Interrupt) */
LONG TimerUsed; /* number of instances running */
LONG hwd_IntSig;
/* config options */
ULONG timervalue; /* polling interval in ms */
ULONG fullduplex; /* force full duplex */
ULONG multicast; /* multicast recv enable */
ULONG flowcontrol; /* enable RX flow control */
};
#define HW_CONFIGFILE "ENV:SANA2/enc624j6net.config"
/* appended to COMMON_TEMPLATE */
#define HW_CONFIGTEMPLATE "TIMER/K/N,FULLDUPLEX/S,MULTICAST/S,FLOWCONTROL/S"
/* referenced by server.c, hw.c */
struct HWConfig
{
struct CommonConfig common; /* import from hw.h, expected in server.c */
ULONG *timervalue;
ULONG fullduplex;
ULONG multicast;
ULONG flowcontrol;
};
#endif /* _INC_HWPRIVATE_H */
+341
View File
@@ -0,0 +1,341 @@
******************************************************************************
* Name: intervaltimer.s *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
;MACHINE MC68040
include exec/types.i
include lvo/exec_lib.i
include lvo/dos_lib.i
include lvo/timer_lib.i
include exec/exec.i
include dos/dos.i
include dos/dostags.i
include exec/io.i
include dos/dosextens.i
include exec/lists.i
DO_TEST EQU 0
ifne 1
include "intervaltimer.i"
else
STRUCTURE IVT_TimerStruct,0
ULONG IVT_Interval ;<1e6 in microseconds
LONG IVT_Signal ;Signal that is sent back to task from interrupt
ULONG IVT_SigMask
APTR IVT_SigTask ;Task that is signaled
APTR IVT_Port
APTR IVT_IORequest
APTR IVT_Device
STRUCT IVT_Int,IS_SIZE ;
BYTE IVT_Init ; !=0 - initialized
BYTE IVT_Runflag ; current status ( !=0 = running )
BYTE IVT_Stopflag ; stop request if != 0
BYTE IVT_Unused ;
ULONG IVT_Counter ; Counter (statistics)
LABEL IVT_Size
endc
xdef _InterruptIntervalTimer
xdef _InitIntervalTimer
xdef _ExitIntervalTimer
xdef _StartIntervalTimer
xdef _StopIntervalTimer
CALLEXEC macro
move.l 4.w,a6
jsr _LVO\1(a6)
endm
section code,text
ifne DO_TEST
TestTimer:
lea dos_name,a1 ;string base
moveq #0,d0
move.l 4.w,a6
jsr _LVOOpenLibrary(a6)
move.l d0,dosbase
beq .nodos
move.l dosbase,a6
jsr _LVOOutput(a6) ;get stdout
move.l d0,stdout
lea timerbase,a1
bsr _InitIntervalTimer
tst.l d0
bge .ok
move.l stdout,d0
move.l #failtimer,d1
move.l dosbase,16
jsr _LVOPutStr(a6)
bra .error
.ok
lea timerbase,a1
move.l #10000,d1
bsr _StartIntervalTimer
moveq #0,d3
.loop
cmp.l #10,d3
bgt.s .exitloop
move.l dosbase,A6
moveq #10,d1
jsr _LVODelay(a6)
addq.l #1,d3
lea timerbase,a1
cmp.l #100,IVT_Counter(a1)
blt.s .loop
.exitloop:
move.l #succname,d1
cmp.l #10,d3
ble.s .succ
move.l #failname,d1
.succ:
jsr _LVOVPrintf(a6)
lea timerbase,a1
bsr _StopIntervalTimer
.error
lea timerbase,a1
bsr _ExitIntervalTimer
move.l dosbase,d0
beq.s .nodos
move.l d0,a1
move.l 4.w,a6
jsr _LVOCloseLibrary(a6)
.nodos:
rts
ENDC ;DO_TEST
; A1 = IVT_TimerStruct
_InterruptIntervalTimer:
movem.l d1-a6,-(sp)
;<- basic Timer.device ops ->
move.l a1,a2
move.l IVT_Port(a2),a0
CALLEXEC GetMsg
tst.l d0
beq.s .error
move.l IVT_SigMask(a2),d0 ;move.l IVT_Signal(a2),d0
move.l IVT_SigTask(a2),a1
CALLEXEC Signal
tst.b IVT_Stopflag(a2)
bne.s .error ;not exactly an error but same consequence
move.l a2,a1
bsr _RestartIntervalTimer
;<- end basic Timer.device ops ->
;<- useful stuff ->
addq.l #1,IVT_Counter(a2)
;<- useful stuff ->
bra.s .endintroutine
.error:
clr.b IVT_Runflag(a2)
.endintroutine:
movem.l (sp)+,d1-a6
rts
_InitIntervalTimer: ;prepare timer.device for buffer swap interrupt
movem.l d1-a6,-(sp)
move.l a1,a2
move #IVT_Size-1,d0
.clr
clr.b (a1)+ ;
dbf d0,.clr
sf IVT_Init(a2)
sf IVT_Runflag(a2)
;signal to wait for from timer output if output queue full
moveq #-1,d0
CALLEXEC AllocSignal
move.l d0,IVT_Signal(a2) ; <0 ? error, valid are 0..31
blt .error ; error: have to go (shouldn't happen)
moveq #0,d1
bset d0,d1
move.l d1,IVT_SigMask(a2)
suba.l a1,a1
CALLEXEC FindTask
move.l d0,IVT_SigTask(a2)
CALLEXEC CreateMsgPort ;Create MsgPort for timing...
move.l d0,IVT_Port(a2)
beq .error ;0=error
move.l IVT_Port(a2),a0
move.l #IOTV_SIZE,d0
CALLEXEC CreateIORequest
move.l d0,IVT_IORequest(a2)
beq .error ;0=error
lea.l IVT_Int(a2),a0
lea _InterruptIntervalTimer(pc),a1
move.l a1,IS_CODE(a0)
move.l a2,IS_DATA(a0)
move.b #NT_INTERRUPT,LN_TYPE(a0)
move.b #0,LN_PRI(a0) ;-32,-16,0,16,32
move.l IVT_Port(a2),a1
;move.b #NT_MSGPORT,LN_TYPE(a1)
move.b #PA_SOFTINT,MP_FLAGS(a1)
move.l a0,MP_SIGTASK(a1) ;Softint eintragen
lea VBtimer_name,a0
moveq #UNIT_MICROHZ,d0
move.l IVT_IORequest(a2),a1
moveq #0,d1
CALLEXEC OpenDevice
tst.l d0
beq.s .ok
moveq #0,d0
bra.s .error
.ok ;OK, timer device is ready
st IVT_Init(a2)
moveq #1,d0 ;OK
.error
movem.l (sp)+,d1-a6
;ret 0 <= err, >0 = ok
rts
_ExitIntervalTimer:
;stop timer.device
movem.l d0-a6,-(sp)
move.l a1,a2
sf IVT_Init(a2)
move.l IVT_IORequest(a2),d5
beq.s .noIO
tst.b IVT_Runflag(a2)
beq.s .noAbort
st IVT_Stopflag(a2)
ifne 0
move.l d5,a1
CALLEXEC WaitIO
else
move.l d5,a1
move.l IO_DEVICE(a1),a6
jsr DEV_ABORTIO(A6) ;clear pending requests
endc
.noAbort:
move.l d5,a1
CALLEXEC CloseDevice
move.l d5,a0
CALLEXEC DeleteIORequest
clr.l IVT_IORequest(a2)
.noIO:
move.l IVT_Port(a2),d0
beq.s .noPort
move.l d0,a0
CALLEXEC DeleteMsgPort
clr.l IVT_Port(a2)
.noPort:
move.l IVT_Signal(a2),d0
not.l d0 ;if( d0 == -1 ) d0 = 0
beq.s .nosig
not.l d0 ; swap back
CALLEXEC FreeSignal
moveq #-1,d0
move.l d0,IVT_Signal(a2)
.nosig:
movem.l (sp)+,d0-a6
rts
; input:
; A1 = IVT Structure (after init)
; D1 = Number of Microseconds
_StartIntervalTimer:
movem.l d0/a1/a2/a6,-(Sp)
move.l a1,a2
move.l d1,IVT_Interval(A2)
sf IVT_Stopflag(a2)
tst.b IVT_Runflag(a2) ;running already ? -> keep it running
bne.s .notimer
move.l a2,a1
bsr _RestartIntervalTimer
.notimer
movem.l (sp)+,d0/a1/a2/a6
rts
; Non-Public
; A1 - IVT Structure
_RestartIntervalTimer:
movem.l d0/a1/a2/a6,-(sp)
move.l a1,a2
move.l IVT_IORequest(a2),d0
beq.s .notimer
move.l d0,a1
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.b IO_FLAGS(a1)
clr.l IOTV_TIME+EV_HI(a1)
move.l IVT_Interval(A2),IOTV_TIME+EV_LO(a1)
move.l IO_DEVICE(a1),a6
jsr DEV_BEGINIO(A6)
st IVT_Runflag(a2)
.notimer
movem.l (sp)+,d0/a1/a2/a6
rts
_StopIntervalTimer:
movem.l d0-a6,-(Sp)
move.l a1,a2
st IVT_Stopflag(a2)
tst.b IVT_Runflag(a2)
beq.s .noIO
move.l IVT_IORequest(a2),d5
beq.s .noIO
move.l d5,a1
move.l IO_DEVICE(a1),a6
jsr DEV_ABORTIO(A6) ;clear pending requests
sf IVT_Runflag(a2)
.noIO:
movem.l (sp)+,d0-a6
rts
section data,data
dosbase: dc.l 0
stdout: dc.l 0
timerbase: ds.b IVT_Size
VBtimer_name: dc.b "timer.device",0
ifne DO_TEST
dos_name: dc.b "dos.library",0
succname: dc.b "success: timer worked",10,0
failname: dc.b "failure: timer didn't work",10,0
failtimer: dc.b "timer open failure",10,0
ENDC ;DO_TEST
END
+57
View File
@@ -0,0 +1,57 @@
/*
******************************************************************************
* Name: intervaltimer.h *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
*/
#ifndef _INC_INTERVALTIMER_H
#define _INC_INTERVALTIMER_H
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <devices/timer.h>
struct IVT_TimerStruct {
unsigned long IVT_Interval;
long IVT_Signal;
unsigned long IVT_SigMask;
struct Task *IVT_SigTask;
struct MsgPort *IVT_Port;
struct IORequest *IVT_IORequest;
struct Device *IVT_Device;
struct Interrupt IVT_Int;
unsigned char IVT_Init;
unsigned char IVT_Runflag;
unsigned char IVT_Stopflag;
unsigned char IVT_Unused;
unsigned long IVT_Counter;
};
#if 1
#include "compiler.h"
#else
/* self-contained in here as well */
#ifdef __SASC
#define ASM __asm
#define ASMR(x) register __ ## x
#define ASMREG(x)
#define SAVEDS __saveds
#else
#define ASM
#define ASMR(x) register
#define ASMREG(x) __asm("" #x "")
#define SAVEDS __saveds
#endif
#endif
ASM SAVEDS int InitIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
ASM SAVEDS int StartIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1),
ASMR(d1) unsigned long Microsecs ASMREG(d1) );
ASM SAVEDS int StopIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
ASM SAVEDS int ExitIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
#endif /* _INC_INTERVALTIMER_H */
+27
View File
@@ -0,0 +1,27 @@
******************************************************************************
* Name: intervaltimer.i *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
ifnd _INC_INTERVALTIMER_I
_INC_INTERVALTIMER_I EQU 1
include exec/types.i
STRUCTURE IVT_TimerStruct,0
ULONG IVT_Interval ;<1e6 in microseconds
LONG IVT_Signal ;Signal that is sent back to task from interrupt
ULONG IVT_SigMask ;signal mask
APTR IVT_SigTask ;Task that is signaled
APTR IVT_Port
APTR IVT_IORequest
APTR IVT_Device
STRUCT IVT_Int,IS_SIZE ;
BYTE IVT_Init ; !=0 - initialized
BYTE IVT_Runflag ; current status ( !=0 = running )
BYTE IVT_Stopflag ; stop request if != 0
BYTE IVT_Unused ;
ULONG IVT_Counter ; Counter (statistics)
LABEL IVT_Size
endc ;_INC_TIMERTEST_I
+126
View File
@@ -0,0 +1,126 @@
; ------------------------------------------------------------------------------
; | Lowlevel Access to memory mapped ENC624J600 in PSP mode |
; | Henryk Richter <henryk.richter@gmx.net> |
; ------------------------------------------------------------------------------
; macro section
;------------------ options, some depend on hardware (CPLD) configuration -------------------------
_OPT_BUFFER_SWAP EQU 0 ;perform byte swap on buffer ops (1) or assume native endian (0)
_OPT_REG_SWAP EQU 0 ;perform byte swap on register ops (1) or assume native endian (0)
_OPT_ADR_QUIRK EQU 0 ;address lines 12/13 are swapped (1) or linear addressing (0)
_OPT_FLOWCONTROL EQU 0 ;perform flow control on RX (1) or not (0)
_OPT_RECV EQU 1 ;faster recv (1) or generic (0)
_OPT_CHECKLINK_TX EQU 0 ;check for link changes in TX (1) or expect from outside (0) -> see server.c
;\1 register offset (without CLEAR offset)
;\2 address register of board I/O base
;\3 register with clear mask
ifne _OPT_REG_SWAP
CLRREG macro
rol.w #8,\3
move.w \3,\1+CLR_OFFSET(\2)
rol.w #8,\3
endm
;\1 register offset (without SET offset)
;\2 address register of board I/O base
;\3 register with set mask
SETREG macro
rol.w #8,\3
move.w \3,\1+SET_OFFSET(\2)
rol.w #8,\3
endm
;\1 register offset (e.g. ESTAT)
;\2 address register of board I/O base (e.g. a0)
;\3 destination register to be written (e.g. d0)
;
;important: when changing this, make sure the flags
;are set according to the return value
READREG macro
rol.w #8,\3
move.w \1(\2),\3
rol.w #8,\3
endm
;\1 register offset (e.g. ESTAT)
;\2 address register of board I/O base (e.g. a0)
;\3 soure register copied to HW register (e.g. d0)
WRITEREG macro
rol.w #8,\3
move.w \3,\1(\2)
rol.w #8,\3
endm
else
CLRREG macro
move.w \3,\1+CLR_OFFSET(\2)
endm
;\1 register offset (without SET offset)
;\2 address register of board I/O base
;\3 register with set mask
SETREG macro
move.w \3,\1+SET_OFFSET(\2)
endm
;\1 register offset (e.g. ESTAT)
;\2 address register of board I/O base (e.g. a0)
;\3 destination register to be written (e.g. d0)
;
;important: when changing this, make sure the flags
;are set according to the return value
READREG macro
move.w \1(\2),\3
endm
;\1 register offset (e.g. ESTAT)
;\2 address register of board I/O base (e.g. a0)
;\3 soure register copied to HW register (e.g. d0)
WRITEREG macro
move.w \3,\1(\2)
endm
endc
;read with address shuffling or direct from SRAM (affects $2000-$5000)
ifne _OPT_ADR_QUIRK
;\1 = address register
;\2 = data register, offset
;\3 = destination
READSRAM macro
eor.w #$6000,\2
move.w (\1,\2.w),\3
eor.w #$6000,\2
endm
else
READSRAM macro
move.w (\1,\2),\3
endm
endc
;read with address shuffling or direct from SRAM (affects $2000-$5000)
ifne _OPT_ADR_QUIRK
;\1 = address register
;\2 = data register, offset
;\3 = destination
READSRAM_LONG macro
eor.w #$6000,\2
move.l (\1,\2.w),\3
eor.w #$6000,\2
endm
else
READSRAM_LONG macro
move.l (\1,\2),\3
endm
endc
; wrap position index (in bytes) \1 (data register .w)
;
; if position >RXSTOP_INIT-1
; position = (position-RXSTOP_INIT+RXSTART_INIT)
; endif
WRAPINDEX macro
cmp.w #RXSTOP_INIT&$fffe,\1 ;wrap Dn if beyond buffer
ble.s .nowrap\0\@
add.w #RXSTART_INIT-RXSTOP_INIT-1,\1
.nowrap\0\@
endm
+606
View File
@@ -0,0 +1,606 @@
/* ------------------------------------------------------------------------------ */
/* | Lowlevel Access to memory mapped ENC624J600 in PSP mode | */
/* | Henryk Richter <henryk.richter@gmx.net> | */
/* ------------------------------------------------------------------------------ */
/* register definition section */
#ifndef _REGISTERS_H_
#define _REGISTERS_H_
/* set/clr register offsets */
#define SET_OFFSET 0x0100
#define CLR_OFFSET 0x0180
/* Register definitions */
/* SPI Bank 0 registers -------- */
#define ETXST 0x7E00
#define ETXSTL 0x7E00
#define ETXSTH 0x7E01
#define ETXLEN 0x7E02
#define ETXLENL 0x7E02
#define ETXLENH 0x7E03
#define ERXST 0x7E04
#define ERXSTL 0x7E04
#define ERXSTH 0x7E05
#define ERXTAIL 0x7E06
#define ERXTAILL 0x7E06
#define ERXTAILH 0x7E07
#define ERXHEAD 0x7E08
#define ERXHEADL 0x7E08
#define ERXHEADH 0x7E09
#define EDMAST 0x7E0A
#define EDMASTL 0x7E0A
#define EDMASTH 0x7E0B
#define EDMALEN 0x7E0C
#define EDMALENL 0x7E0C
#define EDMALENH 0x7E0D
#define EDMADST 0x7E0E
#define EDMADSTL 0x7E0E
#define EDMADSTH 0x7E0F
#define EDMACS 0x7E10
#define EDMACSL 0x7E10
#define EDMACSH 0x7E11
#define ETXSTAT 0x7E12
#define ETXSTATL 0x7E12
#define ETXSTATH 0x7E13
#define ETXWIRE 0x7E14
#define ETXWIREL 0x7E14
#define ETXWIREH 0x7E15
/* SPI all bank registers */
#define EUDAST 0x7E16
#define EUDASTL 0x7E16
#define EUDASTH 0x7E17
#define EUDAND 0x7E18
#define EUDANDL 0x7E18
#define EUDANDH 0x7E19
#define ESTAT 0x7E1A
#define ESTATL 0x7E1A
#define ESTATH 0x7E1B
#define EIR 0x7E1C
#define EIRL 0x7E1C
#define EIRH 0x7E1D
#define ECON1 0x7E1E
#define ECON1L 0x7E1E
#define ECON1H 0x7E1F
/* SPI Bank 1 registers ----- */
#define EHT1 0x7E20
#define EHT1L 0x7E20
#define EHT1H 0x7E21
#define EHT2 0x7E22
#define EHT2L 0x7E22
#define EHT2H 0x7E23
#define EHT3 0x7E24
#define EHT3L 0x7E24
#define EHT3H 0x7E25
#define EHT4 0x7E26
#define EHT4L 0x7E26
#define EHT4H 0x7E27
#define EPMM1 0x7E28
#define EPMM1L 0x7E28
#define EPMM1H 0x7E29
#define EPMM2 0x7E2A
#define EPMM2L 0x7E2A
#define EPMM2H 0x7E2B
#define EPMM3 0x7E2C
#define EPMM3L 0x7E2C
#define EPMM3H 0x7E2D
#define EPMM4 0x7E2E
#define EPMM4L 0x7E2E
#define EPMM4H 0x7E2F
#define EPMCS 0x7E30
#define EPMCSL 0x7E30
#define EPMCSH 0x7E31
#define EPMO 0x7E32
#define EPMOL 0x7E32
#define EPMOH 0x7E33
#define ERXFCON 0x7E34
#define ERXFCONL 0x7E34
#define ERXFCONH 0x7E35
/* SPI all bank registers from 0x36 to 0x3F */
/* SPI Bank 2 registers ----- */
#define MACON1 0x7E40
#define MACON1L 0x7E40
#define MACON1H 0x7E41
#define MACON2 0x7E42
#define MACON2L 0x7E42
#define MACON2H 0x7E43
#define MABBIPG 0x7E44
#define MABBIPGL 0x7E44
#define MABBIPGH 0x7E45
#define MAIPG 0x7E46
#define MAIPGL 0x7E46
#define MAIPGH 0x7E47
#define MACLCON 0x7E48
#define MACLCONL 0x7E48
#define MACLCONH 0x7E49
#define MAMXFL 0x7E4A
#define MAMXFLL 0x7E4A
#define MAMXFLH 0x7E4B
#define MICMD 0x7E52
#define MICMDL 0x7E52
#define MICMDH 0x7E53
#define MIREGADR 0x7E54
#define MIREGADRL 0x7E54
#define MIREGADRH 0x7E55
/* SPI all bank registers from 0x56 to 0x5F */
/* SPI Bank 3 registers ----- */
#define MAADR3 0x7E60
#define MAADR3L 0x7E60
#define MAADR3H 0x7E61
#define MAADR2 0x7E62
#define MAADR2L 0x7E62
#define MAADR2H 0x7E63
#define MAADR1 0x7E64
#define MAADR1L 0x7E64
#define MAADR1H 0x7E65
#define MIWR 0x7E66
#define MIWRL 0x7E66
#define MIWRH 0x7E67
#define MIRD 0x7E68
#define MIRDL 0x7E68
#define MIRDH 0x7E69
#define MISTAT 0x7E6A
#define MISTATL 0x7E6A
#define MISTATH 0x7E6B
#define EPAUS 0x7E6C
#define EPAUSL 0x7E6C
#define EPAUSH 0x7E6D
#define ECON2 0x7E6E
#define ECON2L 0x7E6E
#define ECON2H 0x7E6F
#define ERXWM 0x7E70
#define ERXWML 0x7E70
#define ERXWMH 0x7E71
#define EIE 0x7E72
#define EIEL 0x7E72
#define EIEH 0x7E73
#define EIDLED 0x7E74
#define EIDLEDL 0x7E74
#define EIDLEDH 0x7E75
/* SPI all bank registers from 0x66 to 0x6F */
/* SPI Non-banked Special Function Registers */
#define EGPDATA 0x7E80
#define EGPDATAL 0x7E80
#define ERXDATA 0x7E82
#define ERXDATAL 0x7E82
#define EUDADATA 0x7E84
#define EUDADATAL 0x7E84
#define EGPRDPT 0x7E86
#define EGPRDPTL 0x7E86
#define EGPRDPTH 0x7E87
#define EGPWRPT 0x7E88
#define EGPWRPTL 0x7E88
#define EGPWRPTH 0x7E89
#define ERXRDPT 0x7E8A
#define ERXRDPTL 0x7E8A
#define ERXRDPTH 0x7E8B
#define ERXWRPT 0x7E8C
#define ERXWRPTL 0x7E8C
#define ERXWRPTH 0x7E8D
#define EUDARDPT 0x7E8E
#define EUDARDPTL 0x7E8E
#define EUDARDPTH 0x7E8F
#define EUDAWRPT 0x7E90
#define EUDAWRPTL 0x7E90
#define EUDAWRPTH 0x7E91
/* ;;;;;;;;;;;;;;;;;;;;;;;;; */
/* ENC424J600/624J600 register bits */
/* ;;;;;;;;;;;;;;;;;;;;;;;;; */
/* ESTAT bits ---------- */
#define ESTAT_INT (1<<15)
#define ESTAT_FCIDLE (1<<14)
#define ESTAT_RXBUSY (1<<13)
#define ESTAT_CLKRDY (1<<12)
#define ESTAT_RSTDONE (1<<11)
#define ESTAT_PHYDPX (1<<10)
#define ESTAT_PHYRDY (1<<9)
#define ESTAT_PHYLNK (1<<8)
#define ESTAT_PKTCNT7 (1<<7)
#define ESTAT_PKTCNT6 (1<<6)
#define ESTAT_PKTCNT5 (1<<5)
#define ESTAT_PKTCNT4 (1<<4)
#define ESTAT_PKTCNT3 (1<<3)
#define ESTAT_PKTCNT2 (1<<2)
#define ESTAT_PKTCNT1 (1<<1)
#define ESTAT_PKTCNT0 (1)
/* EIR bits ------------ */
#define EIR_CRYPTEN (1<<15)
#define EIR_MODEXIF (1<<14)
#define EIR_HASHIF (1<<13)
#define EIR_AESIF (1<<12)
#define EIR_LINKIF (1<<11)
#define EIR_PRDYIF (1<<10)
#define EIR_r9 (1<<9)
#define EIR_r8 (1<<8)
#define EIR_r7 (1<<7)
#define EIR_PKTIF (1<<6)
#define EIR_DMAIF (1<<5)
#define EIR_r4 (1<<4)
#define EIR_TXIF (1<<3)
#define EIR_TXABTIF (1<<2)
#define EIR_RXABTIF (1<<1)
#define EIR_PCFULIF (1)
/* ECON1 bits ---------- */
#define ECON1_MODEXST (1<<15)
#define ECON1_HASHEN (1<<14)
#define ECON1_HASHOP (1<<13)
#define ECON1_HASHLST (1<<12)
#define ECON1_AESST (1<<11)
#define ECON1_AESOP1 (1<<10)
#define ECON1_AESOP0 (1<<9)
#define ECON1_PKTDEC (1<<8)
#define ECON1_FCOP1 (1<<7)
#define ECON1_FCOP0 (1<<6)
#define ECON1_DMAST (1<<5)
#define ECON1_DMACPY (1<<4)
#define ECON1_DMACSSD (1<<3)
#define ECON1_DMANOCS (1<<2)
#define ECON1_TXRTS (1<<1)
#define ECON1_RXEN (1)
/* ETXSTAT bits -------- */
#define ETXSTAT_r12 (1<<12)
#define ETXSTAT_r11 (1<<11)
#define ETXSTAT_LATECOL (1<<10)
#define ETXSTAT_MAXCOL (1<<9)
#define ETXSTAT_EXDEFER (1<<8)
#define ETXSTAT_DEFER (1<<7)
#define ETXSTAT_r6 (1<<6)
#define ETXSTAT_r5 (1<<5)
#define ETXSTAT_CRCBAD (1<<4)
#define ETXSTAT_COLCNT3 (1<<3)
#define ETXSTAT_COLCNT2 (1<<2)
#define ETXSTAT_COLCNT1 (1<<1)
#define ETXSTAT_COLCNT0 (1)
/* ERXFCON bits -------- */
#define ERXFCON_HTEN (1<<15)
#define ERXFCON_MPEN (1<<14)
#define ERXFCON_NOTPM (1<<12)
#define ERXFCON_PMEN3 (1<<11)
#define ERXFCON_PMEN2 (1<<10)
#define ERXFCON_PMEN1 (1<<9)
#define ERXFCON_PMEN0 (1<<8)
#define ERXFCON_CRCEEN (1<<7)
#define ERXFCON_CRCEN (1<<6)
#define ERXFCON_RUNTEEN (1<<5)
#define ERXFCON_RUNTEN (1<<4)
#define ERXFCON_UCEN (1<<3)
#define ERXFCON_NOTMEEN (1<<2)
#define ERXFCON_MCEN (1<<1)
#define ERXFCON_BCEN (1)
/* MACON1 bits --------- */
#define MACON1_r15 (1<<15)
#define MACON1_r14 (1<<14)
#define MACON1_r11 (1<<11)
#define MACON1_r10 (1<<10)
#define MACON1_r9 (1<<9)
#define MACON1_r8 (1<<8)
#define MACON1_LOOPBK (1<<4)
#define MACON1_r3 (1<<3)
#define MACON1_RXPAUS (1<<2)
#define MACON1_PASSALL (1<<1)
#define MACON1_r0 (1)
/* MACON2 bits --------- */
#define MACON2_DEFER (1<<14)
#define MACON2_BPEN (1<<13)
#define MACON2_NOBKOFF (1<<12)
#define MACON2_r9 (1<<9)
#define MACON2_r8 (1<<8)
#define MACON2_PADCFG2 (1<<7)
#define MACON2_PADCFG1 (1<<6)
#define MACON2_PADCFG0 (1<<5)
#define MACON2_TXCRCEN (1<<4)
#define MACON2_PHDREN (1<<3)
#define MACON2_HFRMEN (1<<2)
#define MACON2_r1 (1<<1)
#define MACON2_FULDPX (1)
/* MABBIPG bits -------- */
#define MABBIPG_BBIPG6 (1<<6)
#define MABBIPG_BBIPG5 (1<<5)
#define MABBIPG_BBIPG4 (1<<4)
#define MABBIPG_BBIPG3 (1<<3)
#define MABBIPG_BBIPG2 (1<<2)
#define MABBIPG_BBIPG1 (1<<1)
#define MABBIPG_BBIPG0 (1)
/* MAIPG bits ---------- */
#define MAIPG_r14 (1<<14)
#define MAIPG_r13 (1<<13)
#define MAIPG_r12 (1<<12)
#define MAIPG_r11 (1<<11)
#define MAIPG_r10 (1<<10)
#define MAIPG_r9 (1<<9)
#define MAIPG_r8 (1<<8)
#define MAIPG_IPG6 (1<<6)
#define MAIPG_IPG5 (1<<5)
#define MAIPG_IPG4 (1<<4)
#define MAIPG_IPG3 (1<<3)
#define MAIPG_IPG2 (1<<2)
#define MAIPG_IPG1 (1<<1)
#define MAIPG_IPG0 (1)
/* MACLCON bits -------- */
#define MACLCON_r13 (1<<13)
#define MACLCON_r12 (1<<12)
#define MACLCON_r11 (1<<11)
#define MACLCON_r10 (1<<10)
#define MACLCON_r9 (1<<9)
#define MACLCON_r8 (1<<8)
#define MACLCON_MAXRET3 (1<<3)
#define MACLCON_MAXRET2 (1<<2)
#define MACLCON_MAXRET1 (1<<1)
#define MACLCON_MAXRET0 (1)
/* MICMD bits ---------- */
#define MICMD_MIISCAN (1<<1)
#define MICMD_MIIRD (1)
/* MIREGADR bits ------- */
#define MIREGADR_r12 (1<<12)
#define MIREGADR_r11 (1<<11)
#define MIREGADR_r10 (1<<10)
#define MIREGADR_r9 (1<<9)
#define MIREGADR_r8 (1<<8)
#define MIREGADR_PHREG4 (1<<4)
#define MIREGADR_PHREG3 (1<<3)
#define MIREGADR_PHREG2 (1<<2)
#define MIREGADR_PHREG1 (1<<1)
#define MIREGADR_PHREG0 (1)
/* MISTAT bits --------- */
#define MISTAT_r3 (1<<3)
#define MISTAT_NVALID (1<<2)
#define MISTAT_SCAN (1<<1)
#define MISTAT_BUSY (1)
/* ECON2 bits ---------- */
#define ECON2_ETHEN (1<<15)
#define ECON2_STRCH (1<<14)
#define ECON2_TXMAC (1<<13)
#define ECON2_SHA1MD5 (1<<12)
#define ECON2_COCON3 (1<<11)
#define ECON2_COCON2 (1<<10)
#define ECON2_COCON1 (1<<9)
#define ECON2_COCON0 (1<<8)
#define ECON2_AUTOFC (1<<7)
#define ECON2_TXRST (1<<6)
#define ECON2_RXRST (1<<5)
#define ECON2_ETHRST (1<<4)
#define ECON2_MODLEN1 (1<<3)
#define ECON2_MODLEN0 (1<<2)
#define ECON2_AESLEN1 (1<<1)
#define ECON2_AESLEN0 (1)
/* ERXWM bits ---------- */
#define ERXWM_RXFWM7 (1<<15)
#define ERXWM_RXFWM6 (1<<14)
#define ERXWM_RXFWM5 (1<<13)
#define ERXWM_RXFWM4 (1<<12)
#define ERXWM_RXFWM3 (1<<11)
#define ERXWM_RXFWM2 (1<<10)
#define ERXWM_RXFWM1 (1<<9)
#define ERXWM_RXFWM0 (1<<8)
#define ERXWM_RXEWM7 (1<<7)
#define ERXWM_RXEWM6 (1<<6)
#define ERXWM_RXEWM5 (1<<5)
#define ERXWM_RXEWM4 (1<<4)
#define ERXWM_RXEWM3 (1<<3)
#define ERXWM_RXEWM2 (1<<2)
#define ERXWM_RXEWM1 (1<<1)
#define ERXWM_RXEWM0 (1)
/* EIE bits ------------ */
#define EIE_INTIE (1<<15)
#define EIE_MODEXIE (1<<14)
#define EIE_HASHIE (1<<13)
#define EIE_AESIE (1<<12)
#define EIE_LINKIE (1<<11)
#define EIE_PRDYIE (1<<10)
#define EIE_r9 (1<<9)
#define EIE_r8 (1<<8)
#define EIE_r7 (1<<7)
#define EIE_PKTIE (1<<6)
#define EIE_DMAIE (1<<5)
#define EIE_r4 (1<<4)
#define EIE_TXIE (1<<3)
#define EIE_TXABTIE (1<<2)
#define EIE_RXABTIE (1<<1)
#define EIE_PCFULIE (1)
/* EIDLED bits --------- */
#define EIDLED_LACFG3 (1<<15)
#define EIDLED_LACFG2 (1<<14)
#define EIDLED_LACFG1 (1<<13)
#define EIDLED_LACFG0 (1<<12)
#define EIDLED_LBCFG3 (1<<11)
#define EIDLED_LBCFG2 (1<<10)
#define EIDLED_LBCFG1 (1<<9)
#define EIDLED_LBCFG0 (1<<8)
#define EIDLED_DEVID2 (1<<7)
#define EIDLED_DEVID1 (1<<6)
#define EIDLED_DEVID0 (1<<5)
#define EIDLED_REVID4 (1<<4)
#define EIDLED_REVID3 (1<<3)
#define EIDLED_REVID2 (1<<2)
#define EIDLED_REVID1 (1<<1)
#define EIDLED_REVID0 (1)
/* PHCON1 bits --------- */
#define PHCON1_PRST (1<<15)
#define PHCON1_PLOOPBK (1<<14)
#define PHCON1_SPD100 (1<<13)
#define PHCON1_ANEN (1<<12)
#define PHCON1_PSLEEP (1<<11)
#define PHCON1_r10 (1<<10)
#define PHCON1_RENEG (1<<9)
#define PHCON1_PFULDPX (1<<8)
#define PHCON1_r7 (1<<7)
#define PHCON1_r6 (1<<6)
#define PHCON1_r5 (1<<5)
#define PHCON1_r4 (1<<4)
#define PHCON1_r3 (1<<3)
#define PHCON1_r2 (1<<2)
#define PHCON1_r1 (1<<1)
#define PHCON1_r0 (1)
/* PHSTAT1 bits -------- */
#define PHSTAT1_r15 (1<<15)
#define PHSTAT1_FULL100 (1<<14)
#define PHSTAT1_HALF100 (1<<13)
#define PHSTAT1_FULL10 (1<<12)
#define PHSTAT1_HALF10 (1<<11)
#define PHSTAT1_r10 (1<<10)
#define PHSTAT1_r9 (1<<9)
#define PHSTAT1_r8 (1<<8)
#define PHSTAT1_r7 (1<<7)
#define PHSTAT1_r6 (1<<6)
#define PHSTAT1_ANDONE (1<<5)
#define PHSTAT1_LRFAULT (1<<4)
#define PHSTAT1_ANABLE (1<<3)
#define PHSTAT1_LLSTAT (1<<2)
#define PHSTAT1_r1 (1<<1)
#define PHSTAT1_EXTREGS (1)
/* PHANA bits ---------- */
#define PHANA_ADNP (1<<15)
#define PHANA_r14 (1<<14)
#define PHANA_ADFAULT (1<<13)
#define PHANA_r12 (1<<12)
#define PHANA_ADPAUS1 (1<<11)
#define PHANA_ADPAUS0 (1<<10)
#define PHANA_r9 (1<<9)
#define PHANA_AD100FD (1<<8)
#define PHANA_AD100 (1<<7)
#define PHANA_AD10FD (1<<6)
#define PHANA_AD10 (1<<5)
#define PHANA_ADIEEE4 (1<<4)
#define PHANA_ADIEEE3 (1<<3)
#define PHANA_ADIEEE2 (1<<2)
#define PHANA_ADIEEE1 (1<<1)
#define PHANA_ADIEEE0 (1)
/* PHANLPA bits -------- */
#define PHANLPA_LPNP (1<<15)
#define PHANLPA_LPACK (1<<14)
#define PHANLPA_LPFAULT (1<<13)
#define PHANLPA_r12 (1<<12)
#define PHANLPA_LPPAUS1 (1<<11)
#define PHANLPA_LPPAUS0 (1<<10)
#define PHANLPA_LP100T4 (1<<9)
#define PHANLPA_LP100FD (1<<8)
#define PHANLPA_LP100 (1<<7)
#define PHANLPA_LP10FD (1<<6)
#define PHANLPA_LP10 (1<<5)
#define PHANLPA_LPIEEE4 (1<<4)
#define PHANLPA_LPIEEE3 (1<<3)
#define PHANLPA_LPIEEE2 (1<<2)
#define PHANLPA_LPIEEE1 (1<<1)
#define PHANLPA_LPIEEE0 (1)
/* PHANE bits ---------- */
#define PHANE_r15 (1<<15)
#define PHANE_r14 (1<<14)
#define PHANE_r13 (1<<13)
#define PHANE_r12 (1<<12)
#define PHANE_r11 (1<<11)
#define PHANE_r10 (1<<10)
#define PHANE_r9 (1<<9)
#define PHANE_r8 (1<<8)
#define PHANE_r7 (1<<7)
#define PHANE_r6 (1<<6)
#define PHANE_r5 (1<<5)
#define PHANE_PDFLT (1<<4)
#define PHANE_r3 (1<<3)
#define PHANE_r2 (1<<2)
#define PHANE_LPARCD (1<<1)
#define PHANA_LPANABL (1)
/* PHCON2 bits --------- */
#define PHCON2_r15 (1<<15)
#define PHCON2_r14 (1<<14)
#define PHCON2_EDPWRDN (1<<13)
#define PHCON2_r12 (1<<12)
#define PHCON2_EDTHRES (1<<11)
#define PHCON2_r10 (1<<10)
#define PHCON2_r9 (1<<9)
#define PHCON2_r8 (1<<8)
#define PHCON2_r7 (1<<7)
#define PHCON2_r6 (1<<6)
#define PHCON2_r5 (1<<5)
#define PHCON2_r4 (1<<4)
#define PHCON2_r3 (1<<3)
#define PHCON2_FRCLNK (1<<2)
#define PHCON2_EDSTAT (1<<1)
#define PHCON2_r0 (1)
/* PHSTAT2 bits --------- */
#define PHSTAT2_r15 (1<<15)
#define PHSTAT2_r14 (1<<14)
#define PHSTAT2_r13 (1<<13)
#define PHSTAT2_r12 (1<<12)
#define PHSTAT2_r11 (1<<11)
#define PHSTAT2_r10 (1<<10)
#define PHSTAT2_r9 (1<<9)
#define PHSTAT2_r8 (1<<8)
#define PHSTAT2_r7 (1<<7)
#define PHSTAT2_r6 (1<<6)
#define PHSTAT2_r5 (1<<5)
#define PHSTAT2_PLRITY (1<<4)
#define PHSTAT2_r3 (1<<3)
#define PHSTAT2_r2 (1<<2)
#define PHSTAT2_r1 (1<<1)
#define PHSTAT2_r0 (1)
/* PHSTAT3 bits -------- */
#define PHSTAT3_r15 (1<<15)
#define PHSTAT3_r14 (1<<14)
#define PHSTAT3_r13 (1<<13)
#define PHSTAT3_r12 (1<<12)
#define PHSTAT3_r11 (1<<11)
#define PHSTAT3_r10 (1<<10)
#define PHSTAT3_r9 (1<<9)
#define PHSTAT3_r8 (1<<8)
#define PHSTAT3_r7 (1<<7)
#define PHSTAT3_r6 (1<<6)
#define PHSTAT3_r5 (1<<5)
#define PHSTAT3_SPDDPX2 (1<<4)
#define PHSTAT3_SPDDPX1 (1<<3)
#define PHSTAT3_SPDDPX0 (1<<2)
#define PHSTAT3_r1 (1<<1)
#define PHSTAT3_r0 (1)
/* ------------------------------ internal defaults ---------------------------------- */
/* 33.333Mhz clock out frequency */
#define CLOCK_33_CLR (ECON2_COCON3|ECON2_COCON2|ECON2_COCON1) /* clr mask */
#define CLOCK_33_SET (ECON2_COCON0) /* set mask */
/* 25 MHz */
#define CLOCK_25_CLR (ECON2_COCON3|ECON2_COCON2|ECON2_COCON0) /* clr mask */
#define CLOCK_25_SET (ECON2_COCON1) /* set mask */
/* 20 MHz */
#define CLOCK_20_CLR (ECON2_COCON3|ECON2_COCON2) /* clr mask */
#define CLOCK_20_SET (ECON2_COCON1|ECON2_COCON0) /* set mask */
/* 16 MHz */
#define CLOCK_16_CLR (ECON2_COCON3|ECON2_COCON0|ECON2_COCON1) /* clr mask */
#define CLOCK_16_SET (ECON2_COCON2) /* set mask */
/* 4 MHz (after reset) */
#define CLOCK_4_CLR (ECON2_COCON2) /* clr mask */
#define CLOCK_4_SET (ECON2_COCON3|ECON2_COCON0|ECON2_COCON1) /* set mask */
#define CLOCK_DEF_CLR CLOCK_33_CLR
#define CLOCK_DEF_SET CLOCK_33_SET
#endif /* _REGISTERS_H_ */
+602
View File
@@ -0,0 +1,602 @@
; ------------------------------------------------------------------------------
; | Lowlevel Access to memory mapped ENC624J600 in PSP mode |
; | Henryk Richter <henryk.richter@gmx.net> |
; ------------------------------------------------------------------------------
; register definition section
; set/clr register offsets
SET_OFFSET EQU $0100
CLR_OFFSET EQU $0180
; Register definitions
; SPI Bank 0 registers --------
ETXST EQU $7E00
ETXSTL EQU $7E00
ETXSTH EQU $7E01
ETXLEN EQU $7E02
ETXLENL EQU $7E02
ETXLENH EQU $7E03
ERXST EQU $7E04
ERXSTL EQU $7E04
ERXSTH EQU $7E05
ERXTAIL EQU $7E06
ERXTAILL EQU $7E06
ERXTAILH EQU $7E07
ERXHEAD EQU $7E08
ERXHEADL EQU $7E08
ERXHEADH EQU $7E09
EDMAST EQU $7E0A
EDMASTL EQU $7E0A
EDMASTH EQU $7E0B
EDMALEN EQU $7E0C
EDMALENL EQU $7E0C
EDMALENH EQU $7E0D
EDMADST EQU $7E0E
EDMADSTL EQU $7E0E
EDMADSTH EQU $7E0F
EDMACS EQU $7E10
EDMACSL EQU $7E10
EDMACSH EQU $7E11
ETXSTAT EQU $7E12
ETXSTATL EQU $7E12
ETXSTATH EQU $7E13
ETXWIRE EQU $7E14
ETXWIREL EQU $7E14
ETXWIREH EQU $7E15
; SPI all bank registers
EUDAST EQU $7E16
EUDASTL EQU $7E16
EUDASTH EQU $7E17
EUDAND EQU $7E18
EUDANDL EQU $7E18
EUDANDH EQU $7E19
ESTAT EQU $7E1A
ESTATL EQU $7E1A
ESTATH EQU $7E1B
EIR EQU $7E1C
EIRL EQU $7E1C
EIRH EQU $7E1D
ECON1 EQU $7E1E
ECON1L EQU $7E1E
ECON1H EQU $7E1F
; SPI Bank 1 registers -----
EHT1 EQU $7E20
EHT1L EQU $7E20
EHT1H EQU $7E21
EHT2 EQU $7E22
EHT2L EQU $7E22
EHT2H EQU $7E23
EHT3 EQU $7E24
EHT3L EQU $7E24
EHT3H EQU $7E25
EHT4 EQU $7E26
EHT4L EQU $7E26
EHT4H EQU $7E27
EPMM1 EQU $7E28
EPMM1L EQU $7E28
EPMM1H EQU $7E29
EPMM2 EQU $7E2A
EPMM2L EQU $7E2A
EPMM2H EQU $7E2B
EPMM3 EQU $7E2C
EPMM3L EQU $7E2C
EPMM3H EQU $7E2D
EPMM4 EQU $7E2E
EPMM4L EQU $7E2E
EPMM4H EQU $7E2F
EPMCS EQU $7E30
EPMCSL EQU $7E30
EPMCSH EQU $7E31
EPMO EQU $7E32
EPMOL EQU $7E32
EPMOH EQU $7E33
ERXFCON EQU $7E34
ERXFCONL EQU $7E34
ERXFCONH EQU $7E35
; SPI all bank registers from 0x36 to 0x3F
; SPI Bank 2 registers -----
MACON1 EQU $7E40
MACON1L EQU $7E40
MACON1H EQU $7E41
MACON2 EQU $7E42
MACON2L EQU $7E42
MACON2H EQU $7E43
MABBIPG EQU $7E44
MABBIPGL EQU $7E44
MABBIPGH EQU $7E45
MAIPG EQU $7E46
MAIPGL EQU $7E46
MAIPGH EQU $7E47
MACLCON EQU $7E48
MACLCONL EQU $7E48
MACLCONH EQU $7E49
MAMXFL EQU $7E4A
MAMXFLL EQU $7E4A
MAMXFLH EQU $7E4B
MICMD EQU $7E52
MICMDL EQU $7E52
MICMDH EQU $7E53
MIREGADR EQU $7E54
MIREGADRL EQU $7E54
MIREGADRH EQU $7E55
; SPI all bank registers from 0x56 to 0x5F
; SPI Bank 3 registers -----
MAADR3 EQU $7E60
MAADR3L EQU $7E60
MAADR3H EQU $7E61
MAADR2 EQU $7E62
MAADR2L EQU $7E62
MAADR2H EQU $7E63
MAADR1 EQU $7E64
MAADR1L EQU $7E64
MAADR1H EQU $7E65
MIWR EQU $7E66
MIWRL EQU $7E66
MIWRH EQU $7E67
MIRD EQU $7E68
MIRDL EQU $7E68
MIRDH EQU $7E69
MISTAT EQU $7E6A
MISTATL EQU $7E6A
MISTATH EQU $7E6B
EPAUS EQU $7E6C
EPAUSL EQU $7E6C
EPAUSH EQU $7E6D
ECON2 EQU $7E6E
ECON2L EQU $7E6E
ECON2H EQU $7E6F
ERXWM EQU $7E70
ERXWML EQU $7E70
ERXWMH EQU $7E71
EIE EQU $7E72
EIEL EQU $7E72
EIEH EQU $7E73
EIDLED EQU $7E74
EIDLEDL EQU $7E74
EIDLEDH EQU $7E75
; SPI all bank registers from 0x66 to 0x6F
; SPI Non-banked Special Function Registers
EGPDATA EQU $7E80
EGPDATAL EQU $7E80
ERXDATA EQU $7E82
ERXDATAL EQU $7E82
EUDADATA EQU $7E84
EUDADATAL EQU $7E84
EGPRDPT EQU $7E86
EGPRDPTL EQU $7E86
EGPRDPTH EQU $7E87
EGPWRPT EQU $7E88
EGPWRPTL EQU $7E88
EGPWRPTH EQU $7E89
ERXRDPT EQU $7E8A
ERXRDPTL EQU $7E8A
ERXRDPTH EQU $7E8B
ERXWRPT EQU $7E8C
ERXWRPTL EQU $7E8C
ERXWRPTH EQU $7E8D
EUDARDPT EQU $7E8E
EUDARDPTL EQU $7E8E
EUDARDPTH EQU $7E8F
EUDAWRPT EQU $7E90
EUDAWRPTL EQU $7E90
EUDAWRPTH EQU $7E91
;;;;;;;;;;;;;;;;;;;;;;;;;;
; ENC424J600/624J600 register bits
;;;;;;;;;;;;;;;;;;;;;;;;;;
; ESTAT bits ----------
ESTAT_INT EQU (1<<15)
ESTAT_FCIDLE EQU (1<<14)
ESTAT_RXBUSY EQU (1<<13)
ESTAT_CLKRDY EQU (1<<12)
ESTAT_RSTDONE EQU (1<<11)
ESTAT_PHYDPX EQU (1<<10)
ESTAT_PHYRDY EQU (1<<9)
ESTAT_PHYLNK EQU (1<<8)
ESTAT_PKTCNT7 EQU (1<<7)
ESTAT_PKTCNT6 EQU (1<<6)
ESTAT_PKTCNT5 EQU (1<<5)
ESTAT_PKTCNT4 EQU (1<<4)
ESTAT_PKTCNT3 EQU (1<<3)
ESTAT_PKTCNT2 EQU (1<<2)
ESTAT_PKTCNT1 EQU (1<<1)
ESTAT_PKTCNT0 EQU (1)
; EIR bits ------------
EIR_CRYPTEN EQU (1<<15)
EIR_MODEXIF EQU (1<<14)
EIR_HASHIF EQU (1<<13)
EIR_AESIF EQU (1<<12)
EIR_LINKIF EQU (1<<11)
EIR_PRDYIF EQU (1<<10)
EIR_r9 EQU (1<<9)
EIR_r8 EQU (1<<8)
EIR_r7 EQU (1<<7)
EIR_PKTIF EQU (1<<6)
EIR_DMAIF EQU (1<<5)
EIR_r4 EQU (1<<4)
EIR_TXIF EQU (1<<3)
EIR_TXABTIF EQU (1<<2)
EIR_RXABTIF EQU (1<<1)
EIR_PCFULIF EQU (1)
; ECON1 bits ----------
ECON1_MODEXST EQU (1<<15)
ECON1_HASHEN EQU (1<<14)
ECON1_HASHOP EQU (1<<13)
ECON1_HASHLST EQU (1<<12)
ECON1_AESST EQU (1<<11)
ECON1_AESOP1 EQU (1<<10)
ECON1_AESOP0 EQU (1<<9)
ECON1_PKTDEC EQU (1<<8)
ECON1_FCOP1 EQU (1<<7)
ECON1_FCOP0 EQU (1<<6)
ECON1_DMAST EQU (1<<5)
ECON1_DMACPY EQU (1<<4)
ECON1_DMACSSD EQU (1<<3)
ECON1_DMANOCS EQU (1<<2)
ECON1_TXRTS EQU (1<<1)
ECON1_RXEN EQU (1)
; ETXSTAT bits --------
ETXSTAT_r12 EQU (1<<12)
ETXSTAT_r11 EQU (1<<11)
ETXSTAT_LATECOL EQU (1<<10)
ETXSTAT_MAXCOL EQU (1<<9)
ETXSTAT_EXDEFER EQU (1<<8)
ETXSTAT_DEFER EQU (1<<7)
ETXSTAT_r6 EQU (1<<6)
ETXSTAT_r5 EQU (1<<5)
ETXSTAT_CRCBAD EQU (1<<4)
ETXSTAT_COLCNT3 EQU (1<<3)
ETXSTAT_COLCNT2 EQU (1<<2)
ETXSTAT_COLCNT1 EQU (1<<1)
ETXSTAT_COLCNT0 EQU (1)
; ERXFCON bits --------
ERXFCON_HTEN EQU (1<<15)
ERXFCON_MPEN EQU (1<<14)
ERXFCON_NOTPM EQU (1<<12)
ERXFCON_PMEN3 EQU (1<<11)
ERXFCON_PMEN2 EQU (1<<10)
ERXFCON_PMEN1 EQU (1<<9)
ERXFCON_PMEN0 EQU (1<<8)
ERXFCON_CRCEEN EQU (1<<7)
ERXFCON_CRCEN EQU (1<<6)
ERXFCON_RUNTEEN EQU (1<<5)
ERXFCON_RUNTEN EQU (1<<4)
ERXFCON_UCEN EQU (1<<3)
ERXFCON_NOTMEEN EQU (1<<2)
ERXFCON_MCEN EQU (1<<1)
ERXFCON_BCEN EQU (1)
; MACON1 bits ---------
MACON1_r15 EQU (1<<15)
MACON1_r14 EQU (1<<14)
MACON1_r11 EQU (1<<11)
MACON1_r10 EQU (1<<10)
MACON1_r9 EQU (1<<9)
MACON1_r8 EQU (1<<8)
MACON1_LOOPBK EQU (1<<4)
MACON1_r3 EQU (1<<3)
MACON1_RXPAUS EQU (1<<2)
MACON1_PASSALL EQU (1<<1)
MACON1_r0 EQU (1)
; MACON2 bits ---------
MACON2_DEFER EQU (1<<14)
MACON2_BPEN EQU (1<<13)
MACON2_NOBKOFF EQU (1<<12)
MACON2_r9 EQU (1<<9)
MACON2_r8 EQU (1<<8)
MACON2_PADCFG2 EQU (1<<7)
MACON2_PADCFG1 EQU (1<<6)
MACON2_PADCFG0 EQU (1<<5)
MACON2_TXCRCEN EQU (1<<4)
MACON2_PHDREN EQU (1<<3)
MACON2_HFRMEN EQU (1<<2)
MACON2_r1 EQU (1<<1)
MACON2_FULDPX EQU (1)
; MABBIPG bits --------
MABBIPG_BBIPG6 EQU (1<<6)
MABBIPG_BBIPG5 EQU (1<<5)
MABBIPG_BBIPG4 EQU (1<<4)
MABBIPG_BBIPG3 EQU (1<<3)
MABBIPG_BBIPG2 EQU (1<<2)
MABBIPG_BBIPG1 EQU (1<<1)
MABBIPG_BBIPG0 EQU (1)
; MAIPG bits ----------
MAIPG_r14 EQU (1<<14)
MAIPG_r13 EQU (1<<13)
MAIPG_r12 EQU (1<<12)
MAIPG_r11 EQU (1<<11)
MAIPG_r10 EQU (1<<10)
MAIPG_r9 EQU (1<<9)
MAIPG_r8 EQU (1<<8)
MAIPG_IPG6 EQU (1<<6)
MAIPG_IPG5 EQU (1<<5)
MAIPG_IPG4 EQU (1<<4)
MAIPG_IPG3 EQU (1<<3)
MAIPG_IPG2 EQU (1<<2)
MAIPG_IPG1 EQU (1<<1)
MAIPG_IPG0 EQU (1)
; MACLCON bits --------
MACLCON_r13 EQU (1<<13)
MACLCON_r12 EQU (1<<12)
MACLCON_r11 EQU (1<<11)
MACLCON_r10 EQU (1<<10)
MACLCON_r9 EQU (1<<9)
MACLCON_r8 EQU (1<<8)
MACLCON_MAXRET3 EQU (1<<3)
MACLCON_MAXRET2 EQU (1<<2)
MACLCON_MAXRET1 EQU (1<<1)
MACLCON_MAXRET0 EQU (1)
; MICMD bits ----------
MICMD_MIISCAN EQU (1<<1)
MICMD_MIIRD EQU (1)
; MIREGADR bits -------
MIREGADR_r12 EQU (1<<12)
MIREGADR_r11 EQU (1<<11)
MIREGADR_r10 EQU (1<<10)
MIREGADR_r9 EQU (1<<9)
MIREGADR_r8 EQU (1<<8)
MIREGADR_PHREG4 EQU (1<<4)
MIREGADR_PHREG3 EQU (1<<3)
MIREGADR_PHREG2 EQU (1<<2)
MIREGADR_PHREG1 EQU (1<<1)
MIREGADR_PHREG0 EQU (1)
; MISTAT bits ---------
MISTAT_r3 EQU (1<<3)
MISTAT_NVALID EQU (1<<2)
MISTAT_SCAN EQU (1<<1)
MISTAT_BUSY EQU (1)
; ECON2 bits ----------
ECON2_ETHEN EQU (1<<15)
ECON2_STRCH EQU (1<<14)
ECON2_TXMAC EQU (1<<13)
ECON2_SHA1MD5 EQU (1<<12)
ECON2_COCON3 EQU (1<<11)
ECON2_COCON2 EQU (1<<10)
ECON2_COCON1 EQU (1<<9)
ECON2_COCON0 EQU (1<<8)
ECON2_AUTOFC EQU (1<<7)
ECON2_TXRST EQU (1<<6)
ECON2_RXRST EQU (1<<5)
ECON2_ETHRST EQU (1<<4)
ECON2_MODLEN1 EQU (1<<3)
ECON2_MODLEN0 EQU (1<<2)
ECON2_AESLEN1 EQU (1<<1)
ECON2_AESLEN0 EQU (1)
; ERXWM bits ----------
ERXWM_RXFWM7 EQU (1<<15)
ERXWM_RXFWM6 EQU (1<<14)
ERXWM_RXFWM5 EQU (1<<13)
ERXWM_RXFWM4 EQU (1<<12)
ERXWM_RXFWM3 EQU (1<<11)
ERXWM_RXFWM2 EQU (1<<10)
ERXWM_RXFWM1 EQU (1<<9)
ERXWM_RXFWM0 EQU (1<<8)
ERXWM_RXEWM7 EQU (1<<7)
ERXWM_RXEWM6 EQU (1<<6)
ERXWM_RXEWM5 EQU (1<<5)
ERXWM_RXEWM4 EQU (1<<4)
ERXWM_RXEWM3 EQU (1<<3)
ERXWM_RXEWM2 EQU (1<<2)
ERXWM_RXEWM1 EQU (1<<1)
ERXWM_RXEWM0 EQU (1)
; EIE bits ------------
EIE_INTIE EQU (1<<15)
EIE_MODEXIE EQU (1<<14)
EIE_HASHIE EQU (1<<13)
EIE_AESIE EQU (1<<12)
EIE_LINKIE EQU (1<<11)
EIE_PRDYIE EQU (1<<10)
EIE_r9 EQU (1<<9)
EIE_r8 EQU (1<<8)
EIE_r7 EQU (1<<7)
EIE_PKTIE EQU (1<<6)
EIE_DMAIE EQU (1<<5)
EIE_r4 EQU (1<<4)
EIE_TXIE EQU (1<<3)
EIE_TXABTIE EQU (1<<2)
EIE_RXABTIE EQU (1<<1)
EIE_PCFULIE EQU (1)
; EIDLED bits ---------
EIDLED_LACFG3 EQU (1<<15)
EIDLED_LACFG2 EQU (1<<14)
EIDLED_LACFG1 EQU (1<<13)
EIDLED_LACFG0 EQU (1<<12)
EIDLED_LBCFG3 EQU (1<<11)
EIDLED_LBCFG2 EQU (1<<10)
EIDLED_LBCFG1 EQU (1<<9)
EIDLED_LBCFG0 EQU (1<<8)
EIDLED_DEVID2 EQU (1<<7)
EIDLED_DEVID1 EQU (1<<6)
EIDLED_DEVID0 EQU (1<<5)
EIDLED_REVID4 EQU (1<<4)
EIDLED_REVID3 EQU (1<<3)
EIDLED_REVID2 EQU (1<<2)
EIDLED_REVID1 EQU (1<<1)
EIDLED_REVID0 EQU (1)
; PHCON1 bits ---------
PHCON1_PRST EQU (1<<15)
PHCON1_PLOOPBK EQU (1<<14)
PHCON1_SPD100 EQU (1<<13)
PHCON1_ANEN EQU (1<<12)
PHCON1_PSLEEP EQU (1<<11)
PHCON1_r10 EQU (1<<10)
PHCON1_RENEG EQU (1<<9)
PHCON1_PFULDPX EQU (1<<8)
PHCON1_r7 EQU (1<<7)
PHCON1_r6 EQU (1<<6)
PHCON1_r5 EQU (1<<5)
PHCON1_r4 EQU (1<<4)
PHCON1_r3 EQU (1<<3)
PHCON1_r2 EQU (1<<2)
PHCON1_r1 EQU (1<<1)
PHCON1_r0 EQU (1)
; PHSTAT1 bits --------
PHSTAT1_r15 EQU (1<<15)
PHSTAT1_FULL100 EQU (1<<14)
PHSTAT1_HALF100 EQU (1<<13)
PHSTAT1_FULL10 EQU (1<<12)
PHSTAT1_HALF10 EQU (1<<11)
PHSTAT1_r10 EQU (1<<10)
PHSTAT1_r9 EQU (1<<9)
PHSTAT1_r8 EQU (1<<8)
PHSTAT1_r7 EQU (1<<7)
PHSTAT1_r6 EQU (1<<6)
PHSTAT1_ANDONE EQU (1<<5)
PHSTAT1_LRFAULT EQU (1<<4)
PHSTAT1_ANABLE EQU (1<<3)
PHSTAT1_LLSTAT EQU (1<<2)
PHSTAT1_r1 EQU (1<<1)
PHSTAT1_EXTREGS EQU (1)
; PHANA bits ----------
PHANA_ADNP EQU (1<<15)
PHANA_r14 EQU (1<<14)
PHANA_ADFAULT EQU (1<<13)
PHANA_r12 EQU (1<<12)
PHANA_ADPAUS1 EQU (1<<11)
PHANA_ADPAUS0 EQU (1<<10)
PHANA_r9 EQU (1<<9)
PHANA_AD100FD EQU (1<<8)
PHANA_AD100 EQU (1<<7)
PHANA_AD10FD EQU (1<<6)
PHANA_AD10 EQU (1<<5)
PHANA_ADIEEE4 EQU (1<<4)
PHANA_ADIEEE3 EQU (1<<3)
PHANA_ADIEEE2 EQU (1<<2)
PHANA_ADIEEE1 EQU (1<<1)
PHANA_ADIEEE0 EQU (1)
; PHANLPA bits --------
PHANLPA_LPNP EQU (1<<15)
PHANLPA_LPACK EQU (1<<14)
PHANLPA_LPFAULT EQU (1<<13)
PHANLPA_r12 EQU (1<<12)
PHANLPA_LPPAUS1 EQU (1<<11)
PHANLPA_LPPAUS0 EQU (1<<10)
PHANLPA_LP100T4 EQU (1<<9)
PHANLPA_LP100FD EQU (1<<8)
PHANLPA_LP100 EQU (1<<7)
PHANLPA_LP10FD EQU (1<<6)
PHANLPA_LP10 EQU (1<<5)
PHANLPA_LPIEEE4 EQU (1<<4)
PHANLPA_LPIEEE3 EQU (1<<3)
PHANLPA_LPIEEE2 EQU (1<<2)
PHANLPA_LPIEEE1 EQU (1<<1)
PHANLPA_LPIEEE0 EQU (1)
; PHANE bits ----------
PHANE_r15 EQU (1<<15)
PHANE_r14 EQU (1<<14)
PHANE_r13 EQU (1<<13)
PHANE_r12 EQU (1<<12)
PHANE_r11 EQU (1<<11)
PHANE_r10 EQU (1<<10)
PHANE_r9 EQU (1<<9)
PHANE_r8 EQU (1<<8)
PHANE_r7 EQU (1<<7)
PHANE_r6 EQU (1<<6)
PHANE_r5 EQU (1<<5)
PHANE_PDFLT EQU (1<<4)
PHANE_r3 EQU (1<<3)
PHANE_r2 EQU (1<<2)
PHANE_LPARCD EQU (1<<1)
PHANA_LPANABL EQU (1)
; PHCON2 bits ---------
PHCON2_r15 EQU (1<<15)
PHCON2_r14 EQU (1<<14)
PHCON2_EDPWRDN EQU (1<<13)
PHCON2_r12 EQU (1<<12)
PHCON2_EDTHRES EQU (1<<11)
PHCON2_r10 EQU (1<<10)
PHCON2_r9 EQU (1<<9)
PHCON2_r8 EQU (1<<8)
PHCON2_r7 EQU (1<<7)
PHCON2_r6 EQU (1<<6)
PHCON2_r5 EQU (1<<5)
PHCON2_r4 EQU (1<<4)
PHCON2_r3 EQU (1<<3)
PHCON2_FRCLNK EQU (1<<2)
PHCON2_EDSTAT EQU (1<<1)
PHCON2_r0 EQU (1)
; PHSTAT2 bits ---------
PHSTAT2_r15 EQU (1<<15)
PHSTAT2_r14 EQU (1<<14)
PHSTAT2_r13 EQU (1<<13)
PHSTAT2_r12 EQU (1<<12)
PHSTAT2_r11 EQU (1<<11)
PHSTAT2_r10 EQU (1<<10)
PHSTAT2_r9 EQU (1<<9)
PHSTAT2_r8 EQU (1<<8)
PHSTAT2_r7 EQU (1<<7)
PHSTAT2_r6 EQU (1<<6)
PHSTAT2_r5 EQU (1<<5)
PHSTAT2_PLRITY EQU (1<<4)
PHSTAT2_r3 EQU (1<<3)
PHSTAT2_r2 EQU (1<<2)
PHSTAT2_r1 EQU (1<<1)
PHSTAT2_r0 EQU (1)
; PHSTAT3 bits --------
PHSTAT3_r15 EQU (1<<15)
PHSTAT3_r14 EQU (1<<14)
PHSTAT3_r13 EQU (1<<13)
PHSTAT3_r12 EQU (1<<12)
PHSTAT3_r11 EQU (1<<11)
PHSTAT3_r10 EQU (1<<10)
PHSTAT3_r9 EQU (1<<9)
PHSTAT3_r8 EQU (1<<8)
PHSTAT3_r7 EQU (1<<7)
PHSTAT3_r6 EQU (1<<6)
PHSTAT3_r5 EQU (1<<5)
PHSTAT3_SPDDPX2 EQU (1<<4)
PHSTAT3_SPDDPX1 EQU (1<<3)
PHSTAT3_SPDDPX0 EQU (1<<2)
PHSTAT3_r1 EQU (1<<1)
PHSTAT3_r0 EQU (1)
;------------------------------ internal defaults ----------------------------------
;33.333Mhz clock out frequency
CLOCK_33_CLR EQU (ECON2_COCON3|ECON2_COCON2|ECON2_COCON1) ;clr mask
CLOCK_33_SET EQU (ECON2_COCON0) ;set mask
;25 MHz
CLOCK_25_CLR EQU (ECON2_COCON3|ECON2_COCON2|ECON2_COCON0) ;clr mask
CLOCK_25_SET EQU (ECON2_COCON1) ;set mask
;20 MHz
CLOCK_20_CLR EQU (ECON2_COCON3|ECON2_COCON2) ;clr mask
CLOCK_20_SET EQU (ECON2_COCON1|ECON2_COCON0) ;set mask
;16 MHz
CLOCK_16_CLR EQU (ECON2_COCON3|ECON2_COCON0|ECON2_COCON1) ;clr mask
CLOCK_16_SET EQU (ECON2_COCON2) ;set mask
;4 MHz (after reset)
CLOCK_4_CLR EQU (ECON2_COCON2) ;clr mask
CLOCK_4_SET EQU (ECON2_COCON3|ECON2_COCON0|ECON2_COCON1) ;set mask
CLOCK_DEF_CLR EQU CLOCK_33_CLR
CLOCK_DEF_SET EQU CLOCK_33_SET
+101
View File
@@ -0,0 +1,101 @@
/*
hw.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Hardware dependent functions
*/
#ifndef _INC_HW_H
#define _INC_HW_H
#include "compiler.h"
#ifndef DEVBASETYPE
struct devbase;
#define DEVBASETYPE struct devbase
#endif
#ifndef DEVBASEP
#define DEVBASEP DEVBASETYPE *db
#endif
/* find number of boards (stateless) */
ASM SAVEDS LONG hw_Find_Boards( ASMR(a0) DEVBASEP ASMREG(a0) );
/* allocate / release a board
note: alloc may be called more than once, keep track of number of openings
*/
ASM SAVEDS LONG hw_AllocBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) );
ASM SAVEDS LONG hw_ReleaseBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) );
ASM SAVEDS LONG hw_GetMacAddress(ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *mac ASMREG(a1) );
/* initialize global management stuff for hardware (called once on server startup) */
/*
Call order
- hw_Setup
hw_ConfigInit
hw_ConfigUpdate
hw_Attach
main loop: send/recv
hw_Detach
- hw_Shutdown
*/
ASM SAVEDS LONG hw_Setup( ASMR(a0) DEVBASEP ASMREG(a0) );
ASM SAVEDS void hw_Shutdown( ASMR(a0) DEVBASEP ASMREG(a0) );
ASM SAVEDS LONG hw_Attach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0));
ASM SAVEDS void hw_Detach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0));
ASM SAVEDS void hw_ConfigInit( ASMR(a0) DEVBASEP ASMREG(a0) );
ASM SAVEDS void hw_ConfigUpdate( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a1) void * ASMREG(a1) );
ASM SAVEDS LONG hw_send_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1),
ASMR(d1) ULONG framesize ASMREG(d1) );
ASM SAVEDS LONG hw_recv_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1) );
ASM SAVEDS LONG hw_recv_pending( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) );
ASM SAVEDS LONG hw_check_link_change( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) );
ASM SAVEDS LONG hw_change_multicast( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a0) ULONG unit ASMREG(d0),
ASMR(a1) struct List * ASMREG(a1) );
/* kept this global for now: Z-II/Z-III boards share the same interrupt */
ASM SAVEDS ULONG hw_recv_sigmask( ASMR(a0) DEVBASEP ASMREG(a0) );
/* Configuration template: straight replica of Plipbox general options */
struct CommonConfig {
ULONG nospecialstats;
LONG *priority;
ULONG *bps;
ULONG *mtu;
};
#define COMMON_TEMPLATE "NOSPECIALSTATS/S,PRIORITY=PRI/K/N,BPS/K/N,MTU/K/N,"
/* additional definitions */
#include "hwprivate.h"
#endif /* _INC_HW_H */
+52
View File
@@ -0,0 +1,52 @@
/*
macros.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
useful macros
*/
#ifndef _INC_MACROS_H
#define _INC_MACROS_H
#define GetHead(l) (void *)(((struct List *)l)->lh_Head->ln_Succ \
? ((struct List *)l)->lh_Head \
: (struct Node *)0)
#define GetTail(l) (void *)(((struct List *)l)->lh_TailPred->ln_Pred \
? ((struct List *)l)->lh_TailPred \
: (struct Node *)0)
#define GetSucc(n) (void *)(((struct Node *)n)->ln_Succ->ln_Succ \
? ((struct Node *)n)->ln_Succ \
: (struct Node *)0)
#define GetPred(n) (void *)(((struct Node *)n)->ln_Pred->ln_Pred \
? ((struct Node *)n)->ln_Pred \
: (struct Node *)0)
#define REMOVE(n) ((void)(\
((struct Node *)n)->ln_Pred->ln_Succ = ((struct Node *)n)->ln_Succ,\
((struct Node *)n)->ln_Succ->ln_Pred = ((struct Node *)n)->ln_Pred ))
#define ADDHEAD(l,n) ((void)(\
((struct Node *)n)->ln_Succ = ((struct List *)l)->lh_Head, \
((struct Node *)n)->ln_Pred = (struct Node *)&((struct List *)l)->lh_Head, \
((struct List *)l)->lh_Head->ln_Pred = ((struct Node *)n), \
((struct List *)l)->lh_Head = ((struct Node *)n)))
#define ADDTAIL(l,n) ((void)(\
((struct Node *)n)->ln_Succ = (struct Node *)&((struct List *)l)->lh_Tail, \
((struct Node *)n)->ln_Pred = ((struct List *)l)->lh_TailPred, \
((struct List *)l)->lh_TailPred->ln_Succ = ((struct Node *)n), \
((struct List *)l)->lh_TailPred = ((struct Node *)n) ))
#define NEWLIST(l) (((struct List *)l)->lh_TailPred = (struct Node *)(l), \
((struct List *)l)->lh_Tail = 0, \
((struct List *)l)->lh_Head = (struct Node *)&(((struct List *)l)->lh_Tail))
#define min(_a_,_b_) ( (_a_) > (_b_) ) ? _b_ : _a_
#define max(_a_,_b_) ( (_a_) < (_b_) ) ? _b_ : _a_
#define BOUNDS(_val_,_min_,_max_) ( (_val_) < (_min_) ) ? _min_ : ( (_val_) > (_max_) ) ? _max_ : _val_
#endif /* _INC_MACROS_H */
+8
View File
@@ -0,0 +1,8 @@
TODO-List:
Nospecialstats in server.c ?
Interrupt on V500 Expansion port ?
+247
View File
@@ -0,0 +1,247 @@
SDNet - A SANA-II driver for ENC28J60 10BaseT ethernet modules
Written in 2016-2018 by Henryk Richter <henryk.richter@gmx.net>
This driver is supposed to support ENC28J60 modules hooked up to the MicroSD
slot of Vampire 500/600 cards or the expansion port of Vampire 500V2+ cards.
Installation:
-------------
Installation of the driver is straightforward.
Just copy the sdnet.device and v2expeth.device to devs:networks and enable
the interface in your favorite TCP/IP stack as usual. So far, successful
tests have been done with the free AmiTCP/IP 3.0b2, AmiTCP4.0-4.2, Genesis,
MiamiDX and RoadShow.
devs:networks/sdnet.device - SD card slot of V600/V500
devs:networks/v2expeth.device - expansion port of V500V2+
From version 0.8, the driver will require vampire.resource which is included
in the ROM of Vampire Cards since the Gold2.5 release.
Also, make sure that the Vampire SD Card driver (sagasd.device) is not present
in the system or at least inactive when using sdnet.device. Both pieces of
software bang the same hardware registers. v2expeth.device may be used safely
in conjunction with the SD card driver.
Vampire500 Expansion port Hardware
-----------------------------------
Please see http://wiki.apollo-accelerators.com/doku.php/expansionport for
guidance.
MicroSD Slot Hardware:
----------------------
In terms of required hardware, you need the following:
- ready-to-use ENC28J60 module (a 3.3V part, DON'T USE an _unmodified_ 5V
module!)
- MicroSD breakout, e.g.
- a TF to SD extension cable where you rip off the full size SD slot and
solder on short extension cables towards the ENC28J60 module
- MicroSD breakout board (that comes directly with pin headers)
!!ATTENTION!!
Be extremely careful with your SD breakout board. Many conveniently looking
breakouts are made of thick and/or sharp edged PCB material that will likely
damage your Vampire SD slot. Such breakouts could be made working - but only
after patient sanding. Some people in the beta test team have destroyed
their SD slot already. If you don't have the required mechanical skills and
patience, I'd advise against DIY building.
!!ATTENTION!!
You need to connect the SD breakout pins as follows (microSD card sketch
looking at the connectors) to the ENC28J60 module:
----------------------- ENC28J60
| 8 ----
| 7 ---- SO -------------------- SO
| 6 ----- GND -------------------- GND
| 5 ---- SCK -------------------- SCK
| 4 ----- +3.3V ------ +3.3V ----- VCC (see below)
| 3 ---- SI -------------------- SI
| 2 ---- /CS -------------------- /CS
| 1 ---- /INT -- __________ -- /INT
------ ------------- \_| 3.3 kOhm |_/
\___| `----------´
As there is a myriad of ENC28J60 modules available, the naming scheme on
a particular board might differ. Please be aware the some of the boards
have a "CLK" pin as well as an "SCK" pin. Use the latter.
My test setup uses a separate 5V to 3.3V DC-DC converter. I got confirmation
from Igor Majstorovic (majsta) that the 3.3V line on his V500 Vampire designs
should be able to handle the max. 200 mA required by the module. To be on the
safe side, I recommend to use a conveniently placed 5V to 3.3V converter
(hint: the Floppy power connector is easily accessible).
Speaking of power: the reason why I didn't recommend 5V ENC28J60 boards
is that at least some of them have pullups from /CS and /RST to +5V. Your 3.3V
Vampire SD slot will quite likely not be happy with that.
Please put a 3.3 kOhm resistor into the connection from /INT of the ENC28J60 to
pin 1 of the MicroSD breakout.
Testing a new module:
---------------------
I've included two small test programs: sdnettest and v2expethtest. Both will
require the TCP/IP stack to be off(!) The program will perform a series of tests
to check for module presence and communication results at various clock rates. Don't
be alarmed when the slowest (biggest clock divider) mode is commented with a "fail".
This is intentional. What matters is that SPI clock dividers 1, 2 and 3 are working
properly. In case that sdnettest shows a failure for divider 1, see below how
to set a higher clock divider.
Use sdnettest for the SD slot and v2expethtest for the V500 expansion connector.
Software notes:
---------------
- The SD slot doesn't feature an interrupt. I had to implement polling
for incoming data. The current default interval is 10ms. (configurable via
echo "TIMER=20000" >ENVARC:sana2/sdnet.config, value in microseconds,
don't forget Makedir ENVARC:Sana2 if it didn't exist yet, you may also
consider "setenv")
- SPI speed is set to a safe level supporting higher core speeds than
x11 (see released x12 cores). Therefore this default of "SPISPEED=2"
is not providing maximum performance on x10 or x11 cores. For max
speed you may set echo "SPISPEED=1" >ENVARC:sana2/sdnet.config or
echo "SPISPEED=1" >ENVARC:sana2/v2expeth.config, respectively. I'd
suggest to run the test tool to determine the maximum reliable
speed setting.
- The required vampire.resource is unavailable with older versions of the
Vampire Kick Rom (anything before Gold2.5).
To load vampire.resource manually, copy the file to SYS:devs/ and
add the following near the top of your startup-sequence
C:VERSION >nil: vampire.resource 42
IF WARN
LoadModule >nil: DEVS:vampire.resource
ENDIF
In case of OS3.9, you can add the "noreboot" keyword to the loadmodule
line if you call the sniplet above before setpatch.
- If you want to use multiple options in the SANA-II config, concatenate them
in the "echo" command (alternatively "setenv") or use a text editor.
Options
-------
Below you find the options accepted by the drivers. Please note that no special
configuration is necessary for normal operation. What you find documented below
is available for tuning purposes.
The options template for the config files
ENVARC:sana2/sdnet.config
ENVARC:sana2/v2expeth.config
is as follows:
NOSPECIALSTATS/S,PRIORITY=PRI/K/N,BPS/K/N,MTU/K/N,FULLDUPLEX/S,SPISPEED/K/N,
TIMER/K/N,MULTICAST/S
SPISPEED - SPI clock divider (1-10, default 2), controls maximum
clock of SPI bus. Actual I/O clock is Core_Clock/(3+SPISPEED).
FULLDUPLEX - change Ethernet module into full duplex mode,
caution: requires manual configuration of duplex mode
on the switch, too.
PRIORITY - server task priority (default 1)
BPS - bit per seconds for statistics purposes
MTU - maximum transmission unit (default 1500)
NOSPECIALSTATS - obsolete, kept for config file backwards compatibility with
old driver
MULTICAST - obsolete, kept for config file backwards compatibility with
old driver
example (useful for DSL via PPPoE on x11 core with AmiTCP):
echo "MTU=1492 SPISPEED=1 PRIORITY=6" >ENVARC:sana2/sdnet.config
Changelog:
----------
1.90 - first Beta of completely rewritten driver
- support for concurrent IP stacks
- faster handling of statistics
- multiple board support where applicable (not for sdnet)
- workaround for Shapeshifter network plugin bug
- GCC or VBCC as compiler
- faster lowlevel read/write
1.03 - small fixes in sdnettest/v2expethtest, graceful exit when not run
on Vampire Gold 2.5 or later core
- fixed a minor bug in signal handling
1.02 - added build variant for V500V2+ expansion port, same config options
but distinct config file (ENV:SANA2/v2expeth.config), see
http://wiki.apollo-accelerators.com/doku.php/expansionport for pinout
- some more work on robustness (TX robustness and more error handling
this time)
- reworked test utilities to send valid BOOTP/DHCP requests while
doing TX/RX testing, increases chance of received frames within
testing interval
1.01 - some more fine tuning
- adjustments for Gold 2.7 x12, i.e. SPISPEED=2 by default
(see above for reverting to clock divider 1)
1.0 - some modest performance improvements
- reworked lowlevel code for higher robustness
- LED blinking modified: green LED will blink on RX,
orange LED blinks on TX
0.81 - sdnettest added
0.8 - proper hardware allocation by means of vampire.resource
- check for ENC28J60 hardware presence
0.7 - cleanups
0.6 - SPI speed setting support
- removal of spi.c,spi.h
0.5 - auto-generated MAC address, based on the Vampire FPGA serial
- busy waiting moved entirely to lowlevel asm source
- optimized compilation now produces working binaries
- more cleanups
0.4 - cleanups, config variables added
0.3 - first version shared with the Apollo team
Copyrights:
-----------
The SDNet source is heavily based on plipbox, which is in turn based on the magPLIP
driver found on aminet.net:
http://lallafa.de/blog/amiga-projects/plipbox/
http://aminet.net/package/comm/net/magPLIP38.1
magPLIP - SANA II compliant PLIP device driver for AmiTCP/IP, Envoy,
AS225 and other networking packages
(C) Copyright 1993-1994 Oliver Wagner, Michael Balzer
(C) Copyright 1995 Jan Kratochvil & Martin Mares
(C) Copyright 1995-1996 Marius Groeger
All Rights Reserved.
Changes have been made by Stéphane Zermatten to the driver to connect
a LinuxBox to an Amiga via a LapLing cable, based on the release 37.8.
+733
View File
@@ -0,0 +1,733 @@
/*
Microchip ENC28J60 Ethernet Interface Driver
Author: Henryk Richter
Copyright: GPL V2
Notes: when I ran into timing issues in early development, I moved over to quickly
testable ASM code for SPI communication. I didn't bother porting that stuff
back into C code. So this file now references enc28j60l.asm for lowlevel direct
hardware access.
As a consequence, the only functions needed from spi.c are the spi_init() and
spi_disable_eth().
TODO: - support for power down when unused, i.e. offline
- replace busy waiting loop after reset
Based on work by
Christian Vogelgsaang
Guido Socher
Pascal Stang (see enc28j60.c in AVRlib library for the original file)
*/
#undef _DO_DUMP
/* used for Delay() */
#include <dos/dos.h>
#include <proto/dos.h>
#ifdef _DO_DUMP
#include <stdio.h>
#endif
#include "compiler.h"
#include "enc28j60.h"
#include "enc28j60l.h" /* import lowlevel asm functions */
/* #include "spi.h" */ /* no direct use anymore, just for init purposes) */
#include "device.h"
/*
The RXSTART_INIT must be zero. See Rev. B4 Silicon Errata point 5.
Buffer boundaries applied to internal 8K ram
the entire available packet buffer space is allocated
rx packet layout
6 byte heaader
1518
sum: 1524
*/
#define RXSTART_INIT 0x0000 /* start of RX buffer, room for 4 packets */
#define RXSTOP_INIT 0x19FF /* end of RX buffer */
#define TXSTART_INIT 0x1A00 /* start of TX buffer, room for 1 packet */
#define TXSTOP_INIT 0x1FFF /* end of TX buffer */
/* max frame length which the conroller will accept: */
/* (note: maximum ethernet frame length would be 1518 when VLAN tagging is unused) */
#define MAX_FRAMELEN 1518
static u16 gNextPacketPtr;
static u08 is_full_duplex;
static u08 rev;
struct enc28j60_initvars {
u16 have_recv;
u08 last_mac[6];
u08 macon1;
u08 macon2;
u08 macon3;
u08 macon4;
};
static struct enc28j60_initvars initvars;
static void enc28j60_recv_reset( struct enc28j60_initvars *iv, u08 restart );
static int enc28j60_check_config( struct enc28j60_initvars *iv );
static uint8_t readOp (uint8_t op, uint8_t address)
{
return enc28j60l_ReadOp( op, address );
}
static void writeOp (uint8_t op, uint8_t address, uint8_t data)
{
enc28j60l_WriteOp( op, address,data );
}
static void readBuf(uint16_t len, uint8_t* data)
{
enc28j60l_ReadBuffer( data, (unsigned int)len );
}
void SetBank (uint8_t address)
{
enc28j60l_SetBank(address);
}
static uint8_t readRegByte (uint8_t address)
{
return enc28j60l_ReadRegByte( address );
}
#if 0
static uint16_t readReg(uint8_t address) {
return readRegByte(address) + (readRegByte(address+1) << 8);
}
#endif
static void writeRegByte (uint8_t address, uint8_t data)
{
enc28j60l_WriteRegByte( address, data );
}
static void writeReg(uint8_t address, uint16_t data)
{
enc28j60l_WriteReg( address, data );
}
/*
the PHY registers require special care
*/
static uint16_t readPhyByte (uint8_t address) {
writeRegByte(MIREGADR, address);
writeRegByte(MICMD, MICMD_MIIRD);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
writeRegByte(MICMD, 0x00);
return readRegByte(MIRD+1);
}
static void writePhy (uint8_t address, uint16_t data) {
writeRegByte(MIREGADR, address);
writeReg(MIWR, data);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
}
/* just a pass-through to the lowlevel routines */
void enc28j60_SetSPISpeed( unsigned long clock_divider )
{
enc28j60l_SetSPISpeed( clock_divider );
}
/* ---------- init ---------- */
/* Functions to enable/disable broadcast filter bits */
/* With the bit set, broadcast packets are filtered. */
void enc28j60_broadcast_multicast_filter( u08 flags )
{
u08 val = ERXFCON_UCEN|ERXFCON_CRCEN; /* default: crc check and unicast */
if( flags & PIO_INIT_PROMISC )
{
val = 0;
}
else
{
if( flags & PIO_INIT_BROAD_CAST )
val |= ERXFCON_BCEN;
if( flags & PIO_INIT_MULTI_CAST )
val |= ERXFCON_MCEN;
}
writeRegByte(ERXFCON, val );
}
static void enc28j60_recv_reset( struct enc28j60_initvars *iv, u08 restart )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ESTAT, ESTAT_TXABRT);
gNextPacketPtr = RXSTART_INIT;
writeReg(ERXST, RXSTART_INIT);
writeReg(ERXRDPT, RXSTOP_INIT );
writeReg(ERXND, RXSTOP_INIT);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF|EIR_RXERIF );
writeReg(MAMXFL, MAX_FRAMELEN);
writeRegByte(MACON4, iv->macon4 ); /* DEFER bit */
writeRegByte(MACON3, iv->macon3 );
writeRegByte(MACON2, iv->macon2 );
writeRegByte(MACON1, iv->macon1 );
enc28j60_setMAC( iv->last_mac );
writePhy(PHIE, PHIE_PGEIE|PHIE_LNKIE );
writePhy(PHLCON, 0x3c12 ); /* 3(fixed) C(LinkStat+RX Act) 1(TX Act) 2(Stretch enable) */
/* prepare flow control */
writeReg(EPAUS, 20 * 100); /* 100ms */
if( restart )
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
iv->have_recv = 0;
}
static int enc28j60_check_config( struct enc28j60_initvars *iv )
{
int ret = 1; /* def: be optimistic */
/* check RX registers */
if( !(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_RXEN ) )
ret = 0;
if( (readRegByte(MACON1)&iv->macon1) != iv->macon1 )
ret = 0;
if( (readRegByte(MACON2)&iv->macon2) != iv->macon2 )
ret = 0;
if( (readRegByte(MACON3)&iv->macon3) != iv->macon3 )
ret = 0;
/* check TX problems */
if( readRegByte(ESTAT) & ESTAT_TXABRT )
ret = 0;
if( readRegByte(EIR) & EIR_TXERIF )
ret = 0;
/* Verify MAC */
if( readRegByte(MAADR5) != iv->last_mac[0] )
ret = 0;
if( readRegByte(MAADR4) != iv->last_mac[1] )
ret = 0;
if( readRegByte(MAADR3) != iv->last_mac[2] )
ret = 0;
if( readRegByte(MAADR2) != iv->last_mac[3] )
ret = 0;
if( readRegByte(MAADR1) != iv->last_mac[4] )
ret = 0;
if( readRegByte(MAADR0) != iv->last_mac[5] )
ret = 0;
return ret;
}
u08 enc28j60_init(const u08 macaddr[6], u08 flags)
{
unsigned int count;
u08 mac3val;
struct enc28j60_initvars *iv = &initvars;
enc28j60l_Init(); /* spi_init(); spi_disable_eth(); */
{
int i;
for(i=0 ; i < 6 ; i++ )
iv->last_mac[i] = macaddr[i];
}
is_full_duplex = (flags & PIO_INIT_FULL_DUPLEX) == PIO_INIT_FULL_DUPLEX;
/* soft reset cpu */
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
#if 1
enc28j60l_UMinDelay( 2000 ); /* wait 2 ms */
#else
/* Delay(1);*/ /* _delay_ms(2); // errata B7/2 */
/* hard-coded delay right now (TODO: import timer functions) */
for( count = 0 ; count < 20000 ; count++ )
{
mac3val |= (u08)*((volatile unsigned short*)(0x00DE0004));
}
#endif
/* wait or error */
count = 0;
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) {
count ++;
if(count == 0xfff) {
return PIO_NOT_FOUND;
}
}
writeOp(ENC28J60_WRITE_CTRL_REG, ECON1, 0);
/* return rev */
rev = readRegByte(EREVID);
/* microchip forgot to step the number on the silcon when they */
/* released the revision B7. 6 is now rev B7. We still have */
/* to see what they do when they release B8. At the moment */
/* there is no B8 out yet */
if ( (rev < 1) || (rev > 10) ) /* I don't expect a rev > 10 (+1), so I use it to check for module presence */
return PIO_NOT_FOUND; /* important: check this before any writePhy -> no module gets us 0xff back */
if (rev > 5) ++rev;
writeReg(ETXST, TXSTART_INIT);
writeReg(ETXND, TXSTOP_INIT);
/* set packet filter */
enc28j60_broadcast_multicast_filter( flags );/* flags & (PIO_INIT_BROAD_CAST|PIO_INIT_MULTI_CAST) ); */
/* BIST pattern generator? */
writeReg(EPMM0, 0x303f);
writeReg(EPMCS, 0xf7f9);
if(is_full_duplex) {
writeRegByte(MABBIPG, 0x15);
writeReg(MAIPG, 0x0012);
} else {
writeRegByte(MABBIPG, 0x12);
writeReg(MAIPG, 0x0C12);
}
/* MAC init (with flow control) */
mac3val = MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN;
if(is_full_duplex) {
mac3val |= MACON3_FULDPX;
iv->macon4=0;
}
else
{
iv->macon4 = 1<<6; /* DEFER bit */
}
iv->macon3 = mac3val;
iv->macon2 = 0x00;
iv->macon1 = MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS;
/* set packet pointers */
enc28j60_recv_reset( iv, 0 );
/* PHY init */
if(is_full_duplex) {
writePhy(PHCON1, PHCON1_PDPXMD);
writePhy(PHCON2, 0);
} else {
writePhy(PHCON1, 0);
writePhy(PHCON2, PHCON2_HDLDIS);
}
writePhy(PHLCON, 0x3c12 ); /* 3(fixed) C(LinkStat+RX Act) 1(TX Act) 2(Stretch enable) */
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_DMAIF|EIR_LINKIF|EIR_TXIF|EIR_TXERIF|EIR_RXERIF|EIR_PKTIF);
writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE|EIE_LINKIE|EIE_TXIE|EIE_TXERIE|EIE_RXERIE);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
/* wait a while for "LINK UP" -> break after 200 ms */
{
unsigned char status;
int runs = 100;
while( runs-- )
{
enc28j60_status( PIO_STATUS_LINK_UP, &status );
if( status )
break;
enc28j60l_UMinDelay( 2000 ); /* wait 2+ ms */
}
}
return PIO_OK;
}
/* ---------- exit ---------- */
void enc28j60_exit(void)
{
enc28j60_setOffline();
enc28j60l_Shutdown();
}
u08 enc28j60_setOnline( void )
{
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
return 1;
}
u08 enc28j60_setOffline( void )
{
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
return 1;
}
void enc28j60_setMAC( const u08 macaddr[6] )
{
/* set mac */
writeRegByte(MAADR5, macaddr[0]);
writeRegByte(MAADR4, macaddr[1]);
writeRegByte(MAADR3, macaddr[2]);
writeRegByte(MAADR2, macaddr[3]);
writeRegByte(MAADR1, macaddr[4]);
writeRegByte(MAADR0, macaddr[5]);
SetBank(EIR);
}
void enc28j60_getMAC( u08 macaddr[6] ) /* mainly for debug */
{
/* set mac */
macaddr[0] = readRegByte(MAADR5);
macaddr[1] = readRegByte(MAADR4);
macaddr[2] = readRegByte(MAADR3);
macaddr[3] = readRegByte(MAADR2);
macaddr[4] = readRegByte(MAADR1);
macaddr[5] = readRegByte(MAADR0);
}
/* ---------- control ---------- */
u08 enc28j60_control(u08 control_id, u08 value)
{
switch(control_id) {
case PIO_CONTROL_FLOW:
{
u08 flag;
if(is_full_duplex) {
flag = value ? 2 : 3;
} else {
flag = value ? 1 : 0;
}
writeRegByte(EFLOCON, flag);
return PIO_OK;
}
default:
return PIO_NOT_FOUND;
}
}
/* ---------- status ---------- */
u08 enc28j60_status(u08 status_id, u08 *value)
{
switch(status_id) {
case PIO_STATUS_VERSION:
*value = rev;
return PIO_OK;
case PIO_STATUS_LINK_UP:
*value = (readPhyByte(PHSTAT2) >> 2) & 1;
return PIO_OK;
default:
*value = 0;
return PIO_NOT_FOUND;
}
}
/* ---------- send ---------- */
u08 enc28j60_send(const u08 *data, u16 size)
{
/* wait for tx ready */
/* We can't do it here like on ATMEL MCUs, simply because
the Vampire SPI is way faster than 10 MBit/s so that
we would overwrite a frame still being sent out
-> wait first, then fill buffer
*/
while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
{
/* errata sheet issue 12 -> TXRTS never becomes 0 */
/* ignores errata issue 13 -> frame loss due to late collision */
/* eworks around errata sheet issue 15 -> ESTAT.LATECOL not set */
if (readRegByte(EIR) & EIR_TXERIF) {
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
}
}
/* write buffer */
enc28j60l_WriteBuffer( (u08*)data, size, TXSTART_INIT );
/* initiate send */
writeReg(ETXND, TXSTART_INIT+size);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
return PIO_OK;
}
/* ---------- recv ---------- */
/*
unrecoverable error in receiving, bad pointer or other causes
- reset the complete receive logic in order to start receiving anew
input: restart - 0 = don't re-enable RX automatically
1 = re-enable RX after resetting
*/
INLINE void next_pkt(void)
{
if (gNextPacketPtr - 1 > RXSTOP_INIT)
{
#if 1
enc28j60_recv_reset( &initvars, 1 );
#else
writeReg(ERXRDPT, RXSTOP_INIT);
gNextPacketPtr = 0;
#endif
}
else
writeReg(ERXRDPT, gNextPacketPtr - 1);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}
/* endian agnostic version of header read */
static u08 read_hdr(u16 *got_size)
{
struct {
uint8_t nextPacketL;
uint8_t nextPacketH;
uint8_t byteCountL;
uint8_t byteCountH;
uint8_t statusL;
uint8_t statusH;
} header;
readBuf(sizeof header, (uint8_t*) &header);
gNextPacketPtr = (u16)header.nextPacketL + (((u16)header.nextPacketH)<<8);
*got_size = (u16)header.byteCountL + (((u16)header.byteCountH)<<8); /* - 4;*//* -4: remove the CRC count */
return header.statusL;
}
u08 enc28j60_recv(u08 *data, u16 max_size, u16 *got_size)
{
u08 status,result;
u16 len;
/* EIR->PKTIF would be faster but errata sheet says "unreliable" */
u08 pcnt = 1+readRegByte(EPKTCNT); /* wraps to 0 for 0xff = unconnected module */
if( pcnt < 2 ) /* 0xff+1=0x00, 0x00+1=0x01, 0x01+1=0x02 */
return PIO_NOT_FOUND;
writeReg(ERDPT, gNextPacketPtr);
/* read chip's packet header */
status = read_hdr(got_size);
/* was a receive error? */
if ((status & 0x80)==0)
{
/* TODO: recv reset ? */
next_pkt();
return PIO_IO_ERR;
}
/* check size */
len = *got_size;
result = PIO_OK;
if(len > max_size) {
len = max_size;
result = PIO_TOO_LARGE;
}
/* read packet */
readBuf(len, data);
next_pkt();
return result;
}
/* ---------- has_recv ---------- */
u08 enc28j60_has_recv(void)
{
u08 ret = readRegByte(EPKTCNT);
if( !ret )
{
initvars.have_recv++;
if( initvars.have_recv > 10 )
{
/* check for bad config vars and reset hw if that is the case */
if( !enc28j60_check_config( &initvars ) )
{
enc28j60_recv_reset( &initvars, 1 );
}
}
}
else
initvars.have_recv = 0;
return ret;
}
void enc28j60_dump_regs(void)
{
/* this will work only with DOSBase known globally, not when it's redirected to Plipbase */
#ifdef _DO_DUMP
u08 val,eir,eie,econ1,econ2,estat;
u16 erxrdpt,erxwrpt,erxst,erxnd,mamxfll;
u16 macon1,macon3,macon4;
eir = readRegByte(EIR);
eie = readRegByte(EIE);
econ1 = readRegByte(ECON1);
econ2 = readRegByte(ECON2);
estat = readRegByte(ESTAT);
printf("EIR EIE ECON1 ECON2 ESTAT\n");
printf("%02x %02x %02x %02x %02x\n",
eir,eie,econ1,econ2,estat);
erxrdpt = readRegByte(ERXRDPT) + (((u16)readRegByte(ERXRDPT+1))<<8);
erxwrpt = readRegByte(ERXWRPT) + (((u16)readRegByte(ERXWRPT+1))<<8);
erxst = readRegByte(ERXST) + (((u16)readRegByte(ERXST+1))<<8);
erxnd = readRegByte(ERXND) + (((u16)readRegByte(ERXND+1))<<8);
mamxfll = readRegByte(MAMXFL) + (((u16)readRegByte(MAMXFL+1))<<8);
printf("ERXRDPT ERXWRPT ERXST ERXND MAMXFL\n");
printf("%04x %04x %04x %04x %04x\n",
erxrdpt,erxwrpt,erxst,erxnd,mamxfll);
macon1 = readRegByte(MACON1);/* + (((u16)readRegByte(MACON1+1))<<8); */
macon3 = readRegByte(MACON3);/* + (((u16)readRegByte(MACON1+3))<<8); */
macon4 = readRegByte(MACON4);/* + (((u16)readRegByte(MACON1+4))<<8); */
printf("MACON1 MACON3 MACON4\n");
printf("%02x %02x %02x\n",
macon1,macon3,macon4);
printf("MAADR\n");
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
(int)readRegByte(MAADR5),
(int)readRegByte(MAADR4),
(int)readRegByte(MAADR3),
(int)readRegByte(MAADR2),
(int)readRegByte(MAADR1),
(int)readRegByte(MAADR0) );
val = readRegByte(EIR);
#endif
}
#if 0
/* Contributed by Alex M. Based on code from: http://blog.derouineau.fr */
/* /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/ */
void enc28j60_power_down( void )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
while(readRegByte(ESTAT) & ESTAT_RXBUSY);
while(readRegByte(ECON1) & ECON1_TXRTS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
}
void enc28j60_power_up( void )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
while(!readRegByte(ESTAT) & ESTAT_CLKRDY);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
uint8_t enc28j60_do_BIST ( void )
{
#define RANDOM_FILL 0b0000
#define ADDRESS_FILL 0b0100
#define PATTERN_SHIFT 0b1000
#define RANDOM_RACE 0b1100
/* init */
spi_disable_eth();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
_delay_ms(2); /* errata B7/2 */
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ;
/* now we can start the memory test */
uint16_t macResult;
uint16_t bitsResult;
/* clear some of the registers registers */
writeRegByte(ECON1, 0);
writeReg(EDMAST, 0);
/* Set up necessary pointers for the DMA to calculate over the entire memory */
writeReg(EDMAND, 0x1FFFu);
writeReg(ERXND, 0x1FFFu);
/* Enable Test Mode and do an Address Fill */
SetBank(EBSTCON);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL);
/* wait for BISTST to be reset, only after that are we actually ready to */
/* start the test */
/* this was undocumented :( */
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* now start the actual reading an calculating the checksum until the end is */
/* reached */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
/* Compare the results */
/* 0xF807 should always be generated in Address fill mode */
if ((macResult != bitsResult) || (bitsResult != 0xF807)) {
return 0;
}
/* reset test flag */
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* Now start the BIST with random data test, and also keep on swapping the */
/* DMA/BIST memory ports. */
writeRegByte(EBSTSD, 0b10101010 /* | millis()*/);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL);
/* wait for BISTST to be reset, only after that are we actually ready to */
/* start the test */
/* this was undocumented :( */
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* now start the actual reading an calculating the checksum until the end is */
/* reached */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
/* The checksum should be equal */
return macResult == bitsResult;
}
#endif
+71
View File
@@ -0,0 +1,71 @@
/*
* enc28j60.h - Microchip ENC28J60 interface
*
* Written by
* Henryk Richter <henryk.richter@gmx.net>
*
*
* 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 _INC_ENC28J60_H
#define _INC_ENC28J60_H
/* result values */
#define PIO_OK 0
#define PIO_NOT_FOUND 1
#define PIO_TOO_LARGE 2
#define PIO_IO_ERR 3
/* init flags */
#define PIO_INIT_FULL_DUPLEX 1
#define PIO_INIT_LOOP_BACK 2
#define PIO_INIT_BROAD_CAST 4
#define PIO_INIT_FLOW_CONTROL 8
#define PIO_INIT_MULTI_CAST 16
#define PIO_INIT_PROMISC 32
/* status flags */
#define PIO_STATUS_VERSION 0
#define PIO_STATUS_LINK_UP 1
/* control ids */
#define PIO_CONTROL_FLOW 0
/* sigh, old gcc does not have stdint.h */
typedef unsigned char u08;
typedef unsigned short u16;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
/* API functions */
u08 enc28j60_init(const u08 macaddr[6], u08 flags);
void enc28j60_exit(void);
u08 enc28j60_setOnline( void );
u08 enc28j60_setOffline( void );
void enc28j60_SetSPISpeed( unsigned long clock_divider );
u08 enc28j60_send(const u08 *data, u16 size);
u08 enc28j60_recv(u08 *data, u16 max_size, u16 *got_size);
u08 enc28j60_status( u08 status_id, u08 *value);
u08 enc28j60_has_recv(void);
void enc28j60_setMAC( const u08 macaddr[6] );
void enc28j60_getMAC( u08 macaddr[6] ); /* mainly for debug */
void enc28j60_dump_regs(void);
void enc28j60_broadcast_multicast_filter( u08 flags );
#endif /* _INC_ENC28J60_H */
File diff suppressed because it is too large Load Diff
+362
View File
@@ -0,0 +1,362 @@
/*
enc28j60l.h
author: Henryk Richter <henryk.richter@gmx.net>
purpose: header for low-level register and SPI access
to ENC28J60 networking device
*/
#ifndef _INC_ENC28J60L_H
#define _INC_ENC28J60L_H
#include "compiler.h"
/*
Purpose: prepare SPI for use
input: -
output: success/failure
>0 - all fine, ready to work
<=0 - init problem (codes undefined yet)
*/
ASM SAVEDS int enc28j60l_Init( void );
/*
Purpose: shutdown SPI
input: -
output: -
*/
ASM SAVEDS void enc28j60l_Shutdown( void );
/*
Purpose: delay at least the given amount of microseconds
input: delay time in microseconds
output: -
*/
ASM SAVEDS void enc28j60l_UMinDelay(
ASMR(d0) unsigned long udelay ASMREG(d0) );
/*
Purpose: set SPI clock divider
input: clock divider 1...n
output: -
*/
ASM SAVEDS void enc28j60l_SetSPISpeed(
ASMR(d0) unsigned long divider ASMREG(d0) );
/*
purpose: read one byte from a custom register
input: op = ENC28J60_READ_CTRL_REG
address = register address, including bits denoting the bank
data = data byte
*/
ASM SAVEDS unsigned char enc28j60l_ReadOp(
ASMR(d3) unsigned char op ASMREG(d3),
ASMR(d0) unsigned char address ASMREG(d0) );
/*
purpose: write one byte to a custom register
input: op = ENC28J60_WRITE_CTRL_REG,
ENC28J60_BIT_FIELD_SET,
ENC28J60_BIT_FIELD_CLR
address = register address, including bits denoting the bank
data = data byte
*/
ASM void enc28j60l_WriteOp( ASMR(d3) unsigned char op ASMREG(d3),
ASMR(d0) unsigned char address ASMREG(d0),
ASMR(d1) unsigned char data ASMREG(d1));
/*
purpose: select register bank based on given address
input:
address = register address, including bits denoting the bank
*/
ASM SAVEDS void enc28j60l_SetBank( ASMR(d0) unsigned char address ASMREG(d0) );
/*
purpose: read one short from a custom register
input: address = register address, including bits denoting the bank
data = data word (unsigned short)
*/
ASM SAVEDS unsigned short enc28j60l_ReadReg( ASMR(d0) unsigned char address ASMREG(d0) );
/*
purpose: write one short to a custom register
input: address = register address, including bits denoting the bank
data = data word (unsigned short)
*/
ASM void enc28j60l_WriteReg( ASMR(d0) unsigned char address ASMREG(d0),
ASMR(d1) unsigned short data ASMREG(d1));
/*
purpose: read one byte from a custom register
input: address = register address, including bits denoting the bank
*/
ASM SAVEDS unsigned char enc28j60l_ReadRegByte(
ASMR(d0) unsigned char address ASMREG(d0));
/*
purpose: write one byte to a custom register
input: address = register address, including bits denoting the bank
data = data word (unsigned short)
*/
ASM void enc28j60l_WriteRegByte( ASMR(d0) unsigned char address ASMREG(d0),
ASMR(d1) unsigned char data ASMREG(d1));
/*
purpose: Read buffer from device to computer
input: buffer = buffer to read into
size = length of buffer
*/
ASM void enc28j60l_ReadBuffer( ASMR(a1) unsigned char *buffer ASMREG(a1),
ASMR(d0) unsigned int size ASMREG(d0));
/*
purpose: Write buffer from computer to device
input: buffer = buffer to write
size = length of buffer
destptr = destination pointer in device
*/
ASM void enc28j60l_WriteBuffer( ASMR(a1) unsigned char *buffer ASMREG(a1),
ASMR(d0) unsigned int size ASMREG(d0),
ASMR(d1) unsigned short destptr ASMREG(d1));
/*
Definitions based on the enc28j60.c file from the AVRlib library by Pascal Stang.
ENC28J60 Control Registers
Control register definitions are a combination of address,
bank number, and Ethernet/MAC/PHY indicator bits.
- Register address (bits 0-4)
- Bank number (bits 5-6)
- MAC/PHY indicator (bit 7)
*/
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
/* All-bank registers */
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
/* Bank 0 registers */
#define ERDPT (0x00|0x00)
#define EWRPT (0x02|0x00)
#define ETXST (0x04|0x00)
#define ETXND (0x06|0x00)
#define ERXST (0x08|0x00)
#define ERXND (0x0A|0x00)
#define ERXRDPT (0x0C|0x00)
#define ERXWRPT (0x0E|0x00)
#define EDMAST (0x10|0x00)
#define EDMAND (0x12|0x00)
/* #define EDMADST (0x14|0x00) */
#define EDMACS (0x16|0x00)
/* Bank 1 registers */
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCS (0x10|0x20)
/* #define EPMO (0x14|0x20) */
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
/* Bank 2 registers */
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPG (0x06|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFL (0x0A|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWR (0x16|0x40|0x80)
#define MIRD (0x18|0x40|0x80)
/* Bank 3 registers */
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCS (0x08|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUS (0x18|0x60)
/* ENC28J60 ERXFCON Register Bit Definitions */
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_PMEN 0x10
#define ERXFCON_MPEN 0x08
#define ERXFCON_HTEN 0x04
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
/* ENC28J60 EIE Register Bit Definitions */
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
/* ENC28J60 EIR Register Bit Definitions */
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
/* ENC28J60 ESTAT Register Bit Definitions */
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
/* ENC28J60 ECON2 Register Bit Definitions */
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
/* ENC28J60 ECON1 Register Bit Definitions */
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
/* ENC28J60 MACON1 Register Bit Definitions */
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
/* ENC28J60 MACON2 Register Bit Definitions */
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
/* ENC28J60 MACON3 Register Bit Definitions */
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
/* ENC28J60 MICMD Register Bit Definitions */
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
/* ENC28J60 MISTAT Register Bit Definitions */
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
/* ENC28J60 EBSTCON Register Bit Definitions */
#define EBSTCON_PSV2 0x80
#define EBSTCON_PSV1 0x40
#define EBSTCON_PSV0 0x20
#define EBSTCON_PSEL 0x10
#define EBSTCON_TMSEL1 0x08
#define EBSTCON_TMSEL0 0x04
#define EBSTCON_TME 0x02
#define EBSTCON_BISTST 0x01
/* PHY registers */
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
/* ENC28J60 PHY PHCON1 Register Bit Definitions */
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
/* ENC28J60 PHY PHSTAT1 Register Bit Definitions */
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
/* ENC28J60 PHY PHCON2 Register Bit Definitions */
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100
/* PHIE Register */
#define PHIE_PGEIE (1<<1)
#define PHIE_LNKIE (1<<4)
/* ENC28J60 Packet Control Byte Bit Definitions */
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01
/* SPI operation codes */
#define ENC28J60_READ_CTRL_REG 0x00
#define ENC28J60_READ_BUF_MEM 0x3A
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_WRITE_BUF_MEM 0x7A
#define ENC28J60_BIT_FIELD_SET 0x80
#define ENC28J60_BIT_FIELD_CLR 0xA0
#define ENC28J60_SOFT_RESET 0xFF
#endif /* _INC_ENC28J60L_H */
+343
View File
@@ -0,0 +1,343 @@
/*
hw.c
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
SDNet hardware interface to device
*/
#include <exec/libraries.h>
#include <proto/exec.h>
#include "device.h"
#include "hw.h"
#include <proto/vampire.h>
#include <vampire/vampire.h>
#include "vampuuid.h"
#include "enc28j60.h"
#ifndef PROTO_V2EXPNET
#define PORTID V_SDPORT
#else
#define PORTID V_WIFIPORT
#endif
extern const BYTE DeviceName[];
/* use generic board fields in current unit */
#define hw_allocated du_hwl0
#define HW_INTERVALDEF 10000
#define HW_DEFSPI 2
/* these calls might not run within the context of a process and
will be called from varying tasks and without prior init:
hw_Find_Boards()
hw_AllocBoard()
hw_ReleaseBoard()
hw_GetMacAddress()
these calls are in the context of the server task:
hw_Setup() - general init (board allocated already)
hw_Shutdown() - general shutdown (board still allocated)
hw_Attach() - make hardware ready for action (online)
hw_Detach() - put hardware into sleep mode (offline)
hw_recv_sigmask() - get signals for hardware (if any)
hw_recv_pending() - check for pending receive frames
hw_recv_frame() - receive one frame
hw_send_frame() - send one frame
hw_check_link_change() - check if HW link parameters need update
and apply them (called only when no RX/TX
in progress)
hw_config_init() - set defaults for HW
hw_config_update() - update config of HW
*/
const BYTE vres_name[] = V_VAMPIRENAME;
/* return number of boards present in system */
ASM SAVEDS LONG hw_Find_Boards( ASMR(a0) DEVBASEP ASMREG(a0) )
{
LONG ret = 0;
struct Library *VampireBase;
if( (VampireBase = OpenResource( (BYTE*)vres_name )) )
{
/* TODO: check for expansion port, i.e. distinguish V500/V600 */
/* TODO: actually verify that we have a module here */
ret = 1;
}
return ret;
}
ASM SAVEDS LONG hw_AllocBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
LONG ret = 0;
struct Library *VampireBase;
if( (VampireBase = OpenResource( (BYTE*)vres_name )) ) /* OpenResource is probably redundant */
{
if( unit == 0 )
{
ret = 1; /* def: looks good */
if( db->db_Units[unit].hw_allocated == 0 )
{
if( V_AllocExpansionPort( PORTID, (BYTE*)DeviceName ) )
ret = 0;
}
if( ret )
db->db_Units[unit].hw_allocated++;
}
}
return ret;
}
ASM SAVEDS LONG hw_ReleaseBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
LONG ret = 0;
struct Library *VampireBase;
if( (VampireBase = OpenResource( (BYTE*)vres_name )) ) /* OpenResource is probably redundant */
{
if( unit == 0) /* only one device supported */
{
ret = 1; /* def: looks good */
if( db->db_Units[unit].hw_allocated > 0 )
{
db->db_Units[unit].hw_allocated--;
if( !db->db_Units[unit].hw_allocated )
{
V_FreeExpansionPort( PORTID );
}
}
}
}
return ret;
}
/* store MAC address at "mac", 6 Bytes */
ASM SAVEDS LONG hw_GetMacAddress( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *mac ASMREG(a1)
)
{
static UBYTE addr[6] = { 0x02,0x80,0x10,0x0b,0x0a,0xff};
UBYTE vsn[8];
LONG i;
if( vampire_UUID( vsn ) > 0 )
{
for(i=0 ; i<3 ; i++ )
{
addr[3+i] = vsn[i] ^ vsn[i+5];
}
}
for( i=0 ; i<6 ; i++ )
mac[i] = addr[i];
return 0;
}
/* prepare all boards such that a hw_attach() will succeed */
ASM SAVEDS LONG hw_Setup( ASMR(a0) DEVBASEP ASMREG(a0) )
{
LONG ret = 0;
struct HWData *hwd = &db->db_hwdat;
hwd->IntSig = -1;
if( InitIntervalTimer( &hwd->ivtimer ) > 0 )
{
hwd->SigMask = hwd->ivtimer.IVT_SigMask;
ret = 1;
}
return ret;
}
/* free resources, server is quitting */
ASM SAVEDS void hw_Shutdown( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
if( hwd->IntSig >= 0 )
FreeSignal( hwd->IntSig );
ExitIntervalTimer( &hwd->ivtimer );
}
/* make hardware ready for action (online) */
ASM SAVEDS LONG hw_Attach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0))
{
struct HWData *hwd = &db->db_hwdat;
LONG ret = 1;
u08 flags;
if( unit > 0 )
return 0;
flags = PIO_INIT_BROAD_CAST;
if( hwd->multicast )
flags |= PIO_INIT_MULTI_CAST;
if( hwd->fullduplex )
flags |= PIO_INIT_FULL_DUPLEX;
if( db->db_Units[0].du_Flags & DUF_PROMISC )
flags |= PIO_INIT_PROMISC;
enc28j60_SetSPISpeed( hwd->spispeed );
if( enc28j60_init( db->db_Units[0].du_CFGAddr, flags ) != PIO_OK )
ret = 0;
else
{
enc28j60_SetSPISpeed( hwd->spispeed );
StartIntervalTimer( &hwd->ivtimer, hwd->timervalue );
}
return ret;
}
/* put hardware into sleep mode (offline) */
ASM SAVEDS void hw_Detach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0))
{
if( !unit )
{
struct HWData *hwd = &db->db_hwdat;
StopIntervalTimer( &hwd->ivtimer );
enc28j60_exit();
}
}
ASM SAVEDS void hw_ConfigInit( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
hwd->timervalue = HW_INTERVALDEF;
hwd->fullduplex = 0;
hwd->spispeed = HW_DEFSPI; /* default (see above) */
hwd->multicast = 0;
#if 0
USHORT i;
for( i=0 ; i < (USHORT)db->db_NBoards ; i++ )
{ /* no need, these are defaults */
db->db_Units[i].du_MTU = 1500;
db->db_Units[i].du_BitPerSec = 10000000;
}
#endif
}
ASM SAVEDS void hw_ConfigUpdate( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a1) void*argsv ASMREG(a1))
{
struct HWConfig *args = (struct HWConfig *)argsv; /* see hwprivate.h */
struct HWData *hwd = &db->db_hwdat;
if( args->timervalue )
hwd->timervalue = *args->timervalue;
if( args->fullduplex )
hwd->fullduplex = 1;
if( args->spispeed )
hwd->spispeed = *args->spispeed;
if( args->multicast )
hwd->multicast = 1;
}
ASM SAVEDS LONG hw_send_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1),
ASMR(d1) ULONG framesize ASMREG(d1) )
{
enc28j60_send( frame, framesize );
return 1;
}
ASM SAVEDS LONG hw_recv_pending( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
return (LONG)enc28j60_has_recv();
}
ASM SAVEDS LONG hw_recv_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1))
{
volatile SHORT sz;
LONG ret;
/* important: don't call this without enc28j60_has_recv() > 0 check */
if( PIO_OK != enc28j60_recv( frame, 1518, (short*)&sz ) )
ret = 0;
else
ret = (LONG)sz;
return (LONG)ret;
}
ASM SAVEDS ULONG hw_recv_sigmask( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
return hwd->SigMask;
}
/* check if HW link parameters need update */
ASM SAVEDS LONG hw_check_link_change( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
return 0;
}
/* process the list of multicast addresses for an appropriate
filtering list on the hardware
the server preprocesses active multicast listeners such that
this call can decide whether to enable multicast altogether,
filter with hashes etc.
*/
ASM SAVEDS LONG hw_change_multicast( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a0) ULONG unit ASMREG(d0),
ASMR(a1) struct List *mcastlist ASMREG(a1) )
{
u08 flags;
flags = PIO_INIT_BROAD_CAST;
if( db->db_Units[0].du_Flags & DUF_PROMISC )
flags |= PIO_INIT_PROMISC;
/* list non-empty ? (sufficient for ENC28J60 -> it doesn`t have hashing) */
if( mcastlist->lh_Head->ln_Succ )
flags |= PIO_INIT_MULTI_CAST;
enc28j60_broadcast_multicast_filter( flags );/* flags & (PIO_INIT_BROAD_CAST|PIO_INIT_MULTI_CAST) ); */
return 1;
}
+52
View File
@@ -0,0 +1,52 @@
/*
hwprivate.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
private structures for hardware instance
*/
#ifndef _INC_HWPRIVATE_H
#define _INC_HWPRIVATE_H
#include "compiler.h"
/* sdnet/v2expnet specific: polling timer */
#include "intervaltimer.h"
/* this struct is available in devicebase and should carry global information */
struct HWData {
ULONG SigMask; /* signal mask to wait for in server main instance */
struct IVT_TimerStruct ivtimer; /* Timer interval handling (via Interrupt) */
LONG IntSig;
/* config options */
ULONG timervalue; /* polling interval in ms */
ULONG fullduplex; /* force full duplex */
ULONG spispeed; /* SPI clock divider (>=1) */
ULONG multicast; /* multicast recv enable */
};
/* referenced from server.c */
#if (defined PROTO_V2EXPNET)
#define HW_CONFIGFILE "ENV:SANA2/v2expeth.config"
#else
#define HW_CONFIGFILE "ENV:SANA2/sdnet.config"
#endif
/* appended to COMMON_TEMPLATE */
#define HW_CONFIGTEMPLATE "TIMER/K/N,FULLDUPLEX/S,SPISPEED/K/N,MULTICAST/S"
/* referenced by server.c, hw.c */
struct HWConfig
{
struct CommonConfig common; /* import from hw.h, expected in server.c */
ULONG *timervalue;
ULONG fullduplex;
ULONG *spispeed;
ULONG multicast;
};
#endif /* _INC_HWPRIVATE_H */
+341
View File
@@ -0,0 +1,341 @@
******************************************************************************
* Name: intervaltimer.s *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
;MACHINE MC68040
include exec/types.i
include lvo/exec_lib.i
include lvo/dos_lib.i
include lvo/timer_lib.i
include exec/exec.i
include dos/dos.i
include dos/dostags.i
include exec/io.i
include dos/dosextens.i
include exec/lists.i
DO_TEST EQU 0
ifne 1
include "intervaltimer.i"
else
STRUCTURE IVT_TimerStruct,0
ULONG IVT_Interval ;<1e6 in microseconds
LONG IVT_Signal ;Signal that is sent back to task from interrupt
ULONG IVT_SigMask
APTR IVT_SigTask ;Task that is signaled
APTR IVT_Port
APTR IVT_IORequest
APTR IVT_Device
STRUCT IVT_Int,IS_SIZE ;
BYTE IVT_Init ; !=0 - initialized
BYTE IVT_Runflag ; current status ( !=0 = running )
BYTE IVT_Stopflag ; stop request if != 0
BYTE IVT_Unused ;
ULONG IVT_Counter ; Counter (statistics)
LABEL IVT_Size
endc
xdef _InterruptIntervalTimer
xdef _InitIntervalTimer
xdef _ExitIntervalTimer
xdef _StartIntervalTimer
xdef _StopIntervalTimer
CALLEXEC macro
move.l 4.w,a6
jsr _LVO\1(a6)
endm
section code,text
ifne DO_TEST
TestTimer:
lea dos_name,a1 ;string base
moveq #0,d0
move.l 4.w,a6
jsr _LVOOpenLibrary(a6)
move.l d0,dosbase
beq .nodos
move.l dosbase,a6
jsr _LVOOutput(a6) ;get stdout
move.l d0,stdout
lea timerbase,a1
bsr _InitIntervalTimer
tst.l d0
bge .ok
move.l stdout,d0
move.l #failtimer,d1
move.l dosbase,16
jsr _LVOPutStr(a6)
bra .error
.ok
lea timerbase,a1
move.l #10000,d1
bsr _StartIntervalTimer
moveq #0,d3
.loop
cmp.l #10,d3
bgt.s .exitloop
move.l dosbase,A6
moveq #10,d1
jsr _LVODelay(a6)
addq.l #1,d3
lea timerbase,a1
cmp.l #100,IVT_Counter(a1)
blt.s .loop
.exitloop:
move.l #succname,d1
cmp.l #10,d3
ble.s .succ
move.l #failname,d1
.succ:
jsr _LVOVPrintf(a6)
lea timerbase,a1
bsr _StopIntervalTimer
.error
lea timerbase,a1
bsr _ExitIntervalTimer
move.l dosbase,d0
beq.s .nodos
move.l d0,a1
move.l 4.w,a6
jsr _LVOCloseLibrary(a6)
.nodos:
rts
ENDC ;DO_TEST
; A1 = IVT_TimerStruct
_InterruptIntervalTimer:
movem.l d1-a6,-(sp)
;<- basic Timer.device ops ->
move.l a1,a2
move.l IVT_Port(a2),a0
CALLEXEC GetMsg
tst.l d0
beq.s .error
move.l IVT_SigMask(a2),d0 ;move.l IVT_Signal(a2),d0
move.l IVT_SigTask(a2),a1
CALLEXEC Signal
tst.b IVT_Stopflag(a2)
bne.s .error ;not exactly an error but same consequence
move.l a2,a1
bsr _RestartIntervalTimer
;<- end basic Timer.device ops ->
;<- useful stuff ->
addq.l #1,IVT_Counter(a2)
;<- useful stuff ->
bra.s .endintroutine
.error:
clr.b IVT_Runflag(a2)
.endintroutine:
movem.l (sp)+,d1-a6
rts
_InitIntervalTimer: ;prepare timer.device for buffer swap interrupt
movem.l d1-a6,-(sp)
move.l a1,a2
move #IVT_Size-1,d0
.clr
clr.b (a1)+ ;
dbf d0,.clr
sf IVT_Init(a2)
sf IVT_Runflag(a2)
;signal to wait for from timer output if output queue full
moveq #-1,d0
CALLEXEC AllocSignal
move.l d0,IVT_Signal(a2) ; <0 ? error, valid are 0..31
blt .error ; error: have to go (shouldn't happen)
moveq #0,d1
bset d0,d1
move.l d1,IVT_SigMask(a2)
suba.l a1,a1
CALLEXEC FindTask
move.l d0,IVT_SigTask(a2)
CALLEXEC CreateMsgPort ;Create MsgPort for timing...
move.l d0,IVT_Port(a2)
beq .error ;0=error
move.l IVT_Port(a2),a0
move.l #IOTV_SIZE,d0
CALLEXEC CreateIORequest
move.l d0,IVT_IORequest(a2)
beq .error ;0=error
lea.l IVT_Int(a2),a0
lea _InterruptIntervalTimer(pc),a1
move.l a1,IS_CODE(a0)
move.l a2,IS_DATA(a0)
move.b #NT_INTERRUPT,LN_TYPE(a0)
move.b #0,LN_PRI(a0) ;-32,-16,0,16,32
move.l IVT_Port(a2),a1
;move.b #NT_MSGPORT,LN_TYPE(a1)
move.b #PA_SOFTINT,MP_FLAGS(a1)
move.l a0,MP_SIGTASK(a1) ;Softint eintragen
lea VBtimer_name,a0
moveq #UNIT_MICROHZ,d0
move.l IVT_IORequest(a2),a1
moveq #0,d1
CALLEXEC OpenDevice
tst.l d0
beq.s .ok
moveq #0,d0
bra.s .error
.ok ;OK, timer device is ready
st IVT_Init(a2)
moveq #1,d0 ;OK
.error
movem.l (sp)+,d1-a6
;ret 0 <= err, >0 = ok
rts
_ExitIntervalTimer:
;stop timer.device
movem.l d0-a6,-(sp)
move.l a1,a2
sf IVT_Init(a2)
move.l IVT_IORequest(a2),d5
beq.s .noIO
tst.b IVT_Runflag(a2)
beq.s .noAbort
st IVT_Stopflag(a2)
ifne 0
move.l d5,a1
CALLEXEC WaitIO
else
move.l d5,a1
move.l IO_DEVICE(a1),a6
jsr DEV_ABORTIO(A6) ;clear pending requests
endc
.noAbort:
move.l d5,a1
CALLEXEC CloseDevice
move.l d5,a0
CALLEXEC DeleteIORequest
clr.l IVT_IORequest(a2)
.noIO:
move.l IVT_Port(a2),d0
beq.s .noPort
move.l d0,a0
CALLEXEC DeleteMsgPort
clr.l IVT_Port(a2)
.noPort:
move.l IVT_Signal(a2),d0
not.l d0 ;if( d0 == -1 ) d0 = 0
beq.s .nosig
not.l d0 ; swap back
CALLEXEC FreeSignal
moveq #-1,d0
move.l d0,IVT_Signal(a2)
.nosig:
movem.l (sp)+,d0-a6
rts
; input:
; A1 = IVT Structure (after init)
; D1 = Number of Microseconds
_StartIntervalTimer:
movem.l d0/a1/a2/a6,-(Sp)
move.l a1,a2
move.l d1,IVT_Interval(A2)
sf IVT_Stopflag(a2)
tst.b IVT_Runflag(a2) ;running already ? -> keep it running
bne.s .notimer
move.l a2,a1
bsr _RestartIntervalTimer
.notimer
movem.l (sp)+,d0/a1/a2/a6
rts
; Non-Public
; A1 - IVT Structure
_RestartIntervalTimer:
movem.l d0/a1/a2/a6,-(sp)
move.l a1,a2
move.l IVT_IORequest(a2),d0
beq.s .notimer
move.l d0,a1
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.b IO_FLAGS(a1)
clr.l IOTV_TIME+EV_HI(a1)
move.l IVT_Interval(A2),IOTV_TIME+EV_LO(a1)
move.l IO_DEVICE(a1),a6
jsr DEV_BEGINIO(A6)
st IVT_Runflag(a2)
.notimer
movem.l (sp)+,d0/a1/a2/a6
rts
_StopIntervalTimer:
movem.l d0-a6,-(Sp)
move.l a1,a2
st IVT_Stopflag(a2)
tst.b IVT_Runflag(a2)
beq.s .noIO
move.l IVT_IORequest(a2),d5
beq.s .noIO
move.l d5,a1
move.l IO_DEVICE(a1),a6
jsr DEV_ABORTIO(A6) ;clear pending requests
sf IVT_Runflag(a2)
.noIO:
movem.l (sp)+,d0-a6
rts
section data,data
dosbase: dc.l 0
stdout: dc.l 0
timerbase: ds.b IVT_Size
VBtimer_name: dc.b "timer.device",0
ifne DO_TEST
dos_name: dc.b "dos.library",0
succname: dc.b "success: timer worked",10,0
failname: dc.b "failure: timer didn't work",10,0
failtimer: dc.b "timer open failure",10,0
ENDC ;DO_TEST
END
+57
View File
@@ -0,0 +1,57 @@
/*
******************************************************************************
* Name: intervaltimer.h *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
*/
#ifndef _INC_INTERVALTIMER_H
#define _INC_INTERVALTIMER_H
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <devices/timer.h>
struct IVT_TimerStruct {
unsigned long IVT_Interval;
long IVT_Signal;
unsigned long IVT_SigMask;
struct Task *IVT_SigTask;
struct MsgPort *IVT_Port;
struct IORequest *IVT_IORequest;
struct Device *IVT_Device;
struct Interrupt IVT_Int;
unsigned char IVT_Init;
unsigned char IVT_Runflag;
unsigned char IVT_Stopflag;
unsigned char IVT_Unused;
unsigned long IVT_Counter;
};
#if 1
#include "compiler.h"
#else
/* self-contained in here as well */
#ifdef __SASC
#define ASM __asm
#define ASMR(x) register __ ## x
#define ASMREG(x)
#define SAVEDS __saveds
#else
#define ASM
#define ASMR(x) register
#define ASMREG(x) __asm("" #x "")
#define SAVEDS __saveds
#endif
#endif
ASM SAVEDS int InitIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
ASM SAVEDS int StartIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1),
ASMR(d1) unsigned long Microsecs ASMREG(d1) );
ASM SAVEDS int StopIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
ASM SAVEDS int ExitIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
#endif /* _INC_INTERVALTIMER_H */
+27
View File
@@ -0,0 +1,27 @@
******************************************************************************
* Name: intervaltimer.i *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
ifnd _INC_INTERVALTIMER_I
_INC_INTERVALTIMER_I EQU 1
include exec/types.i
STRUCTURE IVT_TimerStruct,0
ULONG IVT_Interval ;<1e6 in microseconds
LONG IVT_Signal ;Signal that is sent back to task from interrupt
ULONG IVT_SigMask ;signal mask
APTR IVT_SigTask ;Task that is signaled
APTR IVT_Port
APTR IVT_IORequest
APTR IVT_Device
STRUCT IVT_Int,IS_SIZE ;
BYTE IVT_Init ; !=0 - initialized
BYTE IVT_Runflag ; current status ( !=0 = running )
BYTE IVT_Stopflag ; stop request if != 0
BYTE IVT_Unused ;
ULONG IVT_Counter ; Counter (statistics)
LABEL IVT_Size
endc ;_INC_TIMERTEST_I
+126
View File
@@ -0,0 +1,126 @@
; ------------------------------------------------------------------------------
; | Flash ROM Read Serial Number |
; | Copyright (c) 2016 APOLLO TEAM |
; | Christoph Höhne, modifications by Henryk Richter |
; ------------------------------------------------------------------------------
FLASH_ID EQU $14 ;cmd=0xAB
FLASH_ADR_W EQU $00DFF1F8
FLASH_ADR_R EQU $00DFF1FA
FLASH_MAGIC EQU $42420000
FLASH_CS EQU $1
FLASH_CSn EQU $0
FLASH_CLK EQU $2
FLASH_CLKn EQU $0
FLASH_MOSI EQU $4
FLASH_MOSIn EQU $0
FLASH_MISO EQU $1
FLASH_MISOn EQU $0
;VAMPIRE_MAGIC EQU ascii "Vamp"
VAMPIRE_MAGIC EQU $56616D70
xdef _vampire_UUID
;-------- Get 64 Bit unique ID --------------------
; Input
; A1 - pointer to 8 Bytes storage
; Output
; D0 = 0 - failure
; >0 - success
_vampire_UUID:
movem.l d1-a6,-(sp) ;lazy register store
bsr flashreadid
cmpi.b #FLASH_ID,d0
bne.s error$
bsr flashuniqueid
move.l d0,(a1)
move.l d1,4(a1)
moveq #1,d0
bra.s return$
error$:
moveq #0,d0
return$
movem.l (sp)+,d1-a6
rts
; uint8_t flashreadid(void)
; D0
flashreadid:
move.b #$AB,d1
bsr flashbyte
bsr flashbyte
bsr flashbyte
bsr flashbyte
bsr flashbyte
bsr flashdeselect
rts
; uint64_t flashuniqueid(void)
; D0/D1
flashuniqueid:
move.b #$5A,d1
bsr flashbyte
clr d1
bsr flashbyte
clr d1
bsr flashbyte
move.b #$F8,d1
bsr flashbyte
bsr flashbyte
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
move.l d1,d3
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
move.l d3,d0
bsr flashdeselect
rts
; void flashselect(void)
;flashselect:
; move.l #FLASH_MAGIC+FLASH_CSn+FLASH_CLKn,d0
; move.l d0,FLASH_ADR_W
; rts
; void flashdeselect(void)
flashdeselect:
move.l #FLASH_MAGIC+FLASH_CS+FLASH_CLK,d3
move.l d3,FLASH_ADR_W
rts
; uint8_t flashbyte(uint8_t)
; D0 D1
flashbyte:
moveq #7,d2
;bsr flashselect
move.l #FLASH_MAGIC+FLASH_CSn+FLASH_CLKn,d0
move.l d0,FLASH_ADR_W
flashbyteloop:
lsl.b #1,d1
scs.b d0
andi.w #FLASH_MOSI,d0
move.l d0,FLASH_ADR_W
ori.w #FLASH_CLK,d0
move.l d0,FLASH_ADR_W
or.w FLASH_ADR_R,d1
dbra d2,flashbyteloop
move.b d1,d0
rts
+24
View File
@@ -0,0 +1,24 @@
/*
vampuuid.h
author: Henryk Richter <henryk.richter@gmx.net>
purpose: Vampire UUID reading prototypes
*/
#ifndef _INC_vampuuid_H
#define _INC_vampuuid_H
#include "compiler.h"
/*
Purpose: Read UUID of Vampire cards
input: buffer - storage for the returned UUID (8 Bytes)
output: success/failure
>0 - all fine, ready to work
<=0 - init problem (codes undefined yet)
*/
ASM SAVEDS int vampire_UUID( ASMR(a1) unsigned char *buffer ASMREG(a1) );
#endif /* __INC_vampuuid_H */
+861
View File
@@ -0,0 +1,861 @@
/*
server.c
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Main Dispatcher
TODO: sort multicast addresses and ranges
*/
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>
#include <proto/timer.h>
#include <dos/dostags.h>
#include <devices/timer.h>
#include "device.h"
#include "hw.h"
#include "macros.h"
#include "server.h"
/* (avoid external link libraries) this call is not time critical */
static void MemSet( void *a, ULONG val, ULONG size )
{
UBYTE *p = (UBYTE*)a;
while( size-- )
*p++ = (UBYTE)val;
}
static void server_AbortList( DEVBASEP, struct List*list )
{
struct IOSana2Req *ioreq;
while( (ioreq = (struct IOSana2Req *)GetHead( list )) )
{
Remove( (struct Node*)ioreq );
ioreq->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ioreq->ios2_WireError = S2WERR_UNIT_OFFLINE;
svTermIO( db, ioreq );
}
}
/* cancel all pending requests */
static void server_Offline_CancelRequests( DEVBASEP , ULONG unit )
{
struct SanaReadType *type;
struct SanaReader *rd;
D(("server_Offline_CancelRequests for unit %ld\n",unit));
ObtainSemaphore( &db->db_Units[unit].du_Sem );
/* start with the read list -> types -> readers -> reqlist */
type = (struct SanaReadType *)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
rd = (struct SanaReader *)GetHead( &type->srt_Readers );
while( rd )
{
server_AbortList( db, &rd->snr_Requests );
rd = GetSucc( rd );
}
type = GetSucc( type );
}
server_AbortList( db, (struct List*)&db->db_Units[unit].du_WriteQueue );
server_AbortList( db, (struct List*)&db->db_Units[unit].du_EventQueue );
server_AbortList( db, (struct List*)&db->db_Units[unit].du_ReadOrphans );
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
/* clear Multicast list, TODO: subroutine */
{
struct MCastEntry *entry;
while(1)
{
entry = (struct MCastEntry *)db->db_svdat.sv_mclist.lh_Head;
if( !entry->mce_Node.mln_Succ )
break;
REMOVE(entry);
FreeMem(entry,sizeof(struct MCastEntry));
}
}
D(("server_Offline_CancelRequests done\n"));
}
static LONG server_setOnline( DEVBASEP , ULONG unit, ULONG evt )
{
D(("Unit %ld online command\n",unit));
if( db->db_Units[unit].du_Flags & DUF_ONLINE )
{
D(("Was Online already\n"));
return SERR_OK;
}
if( !hw_Attach( db, unit ) )
{
D(("Hardware hw_attach Fail\n"));
return SERR_ERROR;
}
D(("hw_attach success\n"));
{ /* track last opening time */
struct timerequest timereq;
struct Library *TimerBase;
MemSet( &timereq, 0, sizeof(struct timerequest));
OpenDevice("timer.device", 0, (struct IORequest*)&timereq, 0);
TimerBase = (struct Library *)timereq.tr_node.io_Device;
GetSysTime( &db->db_Units[unit].du_DevStats.LastStart );
CloseDevice( (struct IORequest*)&timereq);
}
db->db_Units[unit].du_Flags |= DUF_ONLINE;
if( evt )
{
ObtainSemaphore( &db->db_Units[unit].du_Sem );
server_SendEvent(db, unit, S2EVENT_ONLINE );
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
}
D(("Unit %ld online\n",unit));
return SERR_OK;
}
static void server_setOffline( DEVBASEP , ULONG unit, ULONG evt )
{
D(("Unit %ld offline command\n",unit));
if( !(db->db_Units[unit].du_Flags & DUF_ONLINE ))
return;
hw_Detach( db,unit );
db->db_Units[unit].du_Flags &= ~(DUF_ONLINE);
if( evt )
{
ObtainSemaphore( &db->db_Units[unit].du_Sem );
server_SendEvent(db, unit, S2EVENT_OFFLINE );
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
}
D(("Unit %ld offline\n",unit));
}
/* check for online status following closeDevice */
static void server_CloseDevice_offline( DEVBASEP )
{
LONG unit;
unit=0;BOARDLOOP( ( ; unit < db->db_NBoards ; unit++ ) )
{
/* bring unit offline if no opener left */
if( !db->db_Units[unit].du_OpenCount )
{
if( db->db_Units[unit].du_Flags & DUF_ONLINE )
{
server_setOffline( db, unit, 0 );
server_Offline_CancelRequests( db, unit );
}
}
}
}
/* IMPORTANT: base pointer is unavailable here until
retrieved from incoming message */
static struct ServerMsg *server_startup(void)
{
struct ServerMsg *msg;
struct Process *self;
struct { struct Library *db_SysBase; } *db = (void*)0x4;
self = (struct Process*)FindTask(NULL);
D(("Waiting for Message from Device\n"));
WaitPort( &self->pr_MsgPort);
msg = (struct ServerMsg *)GetMsg( &self->pr_MsgPort );
return msg;
}
/* real init, now with active device base */
static LONG server_init( DEVBASEP )
{
LONG ret = SERR_ERROR;
NEWLIST( &db->db_svdat.sv_mclist ); /* list of multicast addresses to accept on RX */
db->db_ServerPort = CreateMsgPort(); /* forwarded IO Requests */
if( db->db_ServerPort )
{
if( hw_Setup( db ) ) /* management stuff for hardware: interrupts, timers, memory etc. */
{
db->db_svdat.sv_framesize = ETH_MTU+18+8; /* 1514 = RAW IEE802.2, 1518 = with VLAN TAG, +8 for safety */
db->db_svdat.sv_frame = AllocMem( db->db_svdat.sv_framesize, MEMF_PUBLIC );
if( db->db_svdat.sv_frame )
ret = SERR_OK;
}
}
return ret;
}
static void server_up( DEVBASEP , struct ServerMsg *msg, LONG updown )
{
msg->sm_result = updown;
ReplyMsg( &msg->sm_msg );
D(("Replied to Startup Message with %ld\n",updown));
}
static LONG server_shutdown( DEVBASEP )
{
hw_Shutdown( db );
if( (db->db_svdat.sv_framesize) && (db->db_svdat.sv_frame) )
FreeMem( db->db_svdat.sv_frame, db->db_svdat.sv_framesize );
db->db_svdat.sv_frame = (0);
if( db->db_ServerPort )
DeleteMsgPort(db->db_ServerPort);
db->db_ServerPort = (0);
return SERR_OK;
}
/* read config file (if present) and reconfigure HW parameters */
const BYTE configtemplate[] = COMMON_TEMPLATE HW_CONFIGTEMPLATE;
static LONG server_Config( DEVBASEP )
{
BPTR cfgfile;
LONG i;
hw_ConfigInit( db ); /* hw defaults */
if((cfgfile = Open(HW_CONFIGFILE, MODE_OLDFILE))) /* see hwprivate.h for CONFIGFILE */
{
struct HWConfig args; /* see hwprivate.h */
struct RDArgs *readargs;
cfgfile = SelectInput(cfgfile); /* get default Input */
MemSet( &args, 0, sizeof( args ) );
readargs = ReadArgs( (BYTE*)configtemplate, (LONG *)&args, NULL);
Close( SelectInput( cfgfile ) ); /* restore default input and retrieve cfgfile */
if( readargs )
{
/* copy common variables */
if(args.common.priority)
SetTaskPri((struct Task*)db->db_ServerProc,BOUNDS(*args.common.priority, MINPRIORITY, MAXPRIORITY));
i=0;BOARDLOOP( ( ; i < db->db_NBoards ; i++ ) )
{
if( args.common.bps )
db->db_Units[i].du_BitPerSec = *args.common.bps;
if( args.common.mtu )
db->db_Units[i].du_MTU = *args.common.mtu;
}
#if 0 /* ignored for now but kept in template for compatibility with old codebase */
if(args.common.nospecialstats)
#endif
hw_ConfigUpdate( db, &args );
FreeArgs( readargs );
}
}
return SERR_OK;
}
static LONG CMPeqMAC( BYTE *a, BYTE *b )
{
LONG i;
for( i=0 ; i<6 ; i++ )
{
if( a[i] != b[i] )
return 0;
}
return 1;
}
static LONG server_changeMulticast( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq, ULONG add_del )
{
/* step 1: find start address in active list */
struct MCastEntry *entry;
LONG ret = 1;
entry = (struct MCastEntry *)db->db_svdat.sv_mclist.lh_Head;
while( entry->mce_Node.mln_Succ )
{
if( CMPeqMAC( entry->mce_start, ioreq->ios2_SrcAddr ) )
break;
entry = (struct MCastEntry *)entry->mce_Node.mln_Succ;
}
/* did we have a prior entry ? */
if( entry->mce_Node.mln_Succ )
{
if( add_del )
{
entry->mce_refcount++;
goto apply;
}
else
{
if( entry->mce_refcount > 1 )
{
entry->mce_refcount--;
goto apply;
}
else
{
REMOVE(entry);
FreeMem( entry, sizeof( struct MCastEntry ) );
goto apply;
}
}
}
/* prior not found */
if( !add_del )
{
ret = 0; /* ERROR: CAN`T DELETE NONEXISTENT ENTRY */
goto donothing;
}
/* add new entry */
entry = (struct MCastEntry*)AllocMem( sizeof( struct MCastEntry ), MEMF_PUBLIC|MEMF_CLEAR );
if( !entry )
{
ret = 0;
goto donothing;
}
entry->mce_refcount = 1;
COPYMAC( entry->mce_start, ioreq->ios2_SrcAddr );
COPYMAC( entry->mce_stop, ioreq->ios2_SrcAddr );
ADDTAIL( &db->db_svdat.sv_mclist, entry );
apply:
hw_change_multicast( db, unit, &db->db_svdat.sv_mclist );
donothing:
return ret;
}
/* forwarded commands (online/offline etc.) */
static LONG server_HandleS2Commands( DEVBASEP )
{
ULONG unit;
struct IOSana2Req *ioreq;
while( (ioreq = (struct IOSana2Req *)GetMsg(db->db_ServerPort)) )
{
unit = (ULONG)ioreq->ios2_Req.io_Unit;
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
switch( ioreq->ios2_Req.io_Command)
{
case S2_ONLINE:
if( server_setOnline(db,unit,1 ) != SERR_OK )
{
ioreq->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
case S2_OFFLINE:
server_setOffline( db, unit, 1 ); /* ,1: send event to opener */
server_Offline_CancelRequests( db, unit );
break;
case S2_CONFIGINTERFACE:
COPYMAC( db->db_Units[unit].du_CFGAddr, ioreq->ios2_SrcAddr );
if( db->db_Units[unit].du_Flags & DUF_ONLINE )
server_setOffline( db, unit,0 );
server_setOnline( db, unit,0 );
break;
case S2_ADDMULTICASTADDRESS:
if( server_changeMulticast( db, unit, ioreq, 1 ) )
{
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
else
{
ioreq->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
case S2_DELMULTICASTADDRESS:
if( server_changeMulticast( db, unit, ioreq, 0 ) )
{
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
else
{
ioreq->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
break;
default: /* shouldn't happen */
ioreq->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
break;
}
svTermIO( db, ioreq );
}
return SERR_OK;
}
/* error conditions sent via EventQueue */
static LONG server_SendEvent( DEVBASEP, ULONG unit, ULONG evtmask )
{
struct IOSana2Req *ioreq;
/* Semaphore is locked outside */
/* ObtainSemaphore( &db->db_Units[unit].du_Sem ); */
for( ioreq = (struct IOSana2Req *)db->db_Units[unit].du_EventQueue.lh_Head;
(ioreq->ios2_Req.io_Message.mn_Node.ln_Succ);
ioreq = (struct IOSana2Req *)ioreq->ios2_Req.io_Message.mn_Node.ln_Succ )
{
if( ioreq->ios2_WireError & evtmask )
{
Remove( (struct Node*)ioreq );
svTermIO( db, ioreq );
break;
}
}
/* ReleaseSemaphore( &db->db_Units[unit].du_Sem ); */
return SERR_OK;
}
/* handle write errors */
LONG server_writeerror( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq, LONG code )
{
if (code == SERR_BUFFER_ERROR) /* no buffer management callback or copy error */
{
D(("buffer error (TX)\n"));
server_SendEvent(db, unit, S2EVENT_ERROR | S2EVENT_BUFF | S2EVENT_SOFTWARE);
/* db->db_SpecialStats[S2SS_TXERRORS].Count++; */
ioreq->ios2_Req.io_Error = S2ERR_SOFTWARE;
ioreq->ios2_WireError = S2WERR_BUFF_ERROR;
}
else /* if (code != SERR_OK) */
{
D(("TX error\n"));
server_SendEvent(db, unit, S2EVENT_ERROR | S2EVENT_TX | S2EVENT_HARDWARE);
/* db->db_SpecialStats[S2SS_TXERRORS].Count++; */
ioreq->ios2_Req.io_Error = S2ERR_TX_FAILURE;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
}
Remove((struct Node*)ioreq);
svTermIO( db, ioreq ); /* SANA2IOF_QUICK is off for write queue, just ReplyMsg */
return 0;
}
#ifndef EXTERNAL_WRITE_FRAME
/* note: entering here with locked semaphore */
static LONG write_frame( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq )
{
LONG ret = SERR_OK;
UBYTE *copy_ptr,*frame;
ULONG framesize;
struct db_BufferManagement *dbm;
D(("write: type %08lx, size %ld\n",ioreq->ios2_PacketType,
ioreq->ios2_DataLength));
frame = db->db_svdat.sv_frame;
framesize = ioreq->ios2_DataLength;
copy_ptr = frame;
/* copy raw frame: simply overwrite ethernet frame part of plip packet */
if( !(ioreq->ios2_Req.io_Flags & SANA2IOF_RAW) )
{
COPYMAC( frame, ioreq->ios2_DstAddr ); /* macro in server.h */
COPYMAC( frame+6, db->db_Units[unit].du_CFGAddr );
*( (USHORT*)(frame+12)) = ioreq->ios2_PacketType;
copy_ptr += 14;
framesize += 14;
}
dbm = (struct db_BufferManagement *)ioreq->ios2_BufferManagement;
/* copy32 is valid ptr, even if the stack offers only regular CopyFromBuffer */
if( (*dbm->dbm_CopyFromBuffer32)(copy_ptr,ioreq->ios2_Data, ioreq->ios2_DataLength) )
{
send:
D(("+hw_send\n"));
ret = hw_send_frame(db, unit, frame, framesize) ? SERR_OK : SERR_ERROR;
D(("-hw_send\n"));
}
else
{
/* if copy32 fails, try regular call */
if( (*dbm->dbm_CopyFromBuffer)(copy_ptr,ioreq->ios2_Data, ioreq->ios2_DataLength) )
goto send;
else
ret = SERR_BUFFER_ERROR;
}
return ret;
}
#endif
static LONG server_writequeue( DEVBASEP, ULONG unit )
{
LONG code;
struct IOSana2Req *ioreq,*nextio;
ObtainSemaphore( &db->db_Units[unit].du_Sem );
#if 1
/* whole queue per call */
for( ioreq = (struct IOSana2Req *)db->db_Units[unit].du_WriteQueue.lh_Head;
(nextio= (struct IOSana2Req *)ioreq->ios2_Req.io_Message.mn_Node.ln_Succ);
ioreq = nextio )
#else
/* single write per call */
ioreq = (struct IOSana2Req *)db->db_Units[unit].du_WriteQueue.lh_Head;
if( ioreq->ios2_Req.io_Message.mn_Node.ln_Succ )
#endif
{
code = write_frame( db, unit, ioreq );
if( code >= 0 )
{
/* statistics ? */
if( db->db_Units[unit].du_Flags & DUF_TYPETRACK )
{
struct SanaReadType *type;
type = (struct SanaReadType *)GetHead( &db->db_Units[unit].du_ReadTypes );
while( type )
{
if( type->srt_Type == ioreq->ios2_PacketType )
{
type->srt_Sana2PacketTypeStats.PacketsSent++;
type->srt_Sana2PacketTypeStats.BytesSent += ioreq->ios2_DataLength;
break;
}
type = GetSucc( type );
}
}
db->db_Units[unit].du_DevStats.PacketsSent++;
ioreq->ios2_Req.io_Error = S2ERR_NO_ERROR;
ioreq->ios2_WireError = S2WERR_GENERIC_ERROR;
REMOVE( (struct Node*)ioreq );
svTermIO( db, ioreq ); /* SANA2IOF_QUICK is off for write queue, just ReplyMsg */
}
else
{
/* error handling */
server_writeerror( db, unit, ioreq, code );
}
}
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
return SERR_OK;
}
/* note: entering here with locked semaphore */
static void server_read_frame( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq, UBYTE *frame, ULONG framesize )
{
struct db_BufferManagement *dbm;
ULONG bcflag = 0;
COPYMAC( ioreq->ios2_DstAddr, frame ); /* macro in server.h */
COPYMAC( ioreq->ios2_SrcAddr, frame+6 );
if( *frame & 1 ) /* just check Ethernet MULTICAST flag */
{
ULONG mac = *((ULONG*)frame );
bcflag = SANA2IOF_MCAST;
if( mac == 0xffffffff ) /* evil: ignore the lower 16 bit... */
bcflag = SANA2IOF_BCAST; /* special case of multicast, actually */
#if 0
mac >>= 8;
if( mac == 0x0001005e ) /* IPv4 MCAST */
bcflag = SANA2IOF_MCAST;
#endif
}
if( !(ioreq->ios2_Req.io_Flags & SANA2IOF_RAW) )
{
frame += 14; /* skip DST,SRC,Type */
framesize -= 18; /* also remove CRC */
D(("plain mode: frame flags %ld\n",bcflag));
}
ioreq->ios2_Req.io_Flags &= SANA2IOF_RAW; /* ignore all other flags */
ioreq->ios2_Req.io_Flags |= bcflag;
ioreq->ios2_Req.io_Error = 0;
ioreq->ios2_WireError = 0;
dbm = (struct db_BufferManagement *)ioreq->ios2_BufferManagement;
D(("ReadFrm IO %lx, DBM %lx frm %lx frmsz %ld\n",(ULONG)ioreq,(ULONG)dbm,(ULONG)frame,framesize));
if(!(*dbm->dbm_CopyToBuffer32)(ioreq->ios2_Data, frame, framesize ))
{
if(!(*dbm->dbm_CopyToBuffer)(ioreq->ios2_Data, frame, framesize ))
{
ioreq->ios2_Req.io_Error = S2ERR_SOFTWARE;
ioreq->ios2_WireError = S2WERR_BUFF_ERROR;
}
}
D(("Copied io_Error %ld\n",(LONG)ioreq->ios2_Req.io_Error));
Remove( (struct Node*)ioreq );
svTermIO( db, ioreq );
}
/* it is assumed that hw_recv_pending() was called before entering here */
static LONG server_readqueue( DEVBASEP, ULONG unit )
{
struct SanaReadType *type;
struct SanaReader *rd;
struct IOSana2Req *ioreq;
UBYTE *frame = db->db_svdat.sv_frame;
LONG framesize;
LONG frametype;
LONG dropflag;
while(1)
{
if( (framesize = hw_recv_frame(db, unit, frame) ) > 0 )
{
/* TODO: devstats */
frametype = *( (USHORT*)(frame+12) );
D(("Have Frame size %ld type %ld\n",framesize,frametype));
ObtainSemaphore( &db->db_Units[unit].du_Sem );
type = (struct SanaReadType *)db->db_Units[unit].du_ReadTypes.lh_Head;
while( (type->srt_Node.mln_Succ) )
{
if( type->srt_Type == frametype )
goto havetype;
type = (struct SanaReadType *)type->srt_Node.mln_Succ;
}
/* desired type not found, get orphan list */
db->db_Units[unit].du_DevStats.UnknownTypesReceived++;
orphan:
ioreq = (struct IOSana2Req *)GetHead( (struct List*)&db->db_Units[unit].du_ReadOrphans );
if( ioreq )
{
D(("Orphaned Frame\n"));
ioreq->ios2_PacketType = frametype;
server_read_frame( db, unit, ioreq, frame, framesize );
}
else
{
D(("No ioreq for orphaned frame\n"));
}
goto nextframe;
havetype:
/* push frame to all readers */
/*if( type->srt_Flags & SRTF_TRACKTYPE )*/ /* stats ? */
{
/* type->srt_Sana2PacketTypeStats.PacketsSent++; */
/* type->srt_Sana2PacketTypeStats.BytesSent++; */
/* type->srt_Sana2PacketTypeStats.PacketsDropped++; */
type->srt_Sana2PacketTypeStats.PacketsReceived++;
type->srt_Sana2PacketTypeStats.BytesReceived += framesize;
}
db->db_Units[unit].du_DevStats.PacketsReceived++;
rd = (struct SanaReader *)type->srt_Readers.lh_Head;
dropflag = 1;
while( rd->snr_Node.mln_Succ )
{
ioreq = (struct IOSana2Req *)rd->snr_Requests.lh_Head;
if( ioreq->ios2_Req.io_Message.mn_Node.ln_Succ )
{
server_read_frame( db, unit, ioreq, frame, framesize );
dropflag = 0;
}
rd = (struct SanaReader *)rd->snr_Node.mln_Succ;
}
if( dropflag )
{
db->db_Units[unit].du_DevStats.Overruns++;
type->srt_Sana2PacketTypeStats.PacketsDropped++;
D(("No Reader for frame\n"));
goto orphan; /* does it help if we give the frame to the orphan list? */
}
nextframe:
ReleaseSemaphore( &db->db_Units[unit].du_Sem );
}
else
{
/* TODO: check for error, send S2EVENT_HARDWARE|S2EVENT_ERROR|S2EVENT_RX along with stats db_DevStats.BadData */
goto end;
}
}
end:
return SERR_OK;
}
VOID SAVEDS ServerTask(void)
{
struct ServerMsg *msg;
ULONG wmask,gotmask,spcmask,portsigmask,recvmask,i=0,action;
struct IOSana2Req*wr;
struct DevUnit *curunit;
DEVBASEP = (0); /* Don`t call Libraries until server_startup() succeeds! */
msg = server_startup();
if( !msg )
goto QuitServer; /* unlikely */
db = msg->sm_devbase;
if( !server_init( db ) )
{
server_up( db, msg, 0 );
goto QuitServer;
}
server_Config( db ); /* load config (if present) */
server_up( db, msg, 1 ); /* init done, server is active */
/*----------------------------------------------------------------------*/
/*-------------------------- MAIN LOOP --------------------------------*/
/*----------------------------------------------------------------------*/
portsigmask = 1<<db->db_ServerPort->mp_SigBit;
recvmask = hw_recv_sigmask( db );
wmask = SIGBREAKF_CTRL_F|SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|portsigmask|recvmask;
spcmask= SIGBREAKF_CTRL_C|portsigmask|SIGBREAKF_CTRL_D;
D(("Portsigmask %ld RecvMask %ld wmask %ld\n",portsigmask,recvmask,wmask));
action = 0;
gotmask = 0;
while(1)
{
if( !action ) /* queues quiet ? -> sleep */
{
i=0;BOARDLOOP( ( ; i < db->db_NBoards ; i++ ) )
{ /* housekeeping -> check for link change on idle, re-enable interrupts etc. */
hw_check_link_change(db,i);
}
gotmask = Wait(wmask);
}
action = 0; /* any action on the queues ? */
curunit = &db->db_Units[0];
i=0;BOARDLOOP( (; i < db->db_NBoards ; i++ ) ) /* skip the for() when just a single device is supported */
{
if( curunit->du_Flags & DUF_ONLINE )
{
/* D(("BoardLoop %ld\n",i)); */
wr = (struct IOSana2Req *)curunit->du_WriteQueue.lh_Head; /* don't bother with semaphore, lh_Head is always !=0 */
if( wr->ios2_Req.io_Message.mn_Node.ln_Succ ) /* quick check for write reqs */
{
server_writequeue( db, i );
action = 1;
}
if( hw_recv_pending(db,i) )
{
D(("Read on unit %ld start\n",i));
server_readqueue( db, i );
D(("Read on unit %ld stop\n",i));
action = 1;
}
}
curunit++;
}
if( gotmask & spcmask ) /* rare flags */
{
if( gotmask & portsigmask )
{
D(("SANA-II request handling commencing\n"));
server_HandleS2Commands( db );
gotmask &= ~portsigmask;
}
if( gotmask & SIGBREAKF_CTRL_D )
{
D(("Got CTRL-D in Server. Checking Online status\n"));
gotmask &= ~SIGBREAKF_CTRL_D;
server_CloseDevice_offline( db );
}
if( gotmask & SIGBREAKF_CTRL_C )
{
D(("Got CTRL-C in Server. leaving.\n"));
break;
}
}
}
server_shutdown( db );
QuitServer:
if( db )
db->db_ServerProc = (0);
{ /* revert to "real" execbase, in case that we never got the startup msg */
struct { struct Library *db_SysBase; } *db = (void*)0x4;
D(("Server exiting"));
Forbid();
}
}
+64
View File
@@ -0,0 +1,64 @@
/*
server.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
Main Dispatcher defs and protos
*/
#ifndef _INC_SERVER_H
#define _INC_SERVER_H
#define ETH_MTU 1500
#define MINPRIORITY -120
#define MAXPRIORITY 120
#define SERR_OK 1
#define SERR_ERROR 0
#define SERR_BUFFER_ERROR -1
#define SERR_TX_ERROR -2
#define SERR_RX_ERROR -3
#if (MAX_UNITS > 1)
#define BOARDLOOP( _a_ ) for _a_
#else
#define BOARDLOOP( _a_ )
#endif
static void MemSet( void *a, ULONG val, ULONG size );
static struct ServerMsg *server_startup(void);
static LONG server_init( DEVBASEP );
static void server_up( DEVBASEP , struct ServerMsg *msg, LONG updown );
static LONG server_shutdown( DEVBASEP );
static LONG server_Config( DEVBASEP );
static LONG server_writequeue( DEVBASEP, ULONG unit );
static LONG server_HandleS2Commands( DEVBASEP );
static LONG server_SendEvent( DEVBASEP, ULONG unit, ULONG evtmask );
LONG server_writeerror( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq, LONG code );
#ifdef EXTERNAL_WRITE_FRAME
LONG write_frame( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq );
#else
static LONG write_frame( DEVBASEP, ULONG unit, struct IOSana2Req *ioreq );
#endif
static LONG server_writequeue( DEVBASETYPE*, ULONG );
static LONG server_setOnline( DEVBASETYPE* , ULONG, ULONG );
static void server_setOffline( DEVBASETYPE* , ULONG, ULONG );
static void server_CloseDevice_offline( DEVBASETYPE * );
#define svTermIO(_a_,_b_) ReplyMsg( (struct Message*)_b_)
#define COPYMAC( _d_, _s_ ) \
{\
USHORT *dst = (USHORT*)(_d_);\
USHORT *src = (USHORT*)(_s_);\
*dst++ = *src++;\
*dst++ = *src++;\
*dst = *src;\
}
#endif /* _INC_SERVER_H */