/* hw.c (C) 2018 Henryk Richter SDNet hardware interface to device */ #include #include /*#include #include */ #include #include #include #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 ); }