/********************************************************************/
/*																	*/
/*	Packet driver for D-LINK DE600 ethernet controller				*/
/*																	*/
/*	Copyleft by P. Mayer, 1992 fortec - TU-Vienna IAEE				*/
/*	All rights reserved												*/
/*																	*/
/*	(Pure-C)														*/
/********************************************************************/

#define LOCKMEM  0x1
#define LOCKALLO 0x2
#define LOCKFREE 0x4
#define LOCKMAIN 0x8
#define LOCKRES  0x10
#define LOCKREC  0x20
#define LOCKNET  0x40

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <tos.h>
#include <time.h>
#include "nicmem.h"
#include "nicdrv.h"
#include "pktqueue.h"
#include "pktdrv.h"
#include "cookie.h"
#include "inetcust.h"

#define noDEBUG
#define noDEBUGINTR
#define noDEBUGPKT

#define Bconws(x) dpy = &x;while(*dpy)(Bconout(2,*dpy++))
char str[80];
char *dpy;

PKTBUF PPKT[MAXPKT];

PKTPOOL PPOOL;

extern char locked200;
int  retval;
int	 term = 0;
int good = 0;
et_hdr		header;
int mode;

void (interrupt *ihandler)(void) = NULL;
extern void lance_interrupt(void);
extern void lance_200interrupt(void);
extern long *old200;

int resetNIC(void);  /* reset lance, init datastructures  */

PKTPOOL			*p_free;  				/* queue of free packets */
u_short			freecnt;  				/* number of free packets */

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 */

/*************************************************************
  test for de600 ethernetadapter interface on cartridge port
 *************************************************************/

#define NIC_BASE 0xfa0000L

typedef unsigned char byte;

byte CurTxPage, Mode_RxPg;
int RxPktLen, RxStartAdd, TxStartAdd, CurRxPage, our_type;
byte our_address[6];
int last, LastTxStartAdd;

#define WRITE		(0x00 << 1)
#define READ		(0x01 << 1)
#define STATUS		(0x02 << 1)
#define COMMAND		(0x03 << 1)
#define NUL_CMD 	(0x0c << 1)
#define RX_LEN		(0x05 << 1)
#define TX_ADR		(0x06 << 1)
#define RW_ADR		(0x07 << 1)

#define WRITE_STROBE	(0x08 << 1)

#define RXEN		(0x08)
#define TXEN		(0x04)
#define LOOPBACK	(0x0c)
#define RX_NONE		(0x00)
#define RX_ALL		(0x01)
#define RX_BP		(0x02)
#define RX_MBP		(0x03)

#define RESET		(0x80)
#define STOP_RESET	(0x00)

#define RXBUSY		0x80
#define GOOD		0x40
#define RESET_FLAG	0x20
#define T16		    0x10
#define TXBUSY		0x08

#define BFRSIZ		2048
#define RUNT		60
#define EADDR_LEN	6
#define HA13		0x20
#define PAGE_0		0x00
#define PAGE_1		0x08
#define PAGE_2		0x10
#define PAGE_3		0x18

#define delay()     /* no delay needed for ST */
#define pause()

/* defines for access to DE600 */

#define WRITE_SUB_FAST(value, cmd) \
	x = *(int*)(base + (int)(((((int)value) << 5) & 0x1e0) + cmd)); \
	x = *(int*)(base + (int)(last = (((((int)value) << 1) & 0x1e0) + ((cmd) ^ WRITE_STROBE)))); \

#define WRITE_SUB(value, cmd) \
	x = *(int*)(base + (int)(((((int)value) << 5) & 0x1e0) + cmd | WRITE_STROBE)); \
	delay(); \
	x = *(int*)(base + (int)(((((int)value) << 5) & 0x1e0) + cmd)); \
	delay(); \
	x = *(int*)(base + (int)(((((int)value) << 1) & 0x1e0) + cmd)); \
	delay(); \
	x = *(int*)(base + (int)(last = ((((int)value) << 1) & 0x1e0) + cmd | WRITE_STROBE)); \

#define WRITE_SUPER_FAST(value, cmd) \
	x = *(int*)(base + (int)(((((int)value) << 5)) + cmd)); \
	x = *(int*)(base + (int)((((((int)value) << 1) & 0x1e0) + ((cmd) ^ WRITE_STROBE)))); \

