/******************************************************************************
 *
 *                                   Lan_d
 *
 * This module contains the ethernet IP lan driver of CMON. It handles 4 layers
 * of communication:
 *
 * 1. The ethernet level. This is the lowest communication level. It programs
 *    the 3com lan controller. It includes functions for packet
 *    transmission and recption and initialization of the 3com
 * 2. The IP (Internet Protocol) level. This includes routines for handling
 *    IP incoming and outgoing IP datagrams.
 * 3. the UDP (User Datagram Protocol) level. This includes routines for
 *    handling incoming and outgoing UDP datagrams.
 * 4. The application level (monitor-debugger protocol). The protocol B 
 *    level is not implemented here since it's not unique for the lan
 *    communication mode. But there is an intermediate layer between the
 *    UDP and protocol B which includes message sequence number for recovery
 *    purposes. This layer is implemented here.
 *
 * Two additional protocols which are used here:
 *
 * 1. ARP (Address Resolution Protocol) and RARP(Reverese ARP). This may
 *    be used in level 2 instead of IP.
 * 2. ICMP (Interenet Control Message Protocol). This may be used in level
 *    3 instead of UDP.
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Eyal       | Written                                        |
 *   +----------+------------+------------------------------------------------+
 *
 *****************************************************************************/


#include "cc_def.h"
#include "lan_d.h"
#include "io.h"

#ifdef LAN_EXIST

/* UDP structure */

#define UDPHLEN 8		/* UDP header length */
#define MAXUDPDATA 1472

typedef struct {
        unsigned short srcprt;  /* source port */
        unsigned short dstprt;  /* destination port */
        unsigned short length;  /* length */
        unsigned short cksum;   /* checksum */
        unsigned char data[MAXUDPDATA]; /* raw data */
}  UDP;

/* ICMP structure */

#define ICRPECH 0               /* Echo Reply */
#define ICRQECH 8               /* Echo request */

typedef struct {
        char ic_typ;            /* ICMP message type */
        char ic_code;           /* Code */
        short ic_cksum;         /* Checksum of header */
        short ic_id;            /* for echo type, a message id */
        short ic_seq;           /* for echo type, a seq. num */
        char ic_data[1];        /* data area of ICMP */
} ICMP;

/* IP datagram structure */

#define IPHLEN		20	/* IP header length */
#define IPRO_ICMP       1       /* Protocol type for ICMP */
#define IPRO_TCP        6       /* Protocol type for ICMP */
#define IPRO_UDP        17      /* Protocol type for UDP packets */

typedef struct {
    char i_verlen;              /* IP version */
    char i_svctyp;              /* Service type */
    short i_paclen;             /* Packet length in bytes */
    short i_id;                 /* datagram ID for fragmentation */
    short i_fragoff;            /* Fragment offset */
    char i_tim2liv;             /* time to live in gateway hops */
    char i_proto;               /* IP protocol */
    short i_cksum;              /* 1's compl. of sum of shorts (header) */
    long i_src;                 /* IP address of source */
    long i_dest;                /* IP address of destination */
    union {
	UDP udp;
	ICMP icmp;
    } data;			/* IP datagram data area */
}  IP;

/* ARP structure */

#define AR_REQ  1               /* arp request to resolve address */
#define AR_RPLY 2               /* reply to a resolve request */
#define AR_RREQ 3               /* reverse ARP request */
#define AR_RRPLY 4               /* reply to a RARP */

typedef struct {
	short ar_hrd;
	short ar_prot;
	char ar_hlen;
	char ar_plen;
	short ar_op;
	char ar_sha[6];
	long ar_spa;
	char ar_tha[6];
	long ar_tpa;
} ARP;

/* Ethernet packet structure */

#define EP_IP   0x0800          /* DARPA Internet protocol */
#define EP_ARP  0x0806          /* ARP packet */
#define EP_RARP 0x8035          /* reverse ARP packet */
#define MINTRANSMIT 60          /* minimum Ethernet transmit size */
#define EPHLEN   14              /* Ethernet header length */

