(none)

J.Crowcroft at ucl-cs.UUCP J.Crowcroft at ucl-cs.UUCP
Tue Jul 3 23:11:49 AEST 1990


From: J.Crowcroft at uk.ac.ucl.cs




 >But didn't you say it was a simple program?  I don't really see the point
 >of your posting if you can't give any more information.  Before IBM
 >will accept it as a bug, you will have to reduce it to its smallest
 >reproducible level.

 >A. Lester Buck     buck at siswat.lonestar.org  ...!texbell!moray!siswat!buck

okay...small is relative...

what follows is a non working driver for a BICC (lance based) ethernet
card for a PS/2 under AIX 1.1.

interrupts and packet reception come and go depending which
buffers/descriptor rings/initialisation blocks are kmemalloc'd and
which are statics...it occasionally crashes the machine...the compiler
problems referred to in my earlier message are the appearance of
*warnings* only when syntax errors are also detected (I havnt time or
energy to reproduce the smallest program that produces this effect).

If you can make the driver work, you are welcome to it( modulo the
usual saying where it came from). its fairly close to working ... but
very untidy...a variant works under a real time executive on the same
hardware base...if i get it working (and you are interested) i can
repost it...

cheers

 jon

--------------------bicc.h-----------------
/*
 * (c) Bloomsbury Computer Consortium
 */
/*
 * Lance init Block
 */
typedef struct {
	u_short mode;	
	u_char paddr[6];	/* phys address */
	u_char ladrf[8];	/* Logicali addr filter */
	u_char raddr[3];	/* rx desriptor addr */
	u_char rlen;		/* rx descriptor length */
	u_char taddr[3];	/* tx descriptor addr */
	u_char tlen;		/* tx descriptor length */
} LanInit;

/*
 * Descriptor Ring entry
 */
typedef struct {
	u_char addr[3];	/* high 8 bits of buffer addr */
	u_char flags;
	short bcnt;	/* buffer length in bytes */
	u_short mcnt;	/* message byte count */
} LanDesc;

/*
 * Constants for LANCE CSR
 */
#define ERR 0x8000  /* error */
#define BABL 0x4000 /* babble */
#define CERR 0x2000 /* Collision */
#define MISS 0x1000 /* Missed */
#define MERR 0x0800 /* memory error */
#define RINT 0x0400 /* rx interrupt */
#define TINT 0x0200 /* tx interrupt */
#define IDON 0x0100 /* initialisation done */
#define INTR 0x0080 /* interrupt */
#define INEA 0x0040 /* interrupt enable */
#define RXON 0x0020 /* rx on */
#define TXON 0x0010 /* tx on */
#define TDMD 0x0008 /* tx demand */
#define STOP 0x0004 /* stop */
#define STRT 0x0002 /* start */
#define INIT 0x0001 /* initialise */

/*
 * Lance Rx Descriptor ring flag bits
 */
#define RxOWN 0x80 	/* Lance's */
#define RxERR 0x40 	/* Error in rx'd pkt */
#define RxFRAM 0x20 	/* Framing Error */
#define RxOFLO 0x10 	/* Overflow in rxd pkt */
#define RxCRC 0x08  	/* CRC Error - Noize */
#define RxBUFF 0x04	/* Buffer Error */
#define RxSTP 0x02	/* Start of Pkt */
#define RxENP 0x01	/* End of Pkt */

/*
 * Lance TX Descriptor ring flag bits 
 */
#define TxOWN 0x80 	/* Lance's */
#define TxERR 0x40 	/* Error in rx'd pkt */
#define TxMORE 0x10 	/* More in next descriptor */
#define TxONE 0x08 	/* One retry needed to send */
#define TxDEF 0x04 	/* Deferred */
#define TxSTP 0x02	/* Start of Pkt */
#define TxENP 0x01	/* End of Pkt */

/*
 * Tx Descriptor 3 bits
 */
#define TxBUFF 0x8000 /* Buffer error */
#define TxUFLO 0x4000 /* Underflow */
#define TxLCOL 0x1000 /* Late Collision */
#define TxLCAR 0x0800 /* Carrier sense loss */
#define TxRTRY 0x0400 /* Retry error */

/*
 * max ethernet frame size
 */
#define MAXPKTSIZE 1540
#define MINPKTSIZE 60
#define SAFEPKTSIZE 2048

#define EthernetHeaderLength 14
/*
 * useful bits 
 */
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x010
#define BIT5 0x020
#define BIT6 0x040
#define BIT7 0x080

extern unsigned short ioin();
extern u_long kvtophys();
/*
 * Configuration specific  ...
 */

/* The Bicc ethernet card is from 0x8280 up to 0x828f */
#define BICCADDR 0x8280

#define CARDID  0x0808;

/* Should be around 8 for decent performance */
#define NRXDS 8
/* and 2**3 = 8, but shift up 5 places for lance... */
#define LN2NRXDS (3 << 5)

/* Force Quad word align (not nec needed...) */
#define QUAD(x) (((u_long)(x) + 3) & 0xfffffffc)

/* use kmemalloc rather than statics for buffers: */
/*
#define DYNBUF
*/

