#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <tos.h>
#include <time.h>
#include "lancemem.h"
#include "lancedrv.h"
#include "pktqueue.h"
#include "pktdrv.h"
#include "cookie.h"
#include "inetcust.h"

#define noDEBUG
#define noDEBUGINTR
#define DEBUGPKT
#define noDEBUGFREECNT


int  retval;
int	 term = 0;

#define TIMEOUTCNT	10000			/* count down for lance to come up */

short new_stack[ISTACKSIZE/2];			/* interrupt stack */
char *old_stack;						/* must be immediately behind new_stack */
void ( *ihandler )(void) = NULL;		/* pointer to interrupt handler */
long old_pterm;
long lnc_vector = LANCEIVEC * sizeof(char *);

int lnc_reset(void);				/* reset lance, init datastructures  */
void lnc_intr(void);				/* interrupt handler */
void memcopy(register char *,register char *,register int);	/* own memcopy routine */
void (* old_berr)();				/* old bus error handler */
extern void berr_catch(void);		/* bus error handler for ST/STE */
extern void berr_tt_catch(void);	/* bus error handler for TT */
int memok;


volatile TMD	*tmd_sent;		/* last sent packet */
PKTBUF 			*pkt_ring[RECVBUFFS];	/* pointers to packets waiting for recv */
int				pkt_recv;		/* index of buufer receiving next packet */
PKTPOOL			*p_free;		/* queue of free packets */
u_short			freecnt;		/* number of free packets */
volatile int	pkt_sent = 0;	/* flag if pkt is successfully sent */
HADDR	my_haddr;				/* my hardware address */

procref ext_tab[8] = 				/* subfunction table */
{
	net_reset,
	net_open,
	net_release,
	net_send,
	net_getadr,
	net_info,
	(procref)net_pktalloc,
	net_pktfree
};

PROTOCOL protos[MAXPROTOCOLS];		/* protocols to serve */
int protocols = 0;					/* number of active protocols */

et_stat stat;						/* statistics block */

long net_resets(void);

int read_inf(void)
{
COOKIE *cookie;

	cookie = get_cookie(INETCUSTCOOKIE);
	if(!cookie || !cookie->val) return(0);
	memcopy(my_haddr,((INETCUST *)(cookie->val))->haddr,(int)sizeof(HADDR));
	return(1);
}




int net_info(int len, char *buf)
{
	if(!buf) return(0);
	if(buf == (char *)1L) 
	{
		lnc_reset();
		return(0);
	}
	stat.st_free = freecnt;
	memcopy(buf,(char *)&stat,(size_t)len<sizeof(stat)?len:(int)sizeof(stat));
	return(len);
}


int net_open(int type, int (*handler)(int,char *))
{
int i,new;
	
	if(!handler) return(EPARAM);
	if(protocols >= MAXPROTOCOLS) return(EPROTAVAIL);
	new = -1;
#ifdef RESET_ON_OPEN
	if(!protocols)
	{
		net_resets();
		if(ihandler == NULL) return(EINIT);
	}
#endif
	for(i=0; i<MAXPROTOCOLS; i++)
	{			/* protocol already used */
		if(protos[i].type == type) return(EPROTUSED);
		if(protos[i].type == ET_UNUSED &&  new<0 ) new = i; /* find first free entry */
	}
	if(new<0) new = protocols;
	protocols++;
	protos[new].handler = handler;
	protos[new].recvd = 0;
	protos[new].sent = 0;
	protos[new].type = type;
	return(new);
}


int net_release(int type)
{
int i;
	if(!protocols) return(EPROTUSED);
	if(type == ET_UNUSED) return(EPROTUSED);
	for(i=0; i<MAXPROTOCOLS; i++)
		if(protos[i].type == type) break;
	if(i==MAXPROTOCOLS) return(EPROTUSED);
	protocols--;
	if(!protocols)
	{
		*RCP_RAP = CSR0;
		*RCP_RDP = CSR0_STOP;
		ihandler = NULL;
	}
	protos[i].type = ET_UNUSED;
	protos[i].handler = NULL;
	return(protocols);
}