typedef struct {
        unsigned char rstatus;  /* Received packet status */
        unsigned char npp;      /* Next Packet Pointer */
        unsigned short int rbc; /* Received Byte Count */
        char e_dest[6];         /* Destination address */
        char e_src[6];          /* Source address */
        short int e_ptype;      /* Ethernet packet type */
	union {
	    IP ip;
	    ARP arp;
	} data;			/* packet data */
} PACKET;

/* Channel structure. This structure contains all the information about
   a session between a local process to a remote process. Currently
   the local process is the monitor and the remote process is a debugger.
   However future extensions will be able to open several channels (e.g.
   between a user-application and a remote process).
 */

#ifndef public

typedef struct {
    unsigned short local_port;
    unsigned short remote_port;
    long remote_iaddr;
    char remote_eaddr[6];
} CHANNEL;

#endif

public unsigned short monport = MAGIC_PORT;
public CHANNEL dbug_ch =    /* Cmon-debugger channel */
    { 0xffff };
public CHANNEL curr_ch;	    /* Channel for current message */

/* Private functions used */

private get_udp();
private get_ip();
private send_udp();
private send_ip();
private send_rarp();
private process_arp();
private process_icmp();
private resend();
private save_message();
private short cksum();
private init_3com();
private init_nic();
private get_packet();
private overflow();
private get_next();
private head_check();
private send_packet();
private overwrite();

private char local_eaddr[6];	    /* The local ethernet address */
private char brdcast_eaddr[6]	    /* Ethernet network broadcast address */
    = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
private long local_iaddr = 0;	    /* The local internet address */
private PACKET c_packet;	    /* An ethernet packet */
private unsigned char nxtpkt;	    /* Next packet pointer */
private int retry;		    /* Transmit retry count */
private char resend_msg[512];
				    /* Saved ack message */
#define TOTAL_SAVED 3
private last_msg_index=0;
private save_msg[TOTAL_SAVED] [512];
private int seqno = 0;

#define c_ip c_packet.data.ip
#define c_arp c_packet.data.arp
#define c_udp c_packet.data.ip.data.udp
#define c_icmp c_packet.data.ip.data.icmp

#ifndef public

#define ETHERNET 1
#define MONPORT (dbug_ch.local_port)
#define HOSTPORT (dbug_ch.remote_port)
#define MAGIC_PORT (unsigned short) 0xffff
#define CONNECTED (MONPORT != MAGIC_PORT)
#define IS_ACK(message) (message)[3] == '*'
/* SWAP macros are needed since the byte-sex used on the network is
   Big-Endian.
*/
#define SWAP_S(x)  ((((x)<<8) & 0xff00) | (((x)>>8) & 0xff))
#define SWAP_L(x)  (((x)<<24) | (((x)<<8) & 0xff0000) | \
                            (((x)>>8) & 0xff00) | (((x)>>24) & 0xff))

#endif


/*-----------------------------------------------------------------------------
 *				init_lan__
 *
 * This function is used at initialization time to initiate lan communication.
 * It includes initialization of the 3com lan controller and RARP transmission
 * in order to determine the local node Internet adddress.
 *
 *---------------------------------------------------------------------------*/

init_lan__(type)
boolean type;	/* 1 - thick ethernet, 0 - thin ethernet */
{
    init_3com(type);
    send_rarp();
}

/*-----------------------------------------------------------------------------
 *				read_line_from_lan__
 *
 * This function is used to read a message from the lan. It reads UDP's
 * until a UDP arrives with the current MONPORT. In this case the monitor
 * will process the message. Sequence number and CTRL_B are stripped from
 * the message.
 *
 *---------------------------------------------------------------------------*/
unsigned int prev_seqno=0xFFFF;