#define DEBUG 1
#define DEBUGI 1
#define DEBUGRI 1
/*
#define DEBUGTI 1
#define DEBUG2 1
*/
---------------bicc.c--------------------
/*
 * BICC ISOLANN MAC Ethernet Devicve Driver
 * Under AIX PS/2 1.1
 * 
 * Version 1.1
 *
 * J Crowcroft 
 * jon at cs.ucl.ac.uk
 * jon at uk.ac.ucl.cs
 * ccaajac at uk.ac.ucl
 *
 * (c) Bloomsbury Computer Consortium
 */

/* 
 * TODO
 * Add multiple device support properly
 * Add LLC support (at least LLC1).
 * Add err logging instead of printfs...
 * Deal with lance freeeze...
 * Add LPP support...(external to this)
 */

/* We ARE of course in Kernel mode */
#ifndef KERNEL
#define KERNEL
#endif

#ifndef i386
#define i386
#endif

/* And get all required definitions */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/tty.h>
#include <sys/devinfo.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/dir.h>
#include <sys/iobuf.h>
#include <sys/ioctl.h>
#include <sys/ioctlcmd.h>
#include <sys/systm.h>
#include <sys/machinfo.h>
#include <sys/erec.h>
#include <sys/i386/pos.h>
#include <sys/i386/cmos.h>
#include <sys/i386/dmaralloc.h>
#include <sys/i386/intr86.h>
#include <sys/callout.h>
#include <sys/i386/mmu386.h>
#include <sys/socket.h>
#include <sys/in.h>
#include <sys/if.h>
#include <sys/if_ieee802.h>
#include <sys/netisr.h>
#include <sys/mbuf.h>
#include <sys/in_var.h>
#include <sys/in_systm.h>
#include <sys/ip.h>

#include <sys/eth.h>

#include <vmalloc.h>

#include "bicc.h"


/*
 * Notes:
 *
 * POS (programmable option select) registers accessed thru I/O
 * see posread/poswrite call
 *   ioin(b) and ioout(b) on PS/2 AIX
 *
 * buffer allocations etc use 
 *	kmemalloc for internal structures
 * 	m_get, m_getclr, m_free, mtod, dtom, m_copy, m_cat, m_pullup
 *
 * queuing to IF uses
 *	IF_ENQUEUE and IF_DEQUEUE

 * called on input and output ifq's in
 * if device we learn about in lainit()...
 *
 * critical network code protected by calling 
 * 	splnet
 * critical mbuf code protected by
 *	splimp
 *
 */


/*
 ****************** GLOBS ******************
 */

/*
 * Stats gathering - inited in lainit
 */
struct laststats {
	u_long txd; /* snet */
	u_long rxd; /* got */
	u_long ovr; /* overrruns */
	u_long bad; /* bad frames */
	u_long sht; /* short frames */
	u_long mst; /* missed frames */
	u_long bab; /* babbles */
	u_long crc; /* Collisions */
	u_long ukn; /* unk type frames */
} Lastats;

#ifdef DYNBUF

#else
/*
 * declare static space for init, rings and buffers
 */
static char sbuff[NRXDS][SAFEPKTSIZE];
static char naff0[16];

static char stxbuff[SAFEPKTSIZE];
static char naff1[16];

static LanDesc stxring;
static char naff2[16];

static LanDesc srxring[NRXDS];
static char naff3[16];

static LanInit slaninit;
static char naff4[16];

#endif

/* Save where we kmemalloc space for buffs (for later checks) */
static char *rxbuff[NRXDS];

/* hang onto the tx buff too */
static char *txbuff;

/* Lance Descriptor rings  and buffers */
static LanDesc *txring, *rxring;

/* Base addr of BICC card */
static u_long labase = BICCADDR;

/* Cardid we read back on MCA for BICC */
static int cardid = CARDID;

static int initted = 0;

/* Visible Device Driver routines */
int lainit(), laoutput(), laioctl(), 
	lawatchdog(), lareset(), laintr();
/* following not defined or used - could be for etherfind (NIT) interface
	laopen(), laread(), lawrite(), 
*/

/* Internal to driver...  */
static void la_rxintr(), la_txintr(), larx();
static void lacsrwrite(), ilacsrwrite();
static u_short lacsread(), ilacsread();

static u_long posfind();
static u_short posread();
static void poswrite();

/* Misc things required */
struct iobuf labuf;
int lalevl; 			/* interrupt level of BICC lance device */

u_short bdn;		 	/* Ether board number */
extern struct devdata devdata[];/* GLobal Device Data structure */
extern struct ifqueue ipintrq;	 /* The IP s/w interrupt queue */

/*
 * Herein should be an Struct arpcom...
 * ( which includes an ifnet...)
 */
struct arpcom ether_ifs[1];	 /* IFPs */
/*
 * our addr - need more than one for multiple bicc support
 */
u_char lanaddr[6];
u_long outipaddr;

/* For timing out a frozen lance... */
static struct callout *freezer = NULL;

/*
 *******************************************************************
 * Find and Initiailize the BICC card
 * Set up any globs for the driver
 * 
 * Here we learn about IF and we tel IF structure about all our entry
 * points
 *
 * Called by Kernel at boot time
 *******************************************************************
 */