#define READ_SUB(cmd) \
	x = *(int*)(base + (int)(cmd | WRITE_STROBE)); \
	delay(); \
	x = *(int*)(base + (int)(cmd)); \
	delay(); \
	y = (*(int*)(base + (int)(cmd)) & 0xf0) ^ 0x80; \
 	x = *(int*)(base + (cmd | WRITE_STROBE)); \
	delay(); \
 	y = (y >> 4) | ((*(int*)(base + (int)(last = (cmd | WRITE_STROBE))) & 0xf0) ^ 0x80); \

#define READ_STAT1 \
	x = *(int*)(base + (int)STATUS); \
	pause(); \
	y = (*(int*)(base + (int)(STATUS))) ^ 0x80; \
	x = *(int*)(base + (int)NUL_CMD); \

#define READ_STAT_FAST \
	x = *(int*)(base + (int)STATUS); \
	pause(); \
	y = (*(int*)(base + (int)(STATUS))) ^ 0x80; \

#define READ_STAT(cmd) \
	x = *(int*)(base + (int)(cmd | WRITE_STROBE)); \
	delay(); \
	x = *(int*)(base + (int)(cmd)); \
	delay(); \
	y = (*(int*)(base + (int)(cmd)) & 0xf0) ^ 0x80; \
    x = *(int*)(base + (int)(NUL_CMD + WRITE_STROBE)); \
    delay(); \
    x = *(int*)(base + (int)(last = NUL_CMD)); \
    delay(); \
         
#define READ_SUB_FAST(cmd) \
	x = *(int*)(base + (int)(cmd)); \
	x = (*(int*)(base + (int)(cmd))) & 0xf0; \
    x >>= 4; \
   	y = *(int*)(base + (int)(cmd + WRITE_STROBE)); \
 	y = (((*(int*)(base + (cmd | WRITE_STROBE)) & 0xf0)) | x); \
    y ^= 0x88; \

#define READ_SUPER_FAST(cmd) \
/*	x = *(int*)(base + (int)(cmd));*/ \
	x = (*(int*)(base + (int)(cmd))) & 0xf0; \
    x >>= 4; \
/*   	y = *(int*)(base + (int)(cmd + WRITE_STROBE));*/ \
 	y = (((*(int*)(base + (cmd | WRITE_STROBE)) & 0xf0)) | x); \
    y ^= 0x88; \

#define READ_HYPER_FAST(cmd) \
	x = (*(int*)(base + (int)(cmd))) & 0xf0; \
    x >>= 4; \
 	y = (((*(int*)(base + (cmd | WRITE_STROBE)) & 0xf0)) | x); \
    y ^= 0x88; \

int copyEAD()
{
  register byte *base = (byte*)NIC_BASE;
  register int x,y;
  int i;

  WRITE_SUB_FAST(0,RW_ADR);
  WRITE_SUB_FAST(HA13,RW_ADR);

  for(i = 0; i < EADDR_LEN;i++) 
  {
    READ_SUB_FAST(READ);
    our_address[i] = y;
  }
  if(   our_address[0] == 0x00 
     && our_address[1] == 0xde 
     && our_address[2] == 0x15)
  {
    our_address[1] = 0x80;
    our_address[2] = 0xc8;

    /* our_address[3] &= 0x0f;
       our_address[3] |= 0x70; */
  }
  else
  {
    return -1;
  }

  WRITE_SUB_FAST(0,RW_ADR);
  WRITE_SUB_FAST(HA13,RW_ADR);

  for(i = 0;i < EADDR_LEN;i++) 
  {
    WRITE_SUB_FAST(our_address[i],WRITE);
  }
  x++;
  return 0;
}

int Check_DE600()
{
  register byte *base = (byte*)NIC_BASE;
  register int x,y;

  x = *(int*)(base + NUL_CMD);
  delay();
  WRITE_SUB(RESET,COMMAND);
  delay();
  WRITE_SUB(STOP_RESET,COMMAND);
  delay();
  READ_STAT(STATUS);
  x++; 	
  if(y == 0) return 0;
  else return -1;
}