public void read_line_from_lan__(message)
str message;
{
    int len, n_stripped;
    unsigned int cur_seqno;

    while (TRUE) {
	get_udp();

	    /* Dbug doesn't set the fieald c_udp.srcprt therfore, Don't **
	    ** Update curr_ch.remote_port from c_udp.srcprt             */
	curr_ch.local_port = c_udp.dstprt;
    	curr_ch.remote_port = dbug_ch.remote_port;

	    /* Copy the message, stripping the sequence number and	**
	    ** CTRL-B if it exists.					*/
	if (c_udp.data[4] == CTRL_B)
	    n_stripped = 5;
	else
	    n_stripped = 4;
	len = SWAP_S(c_udp.length) - UDPHLEN - n_stripped;
	bcopy(c_udp.data+n_stripped, message,len);
	message[len] = '\0';

	if (c_udp.dstprt == MONPORT) {
			cur_seqno = *((unsigned int *) &(c_udp.data[0]));
			if (cur_seqno != prev_seqno) {
			    prev_seqno = cur_seqno;
	    		    return;
			}
	}
	else
	    if (CONNECTED) { 
		if(bcmp(&message[3], "CON ", 4) == 0) {
			/* Here its CON command from another debug	**
			** Therefore, get its hostport & return e_con   */
		    get_con_parms(&message[3], &(curr_ch.local_port), 
						&(curr_ch.remote_port));
		    /* Override The CON command local port in case
		       of CON command to already connecected session */
		    curr_ch.local_port = c_udp.dstprt;
		    write_message__(e_con__);
		}
	    }
    }
}

/*-----------------------------------------------------------------------------
 *				get_udp
 *
 * This function is used for UDP reception. It reads IP datagrams in a loop
 * until the IP contents is a UDP datagram. If the IP datagram contents is
 * an ICMP message it calls procees_icmp to process and returns to the loop.
 *
 *---------------------------------------------------------------------------*/

private get_udp()
{
    while (TRUE) {
	get_ip();
	/* Temporary assignment. Since I can't seem to get my INTERNET address
	   through RARP, I'll use this. When the RARP problem is solved remove
	   the following statement.
	*/
	if (local_iaddr == 0)
	    local_iaddr = c_ip.i_dest;

	curr_ch.remote_iaddr = c_ip.i_src;
	switch (c_ip.i_proto) {
	    case(IPRO_ICMP) :
		process_icmp();
		break;
	    case(IPRO_UDP) :
		return;
	}
    }
}

/*-----------------------------------------------------------------------------
 *				get_ip
 *
 * This function is used for IP datagram reception. It reads ethernet packets
 * in a loop until the packet contents is an IP datagram. If the packet
 * contents is an ARP/RARP it call process_arp to process it and returns to
 * the loop.
 *
 *---------------------------------------------------------------------------*/

private get_ip()
{
    while(TRUE) {
	get_packet();
	bcopy(c_packet.e_src, curr_ch.remote_eaddr, 6);
	switch(SWAP_S(c_packet.e_ptype)) {
	    case EP_ARP :
	    case EP_RARP :
		process_arp();
		break;
	    case EP_IP :
		return;
	}
    }
}


/*-----------------------------------------------------------------------------
 *				write_line_to_lan__
 *
 * This function writes a message to lan. It encode a sequence number for
 * the message and prepends a this sequence number and a CTRL_B to the
 * message. In case of an acknowledge message ("*") the complete message
 * is saved for possible later resend. In case of an informative message
 * the message is saved in a cyclic buffer for recovery purposes (i.e.
 * for a case of an LT command arriving from the debugger). When the message
 * is ready send_udp is called to prepare and send a UDP datagram.
 *
 *---------------------------------------------------------------------------*/

public void write_line_to_lan__(message)
str message;
{

    if (NOT CONNECTED) { /* Lior 4/6/91 */
	return;
    }
    if (curr_ch.remote_port == dbug_ch.remote_port && 
	curr_ch.local_port == dbug_ch.local_port) {
	/* If the current channel is the debugger-cmon channel (meaning the
	   message is for the connected debugger) encode a sequence number
	   and save the message for resend/recovery purposes.
	*/
	seqno++;
	bin_to_hex__(seqno, c_udp.data, '4');
	c_udp.data[4] = CTRL_B;
	strcpy(c_udp.data+5, message);

	/* Save message for the resend mechanism */
	strcpy(resend_msg, c_udp.data);

	    /* Informative message - save it for lost packet recovery */
	if (! IS_ACK(message))
	    save_message(c_udp.data);
    }
    else {
	/* The message is not for the connected debugger. Sequence number is
	   0 and no save is done
	*/
	bin_to_hex__(0, c_udp.data, '4');
	c_udp.data[4] = CTRL_B;
	strcpy(c_udp.data+5, message);
    }
	    

    send_udp(strlen(message)+5);
}
/*-----------------------------------------------------------------------------
 *				send_udp
 *
 * This function is used to prepare and send a UDP datagram. It fills the
 * UDP header with the appropriate values and calls send_ip to prepare and
 * send an IP datagram.
 *
 *---------------------------------------------------------------------------*/

