Files
Zorro-LAN-IDE/Driver/baxnet/source/enc624j6net/hw.c
T
Henryk Richter b13808f240 Updated LAN driver
- changed directory layout
- Multicast support (automatic RX filter adjustment)
- MC68000 target in Makefile
- clarified licensing (GPLv3)
- README files
2018-06-14 08:20:30 +02:00

451 lines
12 KiB
C

/*
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 *mcastlist ASMREG(a1) )
{
/* TODO: implement Multicast hashing */
APTR boardbase = db->db_Units[unit].duh_BASE;
ULONG flags;
flags = PIO_INIT_BROAD_CAST;
if( db->db_Units[0].du_Flags & DUF_PROMISC )
flags |= PIO_INIT_PROMISC;
/* list non-empty ? (kludge right now, can be done more fine grained) */
if( mcastlist->lh_Head->ln_Succ )
flags |= PIO_INIT_MULTI_CAST;
enc624j6l_broadcast_multicast_filter(boardbase, flags );/* flags & (PIO_INIT_BROAD_CAST|PIO_INIT_MULTI_CAST) ); */
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 );
}