int send_packet(byte *p_pkt, int pkt_len)
{
  register byte *base = (byte*)NIC_BASE;
  register int x,y,TxStartAdd,val;
  int loop_cnt;
  void lnc_check(void);

  if(pkt_len < RUNT) pkt_len = RUNT;
  pkt_len = (pkt_len+1) & ~1;  /* make sure min. size */

  CurTxPage ^= 0x8;		/* next free buffer */

  TxStartAdd = (BFRSIZ - pkt_len) | ((int)CurTxPage << 8);
  WRITE_SUB_FAST(TxStartAdd & 0xff, RW_ADR);  
  WRITE_SUB_FAST(TxStartAdd >> 8, RW_ADR);  

  while(pkt_len--)
  {
    val = *p_pkt++;
    WRITE_SUPER_FAST(val,WRITE);
  }
  last = ( ( ( (int)(*(p_pkt-1)) << 1) & 0x1e0) + ((WRITE) ^ WRITE_STROBE));

  for(loop_cnt = 0x4000;loop_cnt--;)
  {
    y = (*(int*)(base + (int)last)) ^ 0x80;
    if((y & TXBUSY) == 0) break;
  }

  LastTxStartAdd = TxStartAdd;
  WRITE_SUB_FAST(TxStartAdd & 0xff, TX_ADR);
  WRITE_SUB_FAST(TxStartAdd >> 8, TX_ADR);
  WRITE_SUB_FAST(Mode_RxPg | RXEN, COMMAND);
  WRITE_SUB_FAST(Mode_RxPg | RXEN | TXEN, COMMAND);
  mode = TXEN | RXEN;

  x++;
  return pkt_len;
}

int test_mem()
{
  register byte *base = (byte*)NIC_BASE;
  register int x,y,i;

  locked200 |= LOCKMEM;
  
  WRITE_SUB_FAST(0,RW_ADR);
  WRITE_SUB_FAST(0,RW_ADR);
  for(i = 0;i < 0x800;i++)
  {
    WRITE_SUB_FAST(i & 0xff,WRITE);
  }
  WRITE_SUB_FAST(0,RW_ADR);
  WRITE_SUB_FAST(0,RW_ADR);
  for(i=0;i<0x800;i++)
  {
    READ_SUB_FAST(READ);
    if((i & 0xff) != y)
    {
      Cconws("\r\nDE600 memory test error!!\r\n");
      locked200 &= ~LOCKMEM;
      return -1;
    }
  }
  x++;
  locked200 &= ~LOCKMEM;
  return 0;
}

int resetNIC(void)
{
  int copyEAD(void);
  int Check_DE600(void);
  int test_mem(void);
  void enable_rcv(void);

  CurRxPage = 0x20;
  Mode_RxPg = RX_BP | 0x20;
  CurTxPage = 0;

  if(Check_DE600()) return -1;
  if(test_mem()) return -1;
  if(copyEAD()) return -1;
  enable_rcv();
  return 0; 
}

void stopNIC(void)
{
  register byte *base = (byte*)NIC_BASE;
  register int x;

  WRITE_SUB_FAST(Mode_RxPg, COMMAND);
  x++; 
}

byte NICstatus()
{
  register byte *base = (byte*)NIC_BASE;
  register int x,y;

  READ_STAT1;
  x++;
  return y;
}

void enable_rcv()
{
  register byte *base = (byte*)NIC_BASE;
  register int x;

  WRITE_SUB_FAST(Mode_RxPg, COMMAND);
  WRITE_SUB_FAST(Mode_RxPg | RXEN, COMMAND);
  mode = RXEN;
  x++;
}

int read_inf(void)
{
  COOKIE *cookie;

  cookie = get_cookie(INETCUSTCOOKIE);
  if(!cookie || !cookie->val) return(0);
  return(1);
}

int net_info(int len, char *buf)
{

  locked200 |= LOCKNET;

  if(!buf)
  {
    Mode_RxPg = (Mode_RxPg & 0xfc) | len;
    locked200 &= ~LOCKNET;
    return(0);
  }
  if(buf == (char *)1L) 
  {
    resetNIC();
    locked200 &= ~LOCKNET;
    return(0);
  }
  stat.st_free = freecnt;
  memcpy(buf,(char *)&stat,(size_t)len<sizeof(stat)?len:(int)sizeof(stat));

  locked200 &= ~LOCKNET;
  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;
  locked200 |= LOCKNET;
  if(!protocols)
  {
    if(net_reset() < 0)
    {
      locked200 &= ~LOCKNET;
      return(EINIT);
    }
  }
  for(i = 0; i < MAXPROTOCOLS; i++)
  {
    /* protocol already used */
    if(protos[i].type == type)
    {
      locked200 &= ~LOCKNET;
      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;
  locked200 &= ~LOCKNET;
  return(new);
}

int net_release(int type)
{
  int i;
  void stopNIC(void);

  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);
  }
  locked200 |= LOCKNET;
  protocols--;
  if(!protocols)
  {
    stopNIC();
  }
  protos[i].type = ET_UNUSED;
  protos[i].handler = NULL;
  locked200 &= ~LOCKNET;
  return(protocols);
}