private send_udp(data_size)
int data_size;
{
    int udp_size;

    c_udp.srcprt = curr_ch.local_port;
    c_udp.dstprt = curr_ch.remote_port;
/*  c_udp.dstprt = dbug_ch.remote_port; */
    udp_size = UDPHLEN + data_size;
    c_udp.length = SWAP_S(udp_size);
    c_udp.cksum = 0; /* Not implemented */

    send_ip(IPRO_UDP, udp_size);
}


/*-----------------------------------------------------------------------------
 *				send_ip
 *
 * This function is used to prepare and send an IP datagram. It fills the IP
 * header with the appropriate values and calls send_packet to prepare and
 * send an ethernet packet.
 *
 *---------------------------------------------------------------------------*/

private send_ip(type, data_size)
char type;
int data_size;
{
    int ip_size;

    c_ip.i_verlen = 0x45; /* Version 4, header size 5 double words */
    c_ip.i_svctyp = 0;	/* Not used */
    ip_size = data_size + IPHLEN;
    c_ip.i_paclen = SWAP_S(ip_size);
    c_ip.i_id = 0x9999;
    c_ip.i_fragoff = 0;
    c_ip.i_tim2liv = 0xff;
    c_ip.i_proto = type;
    c_ip.i_src = local_iaddr;
    c_ip.i_dest = curr_ch.remote_iaddr;

    /* Clear the checksum field before calculating the checksum. This is
       done because the checksum input includes the checksum field.
    */
    c_ip.i_cksum = 0;
    c_ip.i_cksum = cksum(&c_ip, IPHLEN);

    send_packet(EP_IP, ip_size);
}


/*-----------------------------------------------------------------------------
 *				send_rarp
 *
 * This function is used to preapare and send a RARP message. The purpose
 * of this RARP message is to obtain the INTERNET address of the local node
 * by sending a request to the net. The request specifies the local node
 * ethernet address. A reply is expected to arrive later on and is processes
 * in process_arp.
 *
 *---------------------------------------------------------------------------*/

private send_rarp()
{
    c_arp.ar_hrd = SWAP_S(ETHERNET);
    c_arp.ar_prot = SWAP_S(EP_IP);
    c_arp.ar_hlen = 6;
    c_arp.ar_plen = 4;
    c_arp.ar_op = SWAP_S(AR_RREQ);
    bcopy(local_eaddr, c_arp.ar_sha, 6);
    c_arp.ar_spa = 0;
    bcopy(local_eaddr, c_arp.ar_tha, 6);
    c_arp.ar_tpa = 0;
    /* The RARP destination is all the nodes on the network */
    bcopy(brdcast_eaddr, curr_ch.remote_eaddr, 6);

    send_packet(EP_RARP, sizeof(ARP));
}

/*-----------------------------------------------------------------------------
 *				process_arp
 *
 * This function processes ARP requests and RARP replies from the net.
 * In case of an ARP request a reply is sent specifying the local node
 * etherenet address. In case of a RARP reply the local node INTERNET
 * address is extracted from the message and saved.
 *
 *---------------------------------------------------------------------------*/

private process_arp()
{
    switch (SWAP_S(c_arp.ar_op)) {
	case AR_REQ :
	    if (c_arp.ar_tpa == local_iaddr) {
		/* It's an ARP request for the local node */
		c_arp.ar_op = SWAP_S(AR_RPLY);
		c_arp.ar_tpa = c_arp.ar_spa;
		bcopy(c_arp.ar_sha, c_arp.ar_tha, 6);
		c_arp.ar_spa = local_iaddr;
		bcopy(local_eaddr, c_arp.ar_sha, 6);
	
		send_packet(EP_ARP, sizeof(ARP));
	    }
	    break;

	case AR_RRPLY :
		if (strncmp(c_arp.ar_tha, local_eaddr, 6) == 0)
		    local_iaddr = c_arp.ar_tpa;
	    break;
    }
}