int net_send(int len, char *buf)
{
register int *rdp;
register TMD *tmd;
register long timeout;

	if(!buf || !len || (buf < (char *)PPKT) || (buf > (char *)PPOOL)) return(EPARAM);
	rdp = RCP_RDP;
	pkt_sent = 0;
	if(len < 60) len = 60;
	if(len > (int)sizeof(PACKET)) return(EPKTLEN);
	*(rdp+1) = CSR0;
	if(!(*rdp & CSR0_TXON)) return(EINIT);
	tmd = (TMD *)tmd_sent;
	if((tmd->tmd1 & OWN) == OWN_CHIP) return(EINIT);
	tmd->ladr = (u_short)(buf);				/* point to packet to send */
	tmd->tmd2 = -len;
	tmd->tmd3 = 0;
	tmd->tmd1 = (STP | ENP | OWN_CHIP);	/* start/end of packet */
	timeout = clock() + 3*TIMEOUT;
	while(!pkt_sent && !tmd->tmd3 && clock() < timeout);   /* wait till packet sent */
	if(clock() > timeout) return(ETIMEOUT);
	if(tmd->tmd3 || pkt_sent < 0) return(ECOLLISION);
	return(len);
}


int	net_getadr(int len, char *buf)
{
register LNCMEM *mem;

	if(len>=(int)sizeof(HADDR))
	{
		mem = (LNCMEM *)RCP_MEMBOT;
		buf[0] = mem->init.haddr[1];
		buf[1] = mem->init.haddr[0];

		buf[2] = mem->init.haddr[3];
		buf[3] = mem->init.haddr[2];

		buf[4] = mem->init.haddr[5];
		buf[5] = mem->init.haddr[4];
		return((int)sizeof(HADDR));
	}
	return(0);
}


long net_resets(void)
{
int i;
	for(i=0;i<MAXPROTOCOLS;i++)  /* init protocol table */
  	{
   		protos[i].type = ET_UNUSED;
   		protos[i].handler = NULL;
		protos[i].recvd = 0;
		protos[i].sent = 0;
   	}
   	protocols = 0;
	p_free = p_init(MAXPKT,PPOOL,PPKT);		/* init free packets */
	freecnt = MAXPKT;
	for(i=0; i< RECVBUFFS; i++)
	{
		pkt_ring[i] = ap_getpkt(ET_FREE,p_free);	/* retrieve packet for receive */
		if(!pkt_ring[i]) return(EINIT);
		freecnt--;
	}
	return(lnc_reset());
}

int net_reset(void)
{
	Supexec(net_resets);
	if( !ihandler) 
		return(EINIT);
	else
		return(0);
}


PKTBUF *net_pktalloc(protocol)
u_short protocol;
{
register PKTBUF *pkt;
	if(!p_free) net_reset();
	if(freecnt <= 1) return(NULL);
	pkt = ap_getpkt(protocol,p_free);
	if(pkt)
		freecnt--;
	return(pkt);
}


int net_pktfree(p_pkt)
PKTBUF *p_pkt;
{
	if(!p_free || !p_pkt || (p_pkt < PPKT) || (p_pkt > (PKTBUF *)PPOOL))
	{
#ifdef DEBUGPKT
		Cconws("Packet out of band\r\n");
#endif
		return(FALSE);
	}
	if(((long)p_pkt - (long)PPKT) % sizeof(PKTBUF))
	{
#ifdef DEBUGPKT
		Cconws("Packet misaligned\r\n");
#endif
		return(FALSE);
	}
	if(ap_putpkt(p_free,p_pkt))
	{
		freecnt++;
		return(TRUE);
	}
	return(FALSE);
}

/*******************************************************************/
/*******************************************************************/

#ifdef DEBUG
char str[40];
#endif