int net_send(int len, char *buf)
{
  register byte *base = (byte*)NIC_BASE;
  register long timeout;
  register int y;

  void lnc_check(void);
  byte NICstatus(void);
  void enable_rcv(void);
#ifdef DEBI
  printf("netsend %lx length %d good = %d mode = %x\n",buf,len,good,mode);
#endif

  if(!buf || !len || (buf < (char *)PPKT) || (buf > (char *)&PPOOL)) return(EPARAM);
  if(len < 60) len = 60;
  if(len > (int)sizeof(PACKET)) return(EPKTLEN);

  locked200 |= LOCKNET;
  timeout = clock() + 3*TIMEOUT;
  len = send_packet((byte*)buf,len);

#ifdef DEBI	
  printf("send done\n");
#endif
  locked200 &= ~LOCKNET;
  return(0);
}

int	net_getadr(int len, char *buf)
{
  if(len >= (int)sizeof(HADDR))
  {
    buf[0] = our_address[0];
    buf[1] = our_address[1];
    buf[2] = our_address[2];
    buf[3] = our_address[3];
    buf[4] = our_address[4];
    buf[5] = our_address[5];
    return((int)sizeof(HADDR));
  }
  return(0);
}

int net_reset(void)
{
  int i,tmp;

  locked200 |= LOCKRES;
  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;

  tmp = resetNIC();
  locked200 &= ~LOCKRES;
  return tmp;
}

PKTBUF *net_pktalloc(protocol)
u_short protocol;
{
  PKTBUF * tmp;

  if(!p_free) net_reset();
  
  locked200 |= LOCKALLO;
  if(freecnt <= 1)
  {
Bconws("DLINKDRV: No free Packet\r");
    locked200 &= ~LOCKALLO;
    return(NULL);
  }
  freecnt--;
  tmp = ap_getpkt(protocol,p_free);
#ifdef DEBUGPKT
  sprintf(str,"netpktalloc at %lx\r",tmp);
  Bconws(str);
#endif
  locked200 &= ~LOCKALLO;
  return tmp;
}

int net_pktfree(p_pkt)
PKTBUF *p_pkt;
{
  locked200 |= LOCKFREE;
#ifdef DEBUGPKT
  sprintf(str,"netpktfree at %lx\r",p_pkt);
  Bconws(str);
#endif
  if(!p_free || !p_pkt || (p_pkt < PPKT) || (p_pkt > &PPKT[MAXPKT-1]))
  {
    Bconws("DLINKDRV: Packet out of bounds\r");
    locked200 &= ~LOCKFREE;
    return(FALSE);
  }
  if(((long)p_pkt - (long)PPKT) % sizeof(PKTBUF))
  {
    Bconws("DLINKDRV: Packet misaligned\r");
    locked200 &= ~LOCKFREE;
    return(FALSE);
  }
  if(ap_putpkt(p_free,p_pkt))
  {
    freecnt++;
    locked200 &= ~LOCKFREE;
    return(TRUE);
  }
  Bconws("DLINKDRV: Packet not freed\r");
  locked200 &= ~LOCKFREE;
  return(FALSE);
}

/*******************************************************************/
/* read D-LINK function                                            */
/*******************************************************************/