lainit(dev)
dev_t dev;
{
	struct ifnet *ifp; 
	LanInit *laninit;
	LanDesc *rdre;
	int i, j, status;
	u_long addr;
	u_short tmp;
	char *data, *buf;
	int unit, maj, slot; /* for multiple ether board support  */
	int s;
#ifdef PROMISC
	int promisc = 0;
#endif

#ifdef DEBUG2
	printf("lainit\n");
#endif

/* No ints while doin this please */
	s = splimp();

	unit = minor(dev);
	maj = major(dev);

	ifp = &(ether_ifs[0].ac_if); /* should be unit */	

	if ((slot = devexist(cardid, maj, 1, 0)) == -1) {
		printf("lainit: no bicc device\n");
		splx(s);
		return -1;
	}

	if (!initted) {
		DEV_INSTALL(maj, lainit, lareset, nulldev, nulldev,
			laintr, ISNOTATTY);
		BDEV_INSTALL(maj, nulldev, nulldev, &labuf);
		CDEV_INSTALL(maj, nulldev, nulldev, laioctl, nulldev, ISNOTATTY);
	}

#ifdef NVRAMBICC
/* AIXPS2 doesnt know about bicc... yet */
	lalevl = devdata[slot].pd_pos3;
#else
	lalevl = 3; 
#endif

#ifdef DEBUG2
	printf("lalvl %d\n", lalevl);
#endif

/* And stop the beast before we get it going */
	lacsrwrite(0, STOP);

	if (!initted)
		intrattach(laintr, lalevl, SPL_IMP);
/*
 * zero internal stats
 */
	Lastats.txd = 0L;
	Lastats.rxd = 0L;
	Lastats.ovr = 0L;
	Lastats.bad = 0L;
	Lastats.sht = 0L;
	Lastats.mst = 0L;
	Lastats.bab = 0L;
	Lastats.crc = 0L;
	Lastats.ukn = 0L;

/* POS stuff to find BDN BICC/Lance - panics if not present */
	bdn = posfind(cardid); 
	poswrite(bdn, 0x102, 0x11); /* Enable card and sampled timings */
	poswrite(bdn, 0x104, 0x88); /* IRQ, Fairness etc */

/* Get mem for lance descriptor, panic if fail */
#ifdef DYNBUF
	if (!initted)
		laninit = (LanInit *)kmemalloc(sizeof(LanInit), 
			MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP);
	if(laninit == NULL)
		panic("lainit: kmemalloc failed");
#else
	laninit = QUAD(&slaninit);
#endif
	bzero(laninit, sizeof(LanInit));

/* get tx and rxrings */
#ifdef DYNBUF
	if (!initted)
		txring = (LanDesc *)kmemalloc(sizeof(LanDesc),
			MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP);
	if(txring == NULL)
		panic("lainit: kmemalloc failed");
#else
	txring = QUAD(&stxring);
#endif

#ifdef DYNBUF
	if (!initted)
		rxring = (LanDesc *)kmemalloc(NRXDS*sizeof(LanDesc),
			MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP);
	if(rxring == NULL)
		panic("lainit: kmemalloc failed");
#else
	rxring = QUAD(&srxring[0]);
#endif

/* Setup Lance Initialisation bloc */
	for(i=0; i<8; i++)
		laninit->ladrf[i] = 0;

	laninit->tlen = 0; 	/* 1 entry in tx ring */
	laninit->rlen = LN2NRXDS; /* 8 entries in rx ring (2^3)*/

/* Flatten addr of rx and tx descriptor and put in init bloc */
	addr = kvtophys(txring);
	for(i=0; i<3; i++)
		laninit->taddr[i] = ((u_long)(addr >> (8 * i))) & 0xff;
	addr = kvtophys(rxring);
	for(i=0; i<3; i++)
		laninit->raddr[i] = ((u_long)(addr >> (8 * i))) & 0xff;

#ifdef PROMISC
	if (promisc)
		laninit->mode = 0x8000;
	else
		laninit->mode = 0;
#else
	laninit->mode = 0; /* Normal operation mode */
#endif
	txring->flags = 0;

/* setup rx ring */
	for(i=0, rdre = rxring; i<NRXDS; i++, rdre++) {
#ifdef DYNBUF
		if (!initted)
			buf = (char *)kmemalloc(SAFEPKTSIZE,
				MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP);
		if(buf == NULL)
			panic("lainit: kmemalloc failed");
#else
		buf = QUAD(sbuff[i]);
#endif
		rxbuff[i] = buf; /* record v addr */
		addr = kvtophys(buf); /* map to phys */
		for(j=0; j<3; j++)
			rdre->addr[j] = ((u_long)(addr >> (8 * j))) & 0xff;
		rdre->bcnt = -(SAFEPKTSIZE);
		rdre->mcnt = 0;
		rdre->flags = RxOWN; /* Give to the Lance */
	}
/* Receive Descriptor Ring ptr at head of list */
	rdre = rxring;

/* Get mem for transmit buffer */
#ifdef DYNBUF
	txbuff = (char *)kmemalloc(SAFEPKTSIZE, MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP);
	if(txbuff == NULL) 
		panic("lainit: kmemalloc failed\n");
#else
	txbuff = QUAD(&stxbuff[0]);
#endif
	addr = kvtophys(txbuff); /* map to phys */
	for(j=0; j<3; j++)
		txring->addr[j] = ((u_long)(addr >> (8 * j))) & 0xff;

/* get phys addr somewhere more useful */
	for(i=0; i<6; i++) {
		lanaddr[i] = ioin((u_short)(labase + (i * 2))) & 0xff;
		laninit->paddr[i] = lanaddr[i];
		ifp->ac_lanaddr[i] = lanaddr[i];
	}

#ifdef DEBUG
	printf("lainit ea: ");
	for(i=0; i<6; i++) 
		printf("%x.", lanaddr[i]);
	printf("\n");
#endif

/* and give it the init block */
	addr = kvtophys(laninit);
	tmp = (u_short)(addr & 0xffff);
	lacsrwrite(1, tmp);
	tmp = ((u_long)addr >> 16) & 0xff;
	lacsrwrite(2, tmp);
	lacsrwrite(3, 0);
	lacsrwrite(0, INIT);

/* wait for it to live...  */
	while(((status = lacsread(0)) & IDON) == 0) {
		if (status & ERR)  {
			printf("lainit err: 0x%x\n", status);
			panic("lance initialisation error");
		}
	}
/* fill in IF information..  */
	ifp->if_unit = unit;
	ifp->if_name = "la";
	ifp->if_mtu = ETHERMTU;

	ifp->if_init =  lainit;
	ifp->if_output = laoutput;
	ifp->if_ioctl = laioctl;
	ifp->if_reset = lareset;
	ifp->if_watchdog = lawatchdog;

	ifp->if_timer = 0;

/* Do broadcasts, dont do trailers, am ethenet IEEE */
	ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_ETHERNET;
/*
| IFF_IEEE;
*/

/* Set stats */
	ifp->if_ipackets = 0;
	ifp->if_opackets = 0;
	ifp->if_ierrors = 0;
	ifp->if_oerrors = 0;
	ifp->if_collisions = 0;

/* tell aix we've started lance...  */
	if(!initted)
		if_attach(ifp);

	if(!initted) /* avoid wraparound */
		initted++;

	lacsrwrite(0, IDON); 		/* Clear IDON */
	lacsrwrite(0, STRT|INEA);	/* Start and enable interrupts...  */

	splx(s);
	return 0;
}