/*-----------------------------------------------------------------------------
 *				process_icmp
 *
 * This function processes ICMP messages. Actually it accepts only one kind
 * of ICMP message - echo request (used for example by the UNIX ping utility).
 * It responds with an echo reply.
 *
 *---------------------------------------------------------------------------*/

private process_icmp()
{
    int icmp_len;

    if (c_icmp.ic_typ == ICRQECH) {
	/* It's an echo request */

	icmp_len = SWAP_S(c_ip.i_paclen) - IPHLEN;

	c_icmp.ic_typ = ICRPECH;
	c_icmp.ic_cksum = 0;
	c_icmp.ic_cksum = cksum(&c_icmp, icmp_len);

	send_ip(IPRO_ICMP, icmp_len);
    }
}

/*-----------------------------------------------------------------------------
 *				resend
 *
 * This function is used for resending of a saved acknowledge message ("*").
 *
 *---------------------------------------------------------------------------*/

private resend()
{
    if (CONNECTED) {
	strcpy(c_udp.data, resend_msg);
	/* curr_ch = dbug_ch; */
	send_udp(strlen(resend_msg));
    }
}


/*-----------------------------------------------------------------------------
 *				save_message
 *
 * This function used to save a monitor message for recovery purposes. The
 * message is saved in a cyclic buffer overriding the oldest message.
 *
 *---------------------------------------------------------------------------*/

private save_message(message)
char *message;
{
    last_msg_index++;
    if (last_msg_index  >=  TOTAL_SAVED)  last_msg_index = 0;
    strcpy(save_msg[last_msg_index], message);
}

/*-----------------------------------------------------------------------------
 *				resend_lost_message
 *
 * This function is used for message recovery. It is activated after an LT
 * monitor command has arrived from the debugger. The required messgae sequence
 * number is searched in the cyclic buffer. If found the entire message is
 * resent. Otherwise the monitor sends a failure message ("?").
 *
 *---------------------------------------------------------------------------*/
public resend_lost_message__(lost, frame)
char *lost;
char *frame;
{
    int i, len;
    boolean found = FALSE;

    for (i = 0; i <  TOTAL_SAVED; i++) {
	/* Compare to message sequence number */
	if (strncmp(lost, save_msg[i], 4) == 0) {
	    found = TRUE;
	    break;
	}
    }

    if (found) {
	len = strlen(save_msg[i]);
	strcpy(c_udp.data, save_msg[i]);
	send_udp(len);
	send_udp(len);
	send_udp(len);
	/*
	 * Update sequence nmber & return the lost packet frame
	 */
	*frame = c_udp.data[5];
	hex_to_bin__(lost, &seqno, 'W');
    }
    else
	write_message__("?");
}

/*-----------------------------------------------------------------------------
 *				cksum
 *
 * This function is used to calculate a checksum value for IP datagrams and
 * ICMP messages. It is formed by treating the data as a sequence of 16 bit
 * integers, adding them together using one's complement arithmetic and then
 * taking the one's complement of the result.
 *
 *---------------------------------------------------------------------------*/

private short cksum(ptr, len)
unsigned short *ptr;
int len;
{
    unsigned int sum=0;
    for ( ; len >0 ; len-=2, ptr++) {
	if (len == 1)
	/* The length is odd and we are in the last byte. In this case we
	   assume that the next byte after the last is 0 and we add
	   this last word to the sum.
	*/
	    sum += *ptr & 0xff;
	else
	    sum += *ptr;
	if (sum > 0xffff) {
	    sum &= 0xffff;
	    sum++;
	}
    }
    return((short) ~sum);
}

/*-----------------------------------------------------------------------------
 *				Ethernet Driver
 *
 * The following routines implement the lowest comminication level. Actually 
 * they form together a 3com lan controller driver. For reasons of tight
 * schedule they were taken from the old monitor with only slight
 * modifications. Therefore they are not documented and not properly
 * organized. This should hopefully be fixed in the future.
 *
 *---------------------------------------------------------------------------*/