void lnc_check(void)
{
  register byte *base = (byte*)NIC_BASE;
  register int x,y,i,ii,NICstate;
  int		type, RxPktLen, loop;

  et_stat	*et_stat;
  PKTBUF		*pkt;
  byte * pb;

  locked200 |= LOCKREC;

  for(loop = 0;loop < 1;loop++)
  {
	x = *(int*)(base + (int)STATUS);
	y = *(int*)(base + (int)STATUS);
	x = *(int*)(base + (int)NUL_CMD);
    NICstate = y; 

    if(NICstate & GOOD)
    {
      READ_SUPER_FAST(RX_LEN);
      RxPktLen = y;
      READ_SUPER_FAST(RX_LEN);
      RxPktLen += (y << 8);

      Mode_RxPg ^= 0x10;
      WRITE_SUB_FAST(Mode_RxPg, COMMAND);
      WRITE_SUB_FAST(Mode_RxPg | RXEN, COMMAND);
      mode = RXEN;
      WRITE_SUB_FAST(0,RW_ADR);
      WRITE_SUB_FAST(CurRxPage >> 1,RW_ADR);
      CurRxPage ^= 0x10;
      RxPktLen -= 4;
      et_stat = &stat;
      et_stat->st_err = NICstate;
      et_stat->st_intr++;

      pb = (byte*)&header;
      for(i=0; i < 14;i++)
      {
        READ_SUPER_FAST(READ);
        *pb++ = y;
      }
      type = header.et_type;
      et_stat->st_received++;

      if(header.et_dest[0] != 0xff)
      {
          for(i = 0;i < MAXPROTOCOLS; i++)
          {
            if(protos[i].type == type)
            {
              /* must have one packet free */
              if((pkt = ap_getpkt(type,p_free)) != 0)
              {
                freecnt--;
                memcpy((char*)pkt,(char*)&header,sizeof(et_hdr));
                pb = pkt->et_data;
                for(ii=(int)sizeof(et_hdr); ii < RxPktLen; ii++)
                {
                  READ_SUPER_FAST(READ);
                  *pb++ = y;
                }
                if(protos[i].handler)
                { 
                 if(protos[i].handler(RxPktLen,(char *)pkt))
                 {
                  et_stat->st_got++;
                  protos[i].recvd++;
                 }
                }
                else
                {
                /* free unused packet */
                  if(ap_putpkt(p_free,pkt)) freecnt++;
                  break;
                }
              }
            }
          }
      }
    }
    else
    {
      WRITE_SUB_FAST(Mode_RxPg, COMMAND);
      WRITE_SUB_FAST(Mode_RxPg | RXEN, COMMAND);
      if(loop == 0) break;
    }
    
    if(NICstate & T16)
    {
      et_stat->st_xmiterr++;
    }
  }
  x++;
  locked200 &= ~LOCKREC;
}

int main()
{
  COOKIE *cookie;
  int (*call)(void);
  long lance_install(void);

  Cconws("\r\nPacket driver V1.1 for Dlink DE600 ");
  cookie = get_cookie(PKTCOOKIE);
  if(cookie)
  {
    (long)call = ((long *)cookie->val)[NETRESET];
    if(call() == 0)  /* reset network */
      Cconws("\r\nexisting driver reset\r\n");
    else 
      Cconws("\r\ncould not reset existing driver\r\n");
    return 0;
  }
  if(!read_inf())
  {
    Cconws("INETCUST not installed !!\r\n");
    return 1;
  }
  else
  {
    if(net_reset() < 0)
    {
      Cconws("\r\nno DE600 detected !!\r\n");
      return 1;
    }
    locked200 |= LOCKMAIN;
    if((int)Supexec(lance_install) < 0)
    {
      Cconws("\r\ncould not install handler !!\n");
      return 1;
    }
    add_cookie(PKTCOOKIE,(long)ext_tab);
    Cconws("installed\r\n(c) pm FORTec 1992\r\n");
    locked200 &= ~LOCKMAIN;
    Ptermres(_PgmSize,0);
  }
  return 0;
}

long lance_install()
{
  void lnc_check(void);
/*  long **vblqueue = *(long***)0x456L;
  int vblnum = *(int*)0x454L;
  int i;

  for(i=1; i< vblnum;i++) if (*(vblqueue+i) == 0L) break;

  if(i< vblnum) 
  {
    *(vblqueue+i) = (long*)lance_interrupt;
    ihandler = (void (interrupt *)())lnc_check;
    return 0;
  }
  else return -1;
*/
old200 = (long*)Setexc(69,lance_200interrupt);
ihandler = (void (interrupt *)())lnc_check;
/*  old200 = *(long*)0x400;
  *(long*)0x400 = (long)lance_200interrupt;*/
  return 0;
}