/*
 *******************************************************************
 * output to ethernet 
 *
 *******************************************************************
 */
laoutput(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
	int error;
	u_char edst[6], *destn = edst;
	struct ip *pip;
	struct in_addr idst;
	struct mbuf *m = m0;
	spl_t s;
	int type = 0, usetrailors;
	int off;
	int hdr_len, mac_len;
	u_char *macp;
	struct ifqueue *opq;	 /* The IP output queue */

#ifdef IEEE
	int llc_len;
	struct ie2_llc_hdr *llcp;
#else
	struct eth_header *dix;
	struct arphdr *arph;
#endif

#ifdef DEBUG2
	printf("laoutput\n");
#endif
/*
 * is interface up and running ??
 */
	if((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
#ifdef DEBUG2
		printf("laoutput %x\n", ifp->if_flags);
#endif
		error = ENETDOWN;
		goto bad;
	}

/* The BIG SWITCH - see AIX Tech Ref Vol 2 */
	switch(dst->sa_family) {
	case AF_INET:
		idst = ((struct sockaddr_in *)(dst))->sin_addr;
		if(!arpresolve(ifp, m, &idst, destn, &usetrailors)) {
#ifdef DEBUG2
			printf("laoutput: arp failed\n");
#endif
			return 0;
		}
		pip = mtod(m, struct ip *);
		off = ntohs(pip->ip_len) - m->m_len;
/* Trailer stuff */
		if (usetrailors && off > 0 && (off & 0x1ff) == 0 &&
			m->m_off >= MMINOFF + 2 * sizeof(u_short)) {
			type = ETHERTYPE_TRAIL + (off>>9);
			m->m_off -= 2 * sizeof(u_short);
			m->m_len += 2 * sizeof(u_short);
			*mtod(m, u_short *) = htons((u_short)LANTYPE_IP);
			*(mtod(m, u_short *) +1) = htons((u_short)m->m_len);
			goto gottrailortype;
		} else {
			type =  LANTYPE_IP;
			off = 0;
			goto gottype;
		}
		break;

	case AF_ARP:
		type = LANTYPE_ARP;
		/* DROP THRU */
	case AF_UNSPEC:
		bcopy((caddr_t)dst, (caddr_t)edst, 6);
		goto gottype;
		break;

	default:
#ifdef DEBUG2
		printf("laoutput: non supported type\n");
#endif
		error = EAFNOSUPPORT;
		return;
		break;
	}

gottrailortype:
#ifdef DEBUG2 
	printf("laoutput: trailer\n");
#endif
	while(m->m_next) /* pkt going at end of packet!! */
		m = m->m_next;
	m->m_next = m0;
	m = m0->m_next;
	m0->m_next = 0;
	m0 = m;

gottype:
	mac_len = 2 * sizeof(edst) + sizeof(u_short);

#ifdef IEEE802
/* not llc yet */
	llc_len = sizeof(struct ie2_llc_hdr);
	hdr_len = mac_len + llc_len;
#endif
	hdr_len = mac_len; 
	arph = mtod(m, struct arphdr *);

/*  find header space */
	if ((m0->m_off > MMAXOFF) || (MMINOFF + hdr_len > m0->m_off)){
		m = m_get(M_DONTWAIT, MT_HEADER);
		if (m == 0) {
			error = ENOBUFS;
			goto bad;
		}
		m->m_next = m0;
		m0 = m;
		m0->m_off = MMINOFF;
		m0->m_len = hdr_len; 
	} else {
		m0->m_off -= hdr_len;
		m0->m_len += hdr_len;
	}
	
/* Fill in mac header, dst, src, type.  */
/* THIS IS HEAVILY DIX SPEFICIX */
	macp = mtod(m, u_char *);
	dix = mtod(m, struct eth_header *);

/* dst */
	if (type == LANTYPE_ARP) {
		bcopy((char *)arph + sizeof(struct arphdr) + 6 + 4,
			(caddr_t)&(dix->eth_dest[0]), ETH_ADDR_SIZE);
		bcopy(lanaddr, (char *)arph+sizeof(struct arphdr), 
			ETH_ADDR_SIZE);
/* 
 * OVERWRITE 802 part of arp packet 
 */
		arph->ar_hrd = ntohs(ARPHRD_ETHER);
	}
	else
		bcopy((caddr_t)edst, 
			(caddr_t)&(dix->eth_dest[0]), ETH_ADDR_SIZE);
/* src */
	bcopy((caddr_t)&lanaddr[0], 
			(caddr_t)&(dix->eth_srce[0]), ETH_ADDR_SIZE);
/* type */
	if (type != 0) {
		type = htons(type);
		bcopy((caddr_t)&type, 
			(caddr_t)&(dix->eth_type[0]), sizeof(u_short));
	}
/* 
 * Otherwise we'd do the following...
 * fill LLC header (only if IEEE ... )
	llcp = mac_to_llc(macp);
	bcopy((caddr_t)&sap->sa_llc, (caddr_t)llcp, lc_len);
 * and we'd need to set length rather than type (suitably swapped?)
 */

/*
 * watch out critics region about
 * see if we can queue pkt for output, if so start it...
 */
	s = splimp();
	opq = &(ifp->if_snd);
	if (IF_QFULL(opq)) {
		IF_DROP(opq);
		error = ENOBUFS;
		splx(s);
#ifdef DEBUG
		printf("la: IP oq full\n");
#endif
		goto qfull;
	}
	IF_ENQUEUE(opq, m);
	laostart(ifp); 
	splx(s); 
	return 0; 

qfull:
	m0 = m;
bad:
	m_freem(m0);
	return error;

}

/*
 *******************************************************************
 * called if timeout happens (lance frozen?
 *******************************************************************
 */
laorestart(ifp)
struct ifnet *ifp;
{
#ifdef DEBUG
	printf("laorestart\n");
#endif
	if (freezer == NULL)
		return;

	to_cancel(freezer);
	printf("la: frozen lance\n");
/* FOR NOW XXX */
return;

#ifdef DEBUG
	lacsrwrite(0, RINT|TINT|INEA);	/* Start and enable interrupts...  */
#else
	lainit(makedev(25,0));
#endif
}


/*
 *******************************************************************
 * called whenever more work to be done...
 * dequue from ifp->if_snd then get lance going...
 * Currently called only from la_output, could
 * call from lainit & laintr if we want - note
 * splimp should have been called before this is...
 *******************************************************************
 */
laostart(ifp)
struct ifnet *ifp;
{
/*
 * Find from args...
 */
	char *dstadr, *data;
	int olen, len, i;
	u_long addr;
	struct mbuf *m;
	char *buf;

#ifdef DEBUG2
	printf("laostart\n");
#endif

/* dequeue safely packet */
	IF_DEQUEUE(&(ifp->if_snd), m);	

	if (m == NULL) {
		printf("laostart: deq null m\n");
		return;
	}

/* copy in from mbuf, as much as there was */
	len = copy_from_mbufs(txbuff, m);

/* fixup min pkt size...else lance wont send  */
	if (len < MINPKTSIZE)
		len = MINPKTSIZE;
	else if (len & 1) 
		len++;
#ifdef DEBUG2
	ethdump(txbuff, len);
#endif

	addr = kvtophys(txbuff);
	for(i=0; i<3; i++) 
		txring->addr[i] = ((u_long)(addr >> (8 * i))) & 0xff;
	txring->bcnt = -(len);
	txring->mcnt = 0;

/* Set a timer to deal with LANCE freezing here...  */
	freezer = ctimeout(laorestart, ifp, 50);

/* Give to Lance and set STP/ENP */
	txring->flags = TxOWN|TxSTP|TxENP;
	lacsrwrite(0, INEA|TDMD);

	if (txring->flags & TxERR) {
		txerrp(txring->mcnt);
		if (txring->mcnt & TxLCOL) 
			ifp->if_collisions++;
	}	
	m_freem(m);

	Lastats.txd++;
	ifp->if_opackets++;

#ifdef DEBUGTI
	printf("laostart ret\n");
#endif
}

static
txerrp(txerr)
{
#ifdef DEBUG
	printf("La_txintr error 0x%x", txerr);
	if (txerr & TxBUFF) 
		printf("TxBuff");
	if (txerr & TxUFLO) 
		printf("TxUFLO");
	if (txerr & TxLCOL) {
		printf("TxLCOL");
	}
	if (txerr & TxLCAR) 
		printf("TxLCAR");
	if (txerr & TxRTRY) 
		printf("TxRTRY");
	printf("\n");
#endif
}


/*
 *******************************************************************
 * deal with ioctl sys calls to device...
 *******************************************************************
 */
laioctl(ifp, cmd, data)
struct ifnet *ifp;
int cmd;
caddr_t data;
{
	struct ifaddr *ifa = (struct ifaddr *)data;
	struct arpcom *arper = (struct arpcom *)ifp;
	int error = 0;
	short flags = ifp->if_flags;
	spl_t s = splimp();

#ifdef DEBUG2
	printf("laioctl: ");
	lainfo();
#endif

	switch(cmd) {	
	case (SIOCSIFADDR):
		ifp->if_flags |= IFF_UP;
		switch(ifa->ifa_addr.sa_family) {
		case AF_INET:
			arper->ac_ipaddr = 
				IA_SIN(ifa)->sin_addr;
			arpwhohas(arper, &IA_SIN(ifa)->sin_addr);
			break;
		default:
#ifdef DEBUG
			printf("laioctl: arp on non IP?\n");
#endif
			break;
		}
#ifdef DEBUG
		printf("laioctl: %x\n", 
			ntohl(((struct arpcom *)ifp)->ac_ipaddr));
#endif
/*
 * fall thru
 */
	case (SIOCSIFFLAGS):
		if (ifp->if_flags & IFF_UP) {
			if (ifp->if_flags & IFF_RUNNING)
				break;
			else {
/*
 * Bring up the link...
 */
				ifp->if_flags |= IFF_RUNNING;
/*
 * Set anything else
 */
#ifdef DEBUG
			if (flags & IFF_DEBUG)
				lainit(makedev(25,0));
#endif
			}
		} else {
			if(ifp->if_flags & IFF_RUNNING) {
/*
 * Close down link
 */
				ifp->if_flags &= ~IFF_RUNNING;
/*
 * Unset anything else
 */
			}
		}
		break;
	default:
		error = EINVAL;
		break;
	}
	splx(s);
	return error;
}

/*
 *******************************************************************
 * deal with reset
 * eg at reboot
 *******************************************************************
 */
lareset()
{
#ifdef DEBUG
	printf("lareset: called\n");
#endif
/* stop lance */
	lacsrwrite(0, STOP);
	lacsrwrite(0, MERR|INEA);
}

/*
 * watchdog timer - not used...
 */
lawatchdog()
{
#ifdef DEBUG
	printf("lawatchdog: called\n");
#endif
	lacsrwrite(0, STOP);
	lainit(makedev(25,0));
}

/* 
 *******************************************************************
 * The interrupt handler - field in and out...
 *******************************************************************
 */
laintr(vec_num)
int vec_num;
{
	u_short status;
	int done = 0;
	struct ifnet *ifp = &ether_ifs[0]; /* should derive from int vec */

/* Should then derive labase from ether_ifs to support multiple i/fs */

	ioout(labase + 0x0e, 0); 	/* get right CSR */
	status = ioin(labase + 0x0c);  	/* read data */

#ifdef DEBUG
	if (!initted) {
		printf("laintr: not initted\n");
		return done;
	}
#endif

	if (!(status & INTR)) {
		printf("la: spurious interrupt\n");
 		return done;
	}

/*
 * which kind of interrupt?? 
 * Note lance keeps internal track of rx buffer chain
 * need to check own bit then clear rint bit, else
 * packet can arrive between own and rint and we miss intr
 */
#ifdef DEBUG
	if (status & RINT) { 
#else
	if ((status & RINT) && (status & RXON)) { 
#endif
		ilacsrwrite(0, RINT&INEA); /* clr rx int and INEA */
		la_rxintr(ifp);
		done++;
	}

#ifdef DEBUG
	if (status & TINT) { 
#else
	if ((status & TINT) && (status & TXON)) { 
#endif
		ilacsrwrite(0, TINT&INEA); /* clr tx int and INEA */
		la_txintr(ifp);
		done++;
	}

	if ( (status & (BABL | MERR | MISS | TXON | RXON)) != (RXON|TXON) ) 
		la_errintr(status, ifp);

	return done;
}

static
la_errintr(status, ifp)
u_short status;
struct ifnet *ifp; 
{
	int restart = 0;
	
#ifdef DEBUG
	printf("laintr ");
#endif
#ifdef DEBUG2
	printf("(%x) ", status);
#endif

	if (status & MISS) {
		printf("miss ");
		ilacsrwrite(0, MISS|INEA);
		Lastats.mst++;
	}
	if (status & BABL) {
		printf("babl ");
		ilacsrwrite(0, BABL|INEA);
		Lastats.bab++;
	}
	if (status & CERR)  {
		printf("crc ");
		ilacsrwrite(0, CERR|INEA);
		Lastats.crc++;
	}
/* Next one is bad lose!! */
	if (status & MERR) {
		printf("mem ");
/* maybe ilacsrwrite(0, STOP); ??? */
		ilacsrwrite(0, MERR|INEA);
	}
/* Assert the next one never happens :-) */
	if (status & IDON) {
		printf("ini done ");
		ilacsrwrite(0, IDON|INEA);
	}
	if (!(status & RXON)) {
		printf("rx stopped ");
		restart++;
	}
	if (!(status & TXON)) {
		printf("tx stopped ");
		restart++;
	}
	printf("\n");

	if (restart) {
		printf("la: restarting...csr %x\n", status);
		lainit(makedev(25,0));
	}
}

/* 
 *******************************************************************
 * rx interrupt handler - field in 
 *******************************************************************
 */
static void
la_rxintr(ifp)
struct ifnet *ifp; 
{
	register LanDesc *rdre;
	register int cnt; 
	int len;
	char *pkt;
#ifdef DEBUG
	int j;
	u_long addr;
#endif

/* Go round all the rx ring...  check which are ours, */
/* Should check STP and ENP in flags... */
	for(rdre = rxring, cnt=0; cnt<NRXDS; rdre++, cnt++)  {
		if ((rdre->flags & RxOWN) != RxOWN) { /* If its ours...  */
			len = rdre->mcnt;
			pkt = rxbuff[cnt];
			if (rdre->flags & RxERR) 
				rdreerr(rdre->flags);
			else
				larx(pkt, len, ifp);
			rdre->bcnt = -(SAFEPKTSIZE);
			rdre->mcnt = 0;
			rdre->flags = RxOWN; /* and give the lance ownership */
#ifdef DEBUG
/* convert phys addr back to virt and check lance and we agree!! */
			addr = (u_long)(rdre->addr[2] << 16) |
				(u_long)(rdre->addr[1] << 8) |
				(u_long)(rdre->addr[0]); 
			if (addr != kvtophys(pkt)) {
				printf("la: bad rx buffer %x!=%x\n",
					addr, kvtophys(pkt));
				addr = kvtophys(pkt); 
				for(j=0; j<3; j++)
					rdre->addr[j] = 
					((u_long)(addr >> (8 * j))) & 0xff;
			}
#endif
		}
	}
	ifp->if_ipackets++;
	Lastats.rxd++;
#ifdef DEBUG2
	printf("r");
	lainfo();
#endif
}

/*
 *******************************************************************
 * Demux frame types 
 *******************************************************************
 */
static void
larx(pkt, len, ifp)
char *pkt;
int len;
struct ifnet *ifp; 
{
	struct arpcom *arper = (struct arpcom *)ifp;
	int data_len;
	char *addr_of_data;
	u_short type;
	struct mbuf *m, *copy_to_mbufs();
	struct ifqueue *ipq;	 /* The IP s/w interrupt queue */
 
	if ((len < MINPKTSIZE) || (len > MAXPKTSIZE)) {
		Lastats.sht++;
#ifdef DEBUGRI
		printf("la: rx bad size pkt: %d\n", len);
#endif
		return;	
	}
/*
 * the other BIG SWITCH - Find Client for packet type (? LSAP?)
 * Now do the mbuf and queuing stuff...
 */
	type = ((u_short *)(pkt))[6];
	type = htons(type);
	addr_of_data = pkt + sizeof(struct eth_header);
	data_len = len - sizeof(struct eth_header);
	switch(type) {
	case LANTYPE_IP:
printf("I");
		m = copy_to_mbufs(addr_of_data, data_len, ifp);	
		if (m == NULL) 
			break;
		ipq = &ipintrq;
		if (IF_QFULL(ipq)) {
#ifdef DEBUGRI
			printf("larx: IP inq full\n");
#endif
			IF_DROP(ipq);
			m_freem(m);
			return;
		}
#ifdef DEBUGRI
		printf("larx: IP enq %d\n", data_len);
#endif
		IF_ENQUEUE(ipq, m);
		schednetisr(NETISR_IP);
		break;
	case LANTYPE_ARP:
printf("A");
		m = copy_to_mbufs(addr_of_data, data_len, ifp);	
		if (m == NULL)  
			break;
		arpinput(arper, m);
#ifdef DEBUGRI
		printf("larx: ARP enq\n");
#endif
		break;
	default:
/*
 * Could cope with IEEE LLC frames here...
 * need a registry of LSAPs etc etc...
 */
		Lastats.ukn++;
#ifdef DEBUG2
		printf("larx unknwn frm type %x len %d\n", type, len);
#endif
		break;
	}
}

static
rdreerr(flags)
u_short flags;
{
#ifdef DEBUG
	printf("laintr: err 0x%x", flags);
	if (flags & RxCRC) 
		printf("CRC");
	if (flags & RxBUFF)
		printf("BUFF");
	if (flags & RxOFLO) 
		printf("OFLO");
	if (flags & RxFRAM) 
		printf("FRAM");
	printf("\n");
	
#endif
}

/*
 *******************************************************************
 * handle tx interrupts...
 *******************************************************************
 */
static void
la_txintr(ifp)
struct ifnet *ifp;
{
#ifdef DEBUGTI
	printf("t");
#endif
	if (freezer != NULL) {
		to_cancel(freezer);
		freezer = NULL;
	}
#ifdef DEBUG
	if (txring->flags & TxERR) {
		txerrp(txring->mcnt);
		if (txring->mcnt & TxLCOL) 
			ifp->if_collisions++;
	}	
#endif
}

/*
 *******************************************************************
 * general service routines...
 *******************************************************************
 */

/*
 * service routine copy from mbuf to contig lance buffer
 */
static
copy_from_mbufs(buf, m)
unsigned char *buf;
struct mbuf *m;
{
	int offset;
	struct mbuf *mp;

	for(offset=0, mp = m; mp; mp = mp->m_next) {
		unsigned int len = mp->m_len;
		unsigned char *mcp;

		if (len != 0) {
			mcp = mtod(mp, unsigned char *);
			bcopy((caddr_t)mcp, (caddr_t)buf, len);
			offset += len;
			buf += len;
		}
	}
	return offset;
}

/*
 * service routine for la_rxintr
 */
static struct mbuf *
copy_to_mbufs(addr, totlen, ifp)
char *addr;
int totlen;
struct ifnet *ifp; 
{
	int len;
	struct mbuf *m, *top=NULL, **mp = ⊤
	char *mcp;
	spl_t s = splimp();

	ifp->if_ipackets++;
	
	while(totlen > 0) {
		MGET(m, M_DONTWAIT, MT_DATA);
		if (m == NULL) 
			goto bad;
		len = totlen;
		if (ifp != NULL)
			len += sizeof(ifp);

		if (len >= mincluster) {
printf("ctm: >mcl");
			MCLGET(m);
			if (m->m_len == CLBYTES) 
				m->m_len = len = MIN(CLBYTES, len);
			else
				m->m_len = len = MIN(MLEN, len);
		} else {
			m->m_len = len = MIN(MLEN, len);
			m->m_off = MMINOFF;
		}

		mcp = mtod(m, u_char *);
		if(ifp != NULL) {
			*(mtod(m, struct ifnet **)) = ifp;
			mcp += sizeof(ifp);
			len -= sizeof(ifp);
			ifp = NULL;
		}
		
		bcopy(addr, mcp, len);
		addr += len;
		*mp = m;
		mp = &m->m_next;
		totlen -= len;
	}
	splx(s);
	return top;
bad:
#ifdef DEBUGI
	printf("copy_to_mbufs failed, no mbuf\n");
#endif
	if (top != NULL)
		m_freem(top);
	splx(s);
	return NULL;
}

ethdump(pkt, len)
char *pkt;
{
	struct eth_header *dix = (struct eth_header *)pkt;
	int i;

	for(i=0; i<6; i++)
		printf("%2x:", dix->eth_srce[i] & 0x0ff);
	printf("->");
	for(i=0; i<6; i++)
		printf("%2x:", dix->eth_dest[i] & 0x0ff);
	i = *(u_short *)dix->eth_type;
	printf("(%x)\n", i);

	pkt = pkt + sizeof(struct eth_header);
	pktdump(pkt, len);
}

pktdump(pkt, len)
char *pkt;
{
	if (len > 80)
		len = 80;
	while(len-- > 0) {
		printf("%2x ", (int)(*pkt) & 0xff);
		pkt ++;
	}
	printf("\n");
}
/*
 *************************************
 *
 * talk to the lance direct
 */

/*
 *Write lance csr with intrs off
 */
static void
lacsrwrite(csr, data)
u_short csr, data;
{
	spl_t i = splnet();
	ioout(labase + 0x0e, csr);
	ioout(labase +0x0c, data);
	splx(i);
}

/*
 *write lance csr with intrs as are
 */
static void
ilacsrwrite(csr, data)
u_short csr, data;
{
	ioout(labase + 0x0e, csr);
	ioout(labase +0x0c, data);
}

/*
 * read lance CSR
 */
static u_short
lacsread(csr)
u_short csr;
{
u_short ret;
	spl_t i = splnet();
	ioout(labase + 0x0e, csr);
	ret = ioin(labase + 0x0c);
	splx(i);
	return ret;
}

/*
 ***************************************
 *
 * POS things to do
 */

static void 
poswrite(card,reg,val)
u_short card, reg, val;
{
	spl_t i = splnet();
	iooutb(0x94, BIT5|BIT7);
	iooutb(0x96, BIT3|card);
	ioout(0x100|reg, val); /* was iooutb */
	splx(i);
}

static u_short
posread(card, reg)
u_short card, reg;
{
	u_short ret;
	spl_t i = splnet();
	iooutb(0x94, BIT5|BIT7);
	iooutb(0x96, BIT3|card);
	ret = ioin(0x100|reg); /* was ioinb */
	splx(i);
	return ret;
}


static u_long
posfind(id)
u_short id;
{
u_long i;
	for(i=0; i<8; i++) {
		if((posread(i, 0x100) << 8) | posread(i, 0x0101) == id)
			return i;
	}
/*
 * else no such slot...
 */
	panic("cannot BICC Ether card slot");
/* NOTREACHED */
	return -1;
}

/*
 * Debugging prints...
 */
#ifdef DEBUG
static
lainfo()
{
	int i, j;
	LanDesc *rdre; 
	u_short stat;

	stat = lacsread(0);
	printf("\nla stat %x\n", stat);
#ifdef DEBUG2
	for(i=0; i< 6; i++)
		printf("%x.", lanaddr[i]& 0xff);
	printf("\n");
	printf("txt\trxd\tovr\tbad\tsht\tmst\tbab\n");
	printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
		Lastats.txd, 
		Lastats.rxd, 
		Lastats.ovr, 
		Lastats.bad, 
		Lastats.sht, 
		Lastats.mst, 
		Lastats.bab );

	for(j=0, rdre = rxring; j<NRXDS; j++, rdre++) {
		printf("rxbuff %d %x\n", j, rxbuff[j]);
		printf("addr:%x,%x,%x flg: %x bcnt %d mcnt %d\n",
			rdre->addr[0],
			rdre->addr[1],
			rdre->addr[2],
			rdre->flags,
			rdre->bcnt,
			rdre->mcnt);
	}
#endif
}
#endif



More information about the Comp.unix.aix mailing list