#define	MAXPACK	1522		/* max packet size */
#define	MAX_SENDRETRY 1		/* max transmission retries */
#define	MAXRESEND 4		/* max transmission retries */

#ifndef LAN_BUS_OFFSET
#define LAN_BUS_OFFSET 1
#endif

/*
 * the structure is defined using the page 0 names,
 * all other registers are #defined somewhere else
 */

struct etherlink2 {
	/*
	 * NIC registers
	 */
	unsigned char	cr[LAN_BUS_OFFSET];	/* command register */
	unsigned char	pstart[LAN_BUS_OFFSET];	/* page start */
	unsigned char	pstop[LAN_BUS_OFFSET];	/* page stop */
	unsigned char	bnry[LAN_BUS_OFFSET];	/* boundary */
	unsigned char	tpsr[LAN_BUS_OFFSET];	/* transmit page start */
	unsigned char	tbcr0[LAN_BUS_OFFSET];	/* xmit byte count 0 */
	unsigned char   tbcr1[LAN_BUS_OFFSET];	/* xmit byte count 1 */
	unsigned char	isr[LAN_BUS_OFFSET];	/* interrupt status */
	unsigned char	rsar0[LAN_BUS_OFFSET];	/* remote start addr 0 */
	unsigned char	rsar1[LAN_BUS_OFFSET];  /* remote start addr 1 */
	unsigned char	rbcr0[LAN_BUS_OFFSET];	/* remote byte count 0 */
	unsigned char   rbcr1[LAN_BUS_OFFSET];	/* remote byte count 1 */
	unsigned char	rcr[LAN_BUS_OFFSET];	/* receive config */
	unsigned char	tcr[LAN_BUS_OFFSET];	/* xmit config */
	unsigned char	dcr[LAN_BUS_OFFSET];	/* data config */
	unsigned char	imr[LAN_BUS_OFFSET];	/* interrupt mask */
	unsigned char	dummy[1008][LAN_BUS_OFFSET];	/* the gap between nic and */
				/* the gate array registers */
	/*
	 * Gate array registers
	 */
	unsigned char	pstr[LAN_BUS_OFFSET];	/* page start (gate array) */
	unsigned char	pspr[LAN_BUS_OFFSET];	/* page stop */
	unsigned char	dqtr[LAN_BUS_OFFSET];	/* drq timer */
	unsigned char	bfcr[LAN_BUS_OFFSET];	/* base config register */
	unsigned char	pcfr[LAN_BUS_OFFSET];	/* eprom config reg */
	unsigned char	gacfr[LAN_BUS_OFFSET];	/* gate array config */
	unsigned char	ctrl[LAN_BUS_OFFSET];	/* control register */
	unsigned char	streg[LAN_BUS_OFFSET];	/* status register */
	unsigned char	idcfr[LAN_BUS_OFFSET];	/* interrupt/dma */
	unsigned char	damsb[LAN_BUS_OFFSET];	/* dma address - msb */
	unsigned char	dalsb[LAN_BUS_OFFSET];	/* dma address - lsb */
	unsigned char	vptr2[LAN_BUS_OFFSET];	/* vector pointer reg 2 */
	unsigned char   vptr1[LAN_BUS_OFFSET];	/* vector pointer reg 1 */
	unsigned char	vptr0[LAN_BUS_OFFSET];	/* vector pointer reg 0 */
	unsigned char	fifo_u[LAN_BUS_OFFSET]; /* Fifo buffer */
};

#define romeaddr cr
#define par0	pstart
#define curr	isr

/*
 * init_3com - controller initialization
 * 
 * ttype specifies transciever type to use:
 * 0 internal, not 0 external
 */

private init_3com(ttype)
int ttype;
{
	volatile struct etherlink2 * volatile ep = (struct etherlink2 *)LAN_IOBASE;

	int i, j;

	ep->ctrl[0] = 0x3;
	ep->ctrl[0] = 0x2;
	ep->ctrl[0] = 0x6;
	/*
	 * Get the Ethernet address
	 */
	for (i = 0, j = 0; j < 6 ; i += LAN_BUS_OFFSET, j++) {
		local_eaddr[j] = *(&ep->romeaddr[0] + i);	
	}
	if (ttype != 0)
		ep->ctrl[0] = 0;	/* external transceiver */
	else
		ep->ctrl[0] = 0x2;	/* internal transceiver */
	ep->pstr[0] = 0x26;
	ep->pspr[0] = 0x40;
	ep->idcfr[0] = 0x1;
	ep->dqtr[0] = 0x8;
	ep->damsb[0] = 0x20;
	ep->dalsb[0] = 0;
	return(init_nic());
}

/*
 * init_nic - init the Network Interface Controller
 */

private init_nic()
{
	 volatile  struct etherlink2 * volatile ep = (struct etherlink2 *)LAN_IOBASE;
	int i, j;

	ep->cr[0] = 0x21;
	while ((ep->isr[0] & 0x80) == 0)
		;
	ep->dcr[0] = 0x48;
	ep->rbcr0[0] = 0;
	ep->rbcr1[0] = 0;
	ep->tpsr[0] = 0x20;
	ep->rcr[0] = 0x20;
	ep->tcr[0] = 0x2;
	ep->pstart[0] = 0x26;
	ep->bnry[0] = 0x26;
	ep->pstop[0] = 0x40;
	ep->isr[0] = 0xff;
	ep->imr[0] = 0;
	ep->cr[0] = 0x61;
	for (i = 0, j = 0; j < 6 ; i += LAN_BUS_OFFSET, j++) {
		*(&ep->par0[0] + i) = local_eaddr[j];	
	}
	ep->curr[0] = 0x27;
	nxtpkt = 0x27;
	ep->cr[0] = 0x21;
	ep->cr[0] = 0x22;
	ep->rcr[0] = 0;
	ep->tcr[0] = 0;
	return 1;
}

/*
 * get a packet from the net
 */

private get_packet()
{
	volatile struct etherlink2  * volatile ep =
			(struct etherlink2 *)LAN_IOBASE;
	int	i, retries, resends = 0;

	while(TRUE) {
	    for (retries = 0; retries <= MAXRETRY; retries++) {
		if ((ep->isr[0] & 0x10) != 0) {
		    overflow();
		    return 1;
		}
		ep->cr[0] = 0x62;
		i = ep->curr[0];
		ep->cr[0] = 0x22;
		if (i != nxtpkt) {
		    get_next();
		    return;
		}
	    }
	    /* No packet recieved, Resend last paket */
	    if (++resends <= MAXRESEND)
		resend();
	}
}

/*
 * overflow - overflow handler
 */

private overflow()
{
	volatile struct etherlink2  * volatile ep =
			    (struct etherlink2 *)LAN_IOBASE;
	int	i;

	ep->cr[0] = 0x21;
	ep->rbcr0[0] = 0;
	ep->rbcr1[0] = 0;
	while ((ep->isr[0] & 0x80) == 0)
		;
	ep->tcr[0] = 0x2;
	ep->cr[0] = 0x22;
	i = get_next();
	ep->isr[0] |= 0x10;
	ep->rcr[0] = 0;
	ep->tcr[0] = 0;
	return i;
}

/*
 * get_next - get the next packet from NIC's buffer ring
 */

private get_next()
{
	volatile struct etherlink2  * volatile ep = (struct etherlink2 *)LAN_IOBASE;
	int	size,i;
	unsigned char *cp;

	ep->isr[0] |= 1;
	size = head_check();
	if (size == 0)
		return 1;	/* no packet */
	ep->ctrl[0] &= ~ 0x80;
	while ((ep->streg[0] & 0x8) != 0)
		;
	ep->damsb[0] = (char)nxtpkt;
	ep->dalsb[0] = 0;
	cp = (unsigned char *)&c_packet;
	ep->ctrl[0] &= ~ 0x40;
	ep->ctrl[0] |= 0x80;
	/* Read the packet to RAM. In each iteration read 8 bytes */
	for (i = 0; i < size; i+=8) {
		while ((ep->streg[0] & 0x80) == 0)
			;
		/* read dma burst */
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
		*cp++ = ep->fifo_u[0];
	}
	/* Now,  read the last 7 bytes of the packet */
	for (i = size & 0x7;  i > 0; i--) {
		while ((ep->streg[0] & 0x80) == 0)
			;
		*cp++ = ep->fifo_u[0];
	}
	ep->ctrl[0] &= ~ 0x80;
	nxtpkt = c_packet.npp;
	ep->bnry[0] = c_packet.npp - 1;
	if (ep->bnry[0] < 0x26)
		ep->bnry[0] = 0x3f;
	return 0;	/* success */
}

private head_check()
{
	volatile struct etherlink2  * volatile ep =
			    (struct etherlink2 *)LAN_IOBASE;
	unsigned char *cp;

	ep->ctrl[0] &= ~ 0x80;
	while ((ep->streg[0] & 0x8) != 0)
		;
	ep->damsb[0] = nxtpkt;
	ep->dalsb[0] = 0;
	ep->ctrl[0] &= ~ 0x40;
	ep->ctrl[0] |= 0x80;
	while ((ep->streg[0] & 0x80) == 0)
		;
	cp = (unsigned char *)&c_packet;
	*cp++ = ep->fifo_u[0];  /* Read rstatus - packet status       */
	*cp++ = ep->fifo_u[0];	/*      npp     - next packet pointer */
	*cp++ = ep->fifo_u[0];  /* Read rbc	- Byte count	      */
	*cp   = ep->fifo_u[0];

	ep->ctrl[0] &= ~ 0x80;
	while ((ep->streg[0] & 0x8) != 0)
		;
	if (c_packet.rstatus & 0x01) {
		if (c_packet.rbc <= MAXPACK) {
			return c_packet.rbc;
		}
		nxtpkt = c_packet.npp;
		ep->bnry[0] = nxtpkt - 1;
		if (ep->bnry[0] < 0x26)
			ep->bnry[0] = 0x3f;
	} 
	init_nic();
	return 0;
}

/*
 * transmit packet, the packet data is in c_packet
 */

private send_packet(type, data_size)
int data_size;
short type;
{
	
	volatile struct etherlink2  * volatile ep =
		    (struct etherlink2 *)LAN_IOBASE;
	register unsigned char *cp;
	int	size, i, j;

	bcopy(local_eaddr, c_packet.e_src, 6);
	bcopy(curr_ch.remote_eaddr, c_packet.e_dest, 6);
	c_packet.e_ptype = SWAP_S(type);

	retry = 0;
	ep->ctrl[0] &= ~ 0x80;
	while ((ep->streg[0] & 0x8) != 0)
		;
	ep->damsb[0] = 0x20;
	ep->dalsb[0] = 0;
	cp = (unsigned char *)&c_packet.e_dest[0];
	size = MAX(EPHLEN + data_size, MINTRANSMIT);
	ep->ctrl[0] |= 0xc0;

	for (i = 0; i < size; i += 8) {
		while ((ep->streg[0] & 0x80) == 0)
			;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
		ep->fifo_u[0] = *cp++;
	}
	for (i = size & 0x7;  i > 0; i--) {
		while ((ep->streg[0] & 0x80) == 0)
			;
		ep->fifo_u[0] = *cp++;
	}
	ep->ctrl[0] &= ~ 0x80;
	while ((ep->streg[0] & 0x8) != 0)
		;
	again:
	ep->tbcr0[0] = size & 0xff;
	ep->tbcr1[0] = (size >> 8) & 0xff;
	ep->tcr[0] = 0;
	ep->cr[0] = 0x26;
	for (i = 0; i < 60000; i++) {
		if ((ep->isr[0] & 0x2) != 0)
			break;
		if ((ep->isr[0] & 0x8) != 0) {
			/* transmission error, retry */
			ep->isr[0] |= 0x8;
			if (retry++ >= MAX_SENDRETRY) {
				return 1;
			}
			goto again;
		}
	}
	ep->isr[0] |= 0x2;
	return 0;
}

private overwrite()
{
	volatile struct etherlink2  * volatile ep =
		    (struct etherlink2 *)LAN_IOBASE;
	
	if ((ep->isr[0] & 0x10) == 0)
		return 0;
	return 1;
}

#endif /* LAN */