void lnc_intr(void)
{
register int 		csr0;
register int		type;
register int		i;

register int 		*rdp;
register et_stat	*et_stat;
register TMD		*md;
register PKTBUF		*pkt;
	
	rdp = RCP_RDP;		/* register data port */
	*(rdp+1) = CSR0;
	csr0 = *rdp;
	if(!(csr0 & CSR0_INTR)) return;
	et_stat = &stat;
#ifdef DEBUGINTR
	if(protocols>0)
	{
		strcpy(str,"pkt intr: ");
		itoa(csr0,str+strlen(str),16);
		strcat(str,"\r\n");
		Cconws(str);
	}
#endif
	
	et_stat->st_err = csr0;
	et_stat->st_intr++;
	if(csr0 & CSR0_IDON)
		*(rdp) = CSR0_IDON;
	else if(csr0 & CSR0_RINT)
	{
	  do
	  {
		(RMD *)md  = &PRMD[pkt_recv];
		*rdp = CSR0_RINT;
		if(!(((RMD *)md)->rmd1 & ERR))	/* packet ok */
		{
/*			pkt = pkt_ring[pkt_recv]; */
			pkt = (PKTBUF *)((long)RCP_MEMBOT+(((RMD *)md)->ladr));
			type = pkt->et_type;
			et_stat->st_received++;
			if(protocols > 0)
			{
#ifdef DEBUG
				itoa(type,str,16);
				Cconws(str);
				Cconws(" from ");
				ltoa((long)pkt,str,16);
				Cconws(str);
				Cconws("\r\n");
#endif
				for(i=0;i<MAXPROTOCOLS; i++)
				{
					if(protos[i].type == type && freecnt > 0)
					{					/* must have one packet free */
						if(protos[i].handler && 
						   protos[i].handler(((RMD *)md)->mcnt,(char *)pkt))
						{	
									/* retrieve one packet for receive */
							pkt_ring[pkt_recv] = ap_getpkt(ET_FREE,p_free);
							freecnt--;
											/* set new receive packet */
							((RMD *)md)->ladr = (u_short)(pkt_ring[pkt_recv]);
							et_stat->st_got++;
							protos[i].recvd++;
						}
						break;
					}
				}
			}
		}
		else
		{
		  	if(((RMD *)md)->rmd1 & CRC)
				et_stat->st_crc++;
			lnc_reset();
			return;
		}
		((RMD *)md)->mcnt = 0;			/* free packet */
		((RMD *)md)->rmd1 = OWN_CHIP;	/* give packet back to lance */
		pkt_recv++;
		if(pkt_recv >= RECVBUFFS) pkt_recv = 0;
	  } while((PRMD[pkt_recv].rmd1 & OWN) == OWN_HOST);
	}
	else if(csr0 & CSR0_TINT)
	{
		md = (TMD *)tmd_sent;
		if((md->tmd1 & OWN) == OWN_HOST)  /* packet given back to host */
		{
			if((md->tmd1) & ERR)
			{
				pkt_sent = -1;
				et_stat->st_xmiterr++;
			}
			else
			{
				pkt_sent = 1;
				et_stat->st_sent++;
				if(md->tmd1 & (MORE | ONE | DEF))
					et_stat->st_collision++;
			}
		}
		else
			et_stat->st_xmiterr++;
		*rdp = CSR0_TINT;	/* clear interrupt bit */
	}
	else if(csr0 & CSR0_ERR)
	{
		et_stat->st_missed++;
		*(rdp+1) = 0;
		*rdp = CSR0_CERR | CSR0_MERR | CSR0_BABL | CSR0_MISS;
	}
	*(rdp+1) = CSR0;
	*rdp = CSR0_INEA;
}


