437 lines
12 KiB
C
437 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 *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 );
|
|
}
|
|
|
|
|