int lnc_reset(void)
{
register LNCMEM *mem;
register TMD	*md;
register u_long i;
register int *rdp;

	rdp = RCP_RDP;
	*(rdp+1) = CSR0;
	*rdp = CSR0_STOP;
		/* init data structures */
		
	mem = (LNCMEM *)RCP_MEMBOT;
	mem->init.mode = 0;		/* mode word */
	
	mem->init.haddr[5] = my_haddr[4];
	mem->init.haddr[4] = my_haddr[5];
	mem->init.haddr[3] = my_haddr[2];
	mem->init.haddr[2] = my_haddr[3];
	mem->init.haddr[1] = my_haddr[0];
	mem->init.haddr[0] = my_haddr[1];
	
	mem->init.laf[0] = 0;	/* logical adress filter */
	mem->init.laf[1] = 0;	/* logical adress filter */

	mem->init.rdrp.drp_lo = (u_short)offsetof(LNCMEM,rmd[0]);
/*	((u_long)&(PRMD[0]));	*/ /* receive ring descr. pointer */
	mem->init.rdrp.drp_hi = 0;
	mem->init.rdrp.len = RECVRLEN;
	
	mem->init.tdrp.drp_lo = (u_short)offsetof(LNCMEM,tmd);
/*	((u_long)&PTMD);	*/	/* transmit ring descr. pointer */
	mem->init.tdrp.drp_hi = 0;
	mem->init.tdrp.len = XMITRLEN;

	md = &mem->tmd;
	md->ladr = 0;							/* dont know adress of packet to send */
	md->tmd1 = OWN_HOST;
	md->tmd2 = 0;							/* zero pkt size */
	md->tmd3 = 0;
	for(i=0; i< RECVBUFFS; i++)
	{
		(RMD *)md = mem->rmd+i;
		((RMD *)md)->ladr = (u_short)((char *)pkt_ring[i]);
		((RMD *)md)->rmd1 = OWN_CHIP;
		((RMD *)md)->rmd2 = -MAXPKTLEN;			/* 2's complement (maximum size) */
		((RMD *)md)->mcnt = 0;
	}
	tmd_sent = &mem->tmd;
	pkt_recv = 0;
	*(rdp+1) = CSR3;
	*rdp = CSR3_BSWP;
	*(rdp+1) = CSR2;
	*rdp = 0;
	*(rdp+1) = CSR1;
	*rdp = 0;
	*(rdp+1) = CSR0;
	*rdp = CSR0_STRT | CSR0_INIT;
	i = TIMEOUTCNT * 100;
	do
	{
		if(*rdp & CSR0_IDON) break;
	}while(--i > 0);
	if(i<=0 || (*rdp & CSR0_ERR))
	{
		*rdp = CSR0_STOP;
		ihandler = NULL;
		return(EINIT);
	}
	ihandler = lnc_intr;
	*rdp = CSR0_IDON;		/* clear idon-bit */
	*rdp = CSR0_INEA;		/* enable interrupts */
	return(0);
}

main()
{
COOKIE *cookie;
void (*call)(void);

	Cconws("\r\nPacket driver V1.1 for RieblCard Plus ");
   	cookie = get_cookie(PKTCOOKIE);
	if(cookie)
	{
		(long)call = ((long *)cookie->val)[NETRESET];
		call();		/* reset network */
		Cconws("\r\nexisting driver reset\r\n");
		return(0);
	}
	memok = 0;
	old_berr = Setexc(2,(void (*)())(-1));
	cookie = get_cookie(MACHINECOOKIE);
	if(!cookie || ((cookie->val >> 16) != 2))
		Setexc(2,berr_catch);
	else
		Setexc(2,berr_tt_catch);
    *RCP_MEMBOT=0xaa;
    Setexc(2,old_berr);
	if(memok)
	{
		Cconws("\r\nEthernet Card NOT found\r\n\a");
		return(0);
	}
   	if(!read_inf())
   	{
   		Cconws("INETCUST not installed !!\r\n");
   	}
   	else
   	{
#ifdef DEBUGFREECNT
   		Cconws("freecnt at 0x");
   		Cconws(ltoa((long)&freecnt,(char *)new_stack,16));
   		Cconws("\r\n\a\a");
#endif
		ihandler = NULL;
		Supexec(lnc_install);
		if(net_reset() < 0)
   		{
   		    Setexc(LANCEIVEC, (void (*)())old_intr);
   			Cconws("Cannot init Ethernet Card\r\n\a");
   			return(0);
   		}
		add_cookie(PKTCOOKIE,(long)ext_tab);
		Cconws("installed\r\n(c) hw,pm fortec 1991,1992\r\n");
		Ptermres(_PgmSize,0);
	}
	return(0);
}