/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:if_lan.c 12.0$ */
/* $ACIS:if_lan.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/caif/RCS/if_lan.c,v $ */
 
#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:if_lan.c 12.0$";
#endif
 
/*
 * IBM Token-Ring Local Area Network Adapter Driver
 */
 
#include "lan.h"
#if NLAN > 0
 
#include "../machine/pte.h"
#include "param.h"
#include "dir.h"
#include "user.h"
#include "mbuf.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "vmmac.h"
#include "ioctl.h"
#include "errno.h"
#include "un.h"
 
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
#include "../netinet/if_ether.h"
#include "../netinet/if_lan.h"
 
#include "../machine/io.h"
#include "../machineio/ioccvar.h"
#include "../machineio/dmareg.h"
#include "../machineio/dmavar.h"
#include "../machine/debug.h"
#include "kernel.h"
 
#include "../machineif/if_lanreg.h"
#include "../machineif/if_landma.h"
#include "../machineif/if_lanvar.h"
#include "../machineif/if_lanio.h"
 
#ifdef DEBUG
char lan_debug = 0x00;			/* controls printf's for debugging */
#endif DEBUG
 
char *clear_dma_reg = (char *)0xf00088c0;
 
int lan_probe(), lan_slave(), lan_attach(), lan_dgo();
int lan_intr(), lan_init(), lan_ioctl(), lan_output(), lan_reset();
int lan_timout();
unsigned short mior();
struct mbuf *lan_get();
 
caddr_t lan_std[] = {
	(caddr_t)0xf00001c0, (caddr_t)0xf0000140,
	(caddr_t)0xf00011c0, (caddr_t)0xf00011d0, 0
};
struct iocc_ctlr *lanc_info[NLAN];
struct iocc_device *lan_info[NLAN];
 
struct iocc_driver lancdriver = {
	lan_probe, lan_slave, lan_attach, lan_dgo, lan_std, 
	"lan", lan_info, "lanc", lanc_info,
	lan_intr, LAN_CMDREG, 0
};
 
int lan_initialize(), lan_ring_watch(), lan_thaw();
struct lan_softc lan_softc[NLAN];
char lan_xid_resp[LAN_L_XID_RESP] = { '\201', '\001', '\002'};
struct buf dmabuf, *bp=&dmabuf;
 
/*
 * lan_probe - adapter does not interrupt in this state
 */
lan_probe(addr)
	register caddr_t addr;
{
	DEBUGF(lan_debug, printf("lan controller probed\n");
	);
	return (PROBE_NOINT);
}
 
/*
 * lan_slave - adapter does not interrupt in this state
 */
lan_slave(addr)
	register caddr_t addr;
{
	DEBUGF(lan_debug, printf("lan adapter probed\n");
	);
	return (PROBE_NOINT);
}
 
/*
 * lan_dgo 
 */
lan_dgo(ic)
	register struct iocc_ctlr *ic;
{
	dma_go(ic->ic_dmachannel);
	return;
}
 
 
/*
 * lan_attach - make interface available to network software if
 *		autoconfig determines that interface exists.
 */
 
lan_attach(iod)
	register struct iocc_device *iod;
{
	register struct lan_softc *lns = &lan_softc[iod->iod_unit];
	register struct ifnet *ifp = &lns->lns_if;
 
	ifp->if_unit = iod->iod_unit;
	ifp->if_name = "lan";
	ifp->if_mtu = LAN_MTU;
	ifp->if_init = lan_init;
	ifp->if_ioctl = lan_ioctl;
	ifp->if_output = lan_output;
	ifp->if_reset = lan_reset;
	ifp->if_flags = IFF_BROADCAST | IFF_SNAP | IFF_BRIDGE;
	if_attach(ifp);
 
	DEBUGF(lan_debug, printf("lan%d attached\n", iod->iod_unit);
	);
}
 
 
/*
 * lan_reset - reset interface
 */
 
lan_reset(unit)
	register unsigned int unit;
{
	register struct iocc_device *iod;
 
	if (unit < NLAN && (iod = lan_info[unit]) != 0 && iod->iod_alive != 0)
		lan_init(unit);
	DEBUGF(lan_debug, printf("lan%d reset\n", unit);
	);
}
 
 
/*
 * lan_init - initialize and open adapter
 */
 
lan_init(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	struct ifnet *ifp = &lns->lns_if;
	register struct lan_device *addr;
 
	*clear_dma_reg = 0;
 
	if (ifp->if_addrlist == (struct ifaddr *)0)
		return;
 
	DEBUGF(lan_debug, printf("lan%d begin init\n", unit);
	);
 
	addr = (struct lan_device *)(lan_info[unit]->iod_addr);
 
	if((lns->lns_if.if_flags & IFF_RUNNING) == 0) {
		/* read node address pointer once */
		lns->lns_adapter &= LAN_ADAP_BIA_READ;
		if (lan_initialize(unit) == 0) {
			lns->lns_if.if_flags |= IFF_RUNNING;
			if (!(lns->lns_adapter & LAN_ADAP_BIA_READ)) {
				lns->lns_ctl->lan_bia.adap_addr = LAN_ADDRESSES;
				lns->lns_ctl->lan_bia.count = LAN_L_ADDR;
				lns->lns_ctl->lan_bia.flag |= LAN_ADDR_PENDING;
				lan_exec(LAN_RDADAPTR, &lns->lns_ctl->lan_bia, addr, unit);
			}
			DEBUGF(lan_debug, printf("lan%d: end init\n", unit);
			);
 
		} else {
			lns->lns_adapter |= LAN_ADAP_BROKEN;
			lns->lns_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
			lns->lns_adapter &= ~LAN_ADAP_CHANNEL;
			dma_done(lns->lan_dma_chan);
		}
	}
	if(lns->lns_if.if_flags & IFF_RUNNING) {
		if((lns->lns_adapter & LAN_ADAP_OPEN) == 0) {
			lan_open(unit, addr);  /* Open adapter */
		}
	}
}
 
 
 /* end lan_init */
 
/*
 * lan_start - start output to the adapter
 */
 
lan_start(lns, addr, xbuf, unit)
	register struct lan_softc *lns;
	register struct lan_device *addr;
	register int xbuf;
	register int unit;
{
 
	lns->lns_oactive = 1;
	lns->lns_xbuf = xbuf;
	DEBUGF(lan_debug,
		printf("lan%d start xmit buf %x length = %x\n",
		unit, xbuf, lns->xp[xbuf]->frame_size);
	);
	/* execute transmit command */
	lan_exec(LAN_TRANSMIT, lns->xp[xbuf], addr, unit);
}
 
 
/*
 * lan_ssb_clear - clear adapter -> system interrupt
 */
 
lan_ssb_clear(addr)
	register struct lan_device *addr;
{
	miow(&(addr->lan_cmdstat), LAN_SSBCLEAR);
}
 
/*
 * lan_recv - get mbufs and initialize receive lists for reception
 *		into mbuf data area
 */
 
lan_recv(unit, addr)
	register int unit;
	register struct lan_device *addr;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_recv_ctl *rcv;
	struct mbuf *m, *p;
	register int tcw;
	int i;
	/*
	 * use an mbuf to hold
	 * receive lists 
	 */
	if(lns->lns_recv == 0) {
		MGET(m, M_DONTWAIT, MT_DATA);
		if (m == 0)
			return(ENOBUFS);
		lns->lns_recv = mtod(m, struct lan_recv_ctl *);
	}
	rcv = lns->lns_recv;
	tcw = lan_dma_setup(rcv, TCW_RESERVE, lns->lan_dma_chan);
	for (i = 0; i < 2; i++) {
		lns->rp[i] = &rcv->lan_rlist[i];
		lns->rp[i]->xlp_h =
			DMA_HI_ADDR(tcw);
	}
	lns->rp[0]->xlp_l =
		DMA_LO_ADDR(lns->rp[1], tcw);
	lns->rp[1]->xlp_l = 
		DMA_LO_ADDR(lns->rp[0], tcw);
	/*
	 * use large mbufs for
	 * receive headers and data
	 */
	for (i = 0; i < 2; i++) {
		if(lns->lns_rdata[i] == 0) {
			MGET(m, M_DONTWAIT, MT_DATA);
			if (m == 0)
				return(ENOBUFS);
			MCLALLOC(p, 1);
			if (p == 0)
				return(ENOBUFS);
			else
				m->m_off = (int)p - (int)m;
			lns->lns_rbufp[i] = m;
			lns->lns_rdata[i] = p;
		}
		tcw = lan_dma_setup(lns->lns_rdata[i],TCW_RESERVE, lns->lan_dma_chan);
		lns->lns_mac[i] = (struct mac_hdr *)(lns->lns_rdata[i]);
		/* set up lan header areas */
		lns->rp[i]->d_parm[0].d_cnt =
			(((short)(sizeof(struct mac_hdr)  +
			sizeof(struct llc_hdr) + LAN_MTU)) &
			(~LAN_CHAIN));
		lns->rp[i]->d_parm[0].d_haddr = DMA_HI_ADDR(tcw);
		lns->rp[i]->d_parm[0].d_laddr = DMA_LO_ADDR(lns->lns_rdata[i], tcw);
		lns->rp[i]->cstat = LAN_R_CSTAT_REQ;
	}
	return(0);
}
 
/*
 * lan_exec - serialize command requests to the adapter;
 *		do one at a time for now except for
 *		receive which needs to be outstanding.
 */
lan_exec(cmd, plist, addr, unit)
	register short cmd;
	register u_short *plist;
	register struct lan_device *addr;
	register int unit;
{
	struct lan_softc *lns = &lan_softc[unit];
	register struct lan_ctl *ctl = lns->lns_ctl;
	register int i;
	int tcw;
	int s = splimp();
 
	tcw = lan_dma_setup(plist, TCW_RESERVE, lns->lan_dma_chan);
	for (i = 5*ONESEC; i>0; i--) {
		DELAY(1);
		if (ctl->lan_scb.command == 0)  /* wait until scb cleared */
			break;
	}
	if(i>0) {
		ctl->lan_scb.command = cmd;
		ctl->lan_scb.h_addr = DMA_HI_ADDR(tcw);
		ctl->lan_scb.l_addr = DMA_LO_ADDR(plist, tcw);
		DEBUGF(lan_debug,
			printf("lan%d exec cmd %x, addr %x %x\n",
				unit, ctl->lan_scb.command,
				ctl->lan_scb.h_addr, ctl->lan_scb.l_addr);
		);
		miow(&(addr->lan_cmdstat), LAN_EXECUTE);
	} else printf("lan%d: adapter jammed\n",unit);
	splx(s);
}
 
/*
 * lan_open - open adapter
 */
 
lan_open(unit, addr)
	register int unit;
	register struct lan_device *addr;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_ctl *ctl = lns->lns_ctl;
 
	if((lns->lns_adapter & LAN_OPEN_IN_PROGRESS) == 0) {
		lns->lns_adapter |= LAN_OPEN_IN_PROGRESS;
		ctl->open_parm[0] = LAN_OPEN_OPTIONS;
		ctl->open_parm[1] = ctl->open_parm[2] = ctl->open_parm[3] = 0;
		ctl->open_parm[4] = ctl->open_parm[5] =
			ctl->open_parm[6] = ctl->open_parm[7] = 0;
		ctl->open_parm[8] = LAN_OPEN_RLIST;
		ctl->open_parm[9] = LAN_OPEN_XLIST;
		ctl->open_parm[10] = LAN_OPEN_BUFSIZE;
		ctl->open_parm[11] = LAN_OPEN_RAMSTART;
		ctl->open_parm[12] = LAN_OPEN_RAMEND;
		ctl->open_parm[13] = LAN_OPEN_XMINMAX;
 
		lan_exec(LAN_OPEN, ctl->open_parm, addr, unit);
		DEBUGF(lan_debug,
			printf("lan%d attempting to open addr %x %x %x\n",
				unit, ctl->open_parm[1],
				ctl->open_parm[2], ctl->open_parm[3]);
		);
	}
}
 
 
/*
 * lan_intr - adapter interrupt handler
 */
 
lan_intr(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_device *addr = (struct lan_device *)lan_info[unit]->iod_addr;
	struct lan_ctl *ctl = lns->lns_ctl;
	register struct mbuf *m;
	register short sifrbuf, sifwbuf;
	struct ifqueue *inq;
	struct mbuf *p;
	char type, *c;
	unsigned short len;
	register int next_buf;
	int k, i, tcw;
	short retparm[4];
	short *retptr;
	unsigned short *initptr, *shortptr;
	unsigned short open_phase, open_status;
	int alt, ri_len, mac_hdr_len, layer1_hdr_len, snap_len;
	int s;
 
	sifrbuf = (mior(&(addr->lan_cmdstat)));
	if ((sifrbuf & LAN_INT) == 0) {
		return (1);
	}
	sifrbuf &= LAN_ADAP_INT;	/* interrupt type */
	DEBUGF(lan_debug,
		printf("lan%d int: status = 0x%b, code = 0x%x, ssb = %x,%x,%x,%x\n",
			unit, sifrbuf & 0xfff0, LAN_STAT_BITS, sifrbuf & 0x000f,
			ctl->lan_ssb.command, ctl->lan_ssb.status0,
			ctl->lan_ssb.status1, ctl->lan_ssb.status2);
	);
 
	switch (sifrbuf) {
	case LAN_RECVSTAT:
		/*
		 * Receive notify
		 *
		 * When frame complete, copy data to mbuf chain.
		 * Signal adapter to continue.  Then either enqueue IP 
		 * packets or process arp packets.
		 */
 
		/* determine buffer(s) from receive list address */
		if ((ctl->lan_ssb.status1 == lns->rp[1]->xlp_h) &&
			(ctl->lan_ssb.status2 == lns->rp[1]->xlp_l))
			i = 0;
			else i = 1;
		if (ctl->lan_ssb.status0 == LAN_FRAME_COMPLETE) {
			DEBUGF(lan_debug,
				printf("lan%d recv: status=0x%b, cstat=0x%b, length=%x\n",
					unit, ctl->lan_ssb.status0, LAN_RECV_BITS,
					lns->rp[i]->cstat, LAN_RCSTAT_BITS,
					lns->rp[i]->frame_size);
			);
		lan_ssb_clear(addr);
		alt = 0;
		if(lns->rp[1-i]->cstat & LAN_RCSTAT_COMPLETE) {
			alt=LAN_RECV_ALT;
			i=1-i;
		}
		check_alt:
			lns->lns_if.if_ipackets++;
			/* check for routing info 
			and determine header lengths */
			if (lns->lns_mac[i]->from_addr[0] & LAN_RI_PRESENT) {
				ri_len = (lns->lns_mac[i]->rcf & 
					LAN_RCF_LEN_MASK) >> 8;
			}
			else
				ri_len = 0;
			mac_hdr_len = MIN_MAC_HDR_SIZE + ri_len;
			lns->lns_llc[i] = (struct llc_hdr *)((int)
				(lns->lns_rdata[i]) + 
				mac_hdr_len);
			type = lns->lns_llc[i]->dsap;
			if (type == LAN_SNAP_SAP) {
				lns->lns_snap[i] = (struct snap_hdr *) ((int)
					(lns->lns_llc[i]) + 
					sizeof (struct llc_hdr));
				snap_len = sizeof(struct snap_hdr);
				if (lns->lns_snap[i]->ether_type[1] ==
					LAN_SNAP_IPTYPE) {
					type = LAN_IPTYPE;
				}
				else if(lns->lns_snap[i]->ether_type[1] ==
					LAN_SNAP_ARPTYPE) {
					type = LAN_ARPTYPE;
				}
			}
			else 
				snap_len = 0;
			layer1_hdr_len = mac_hdr_len + sizeof(struct llc_hdr)
				+ snap_len;
			len = lns->rp[i]->frame_size - layer1_hdr_len;
			if (len == 0)
				goto chk_xid;
			p = (struct mbuf *)((int)(lns->lns_rdata[i]) + 
				layer1_hdr_len);
			m = lan_get(p, len, &lns->lns_if);
			if (m == 0)
				goto clear;
		chk_xid:
			if((lan_xid_test(m, i, unit)) || (len == 0))
				goto clear;
 
			switch (type) {
#ifdef INET
			case LAN_IPTYPE:
				schednetisr(NETISR_IP);
				inq = &ipintrq;
				break;
			case LAN_ARPTYPE:
				tr_arpinput(&lns->lns_ac, m,lns->lns_mac[i]);
				goto clear;
#endif
			default:
				DEBUGF(lan_debug,
					printf("lan%d packet not recognized, freeing mbuf\n",
						unit);
				);
				m_freem(m);
				goto clear;
			}
			s = splimp();
#ifndef NSS
			if (IF_QFULL(inq)) {
				DEBUGF(lan_debug, printf("lan%d: ip qfull\n", unit);
				);
				IF_DROP(inq);
				m_freem(m);
			} else {
				IF_ENQUEUE(inq, m);
			}
#else
			IF_ENQUEUEIF(inq, m);
#endif NSS
			splx(s);
 
		}
		else lan_ssb_clear(addr);
		clear:
		lns->rp[i]->cstat = LAN_R_CSTAT_REQ;
		miow(&(addr->lan_cmdstat), LAN_RECVALID);
		if (alt) {
			alt=0;
			i=1-i;
			goto check_alt;
		}
		break;
 
	case LAN_XMITSTAT:
		/*
		 * Transmit status
		 *
		 * If next buffer full, start output.
		 * Dequeue next IP buffer.
		 */
 
		lns->lns_if.if_opackets++;
 
		/*
		 * First, figure out which buffer just finished.
		 */
		if ((ctl->lan_ssb.status1 == lns->xp[1]->xlp_h) &&
			(ctl->lan_ssb.status2 ==
					(lns->xp[1]->xlp_l & ~LAN_ODD_PTR))) {
			next_buf = 1;
		} else {
			next_buf = 0;
		}
		/*
		 * Do the ssb_clear now, just in case adapter needs it
		 * Don't do it any earlier, since the above computation
		 * of next_buf uses data from the ssb.
		 */
		lan_ssb_clear(addr);
		/*
		 * Now, fill in all the buffers.  If next_buf has XMIT_VALID,
		 * then it was not transmitted, so don't fill IT in now.
		 */
		if (lns->lns_if.if_snd.ifq_head) {
			struct mbuf *m;
 
			IF_DEQUEUE(&lns->lns_if.if_snd, m);
			if (lns->xp[next_buf]->cstat&LAN_XMIT_VALID) {
				/*
				 * After the lan_put, two buffers will be full,
				 * and next_buf is the index of the first.
				 */
				if (lan_put(lns, m, 1 - next_buf, unit) != 0) {
					m_freem(m);
				}
			} else {		/* can fill up to 2! */
				/* After the lan_put, only next_buf is full. */
				if (lan_put(lns, m, next_buf, unit) != 0) {
					m_freem(m);
				}
				if (lns->lns_if.if_snd.ifq_head) {
					IF_DEQUEUE(&lns->lns_if.if_snd, m);
					/*
					 * After the lan_put, two buffers
					 * will be full, and next_buf is the
					 * index of the first.
					 */
					if (lan_put(lns, m,
						    1 - next_buf, unit) != 0) {
						m_freem(m);
					}
				}
			}
		}
		/* If ANY buffer is full, then next_buf will be. */
		if (lns->xp[next_buf]->cstat & LAN_XMIT_VALID) {
			lan_start(lns, addr, next_buf, unit);
		} else {
			lns->lns_oactive = 0;
			/*
			 * At this point, there had better NOT be any
			 * data on the send queue, because if there IS,
			 * it will never be sent.
			 */
			if (lns->lns_if.if_snd.ifq_head) {
				printf("lan%d:  Send queue not empty!\n");
			}
		}
		break;
 
	case LAN_ACHECK:
		/*
		 * Adapter check - Retry adapter initialization
		 */
		lns->lns_adapter |= LAN_ADAP_BROKEN;
		lns->lns_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
		sifwbuf = LAN_ACHECK_DATA;
		shortptr = &(addr->lan_address);
		initptr = &(addr->lan_data);
		for (k = ADAP_CHK_SIZE, retptr = ((short *)retparm); k > 0; k--, retptr++) {
			miow(shortptr, sifwbuf);
			*retptr = mior(initptr);
			sifwbuf = sifwbuf + 2;
		}
		printf("lan%d: unrecoverable token ring adapter failure, ", unit);
		printf("adapter check field = 0x%b, parm0=0x%x, parm1=0x%x, parm2=0x%x\n",
			retparm[0], LAN_ACHECK_BITS, retparm[1], retparm[2], retparm[3]);
		if (lns->lns_freezer != 0) {
			lan_freeze(unit); /* freeze the adapter */
			wakeup(lns);
			timeout(lan_thaw, (caddr_t)unit, 180 * hz);
		} else {
			lan_ssb_clear(addr);
			timeout(lan_reset, (caddr_t)unit, 1 * hz);
		}
		if (retparm[0] & (XMIT_PARITY || XMIT_UNDERRUN)) {
			lns->lns_if.if_oerrors++;
		}
		if (retparm[0] & (RECV_PARITY || RECV_OVERRUN)) {
			lns->lns_if.if_ierrors++;
		}
		break;
 
	case LAN_RINGSTAT:
		/*
		 * Ring status 
		 *
		 * Processing depends on ring status.  For
		 * ring error set timer to check again in 60
		 * seconds to allow for possible ring
		 * recovery.  Certain statuses update adapter
		 * state.  Others informational only.
		 */
 
		DEBUGF(lan_debug,
			printf("lan%d ring status = %b\n",
				unit, ctl->lan_ssb.status0, LAN_RING_BITS);
		);
		if (ctl->lan_ssb.status0 & LAN_AUTOER1) {
			lns->lns_adapter |= LAN_ADAP_AUTOER1;
			lns->lns_if.if_flags &= ~IFF_UP;
			lan_ssb_clear(addr);
			lan_open(unit, addr);
			break;
		} else if (ctl->lan_ssb.status0 & LAN_REMOVE_RECV) {
			lns->lns_adapter &= ~LAN_ADAP_OPEN;
			lns->lns_adapter |= LAN_ADAP_DOWN;
			lns->lns_if.if_flags &= ~IFF_UP;
			printf("lan%d: removed from network\n", unit);
		} else if (ctl->lan_ssb.status0 & LAN_WIRE_FAULT) {
			printf("lan%d: cable failure\n", unit);
			lan_ssb_clear(addr);
			lns->lns_adapter &= ~LAN_ADAP_OPEN;
			lns->lns_if.if_flags &= ~IFF_UP;
			lns->lns_ring |= LAN_CABLE_FAIL;
			if(!lns->lns_ring_watch) {
				lns->lns_ring_watch++;
				timeout(lan_ring_watch, (caddr_t)unit, SIXTY * hz);
			}
			break;
		} else if ( ctl->lan_ssb.status0 &
			(LAN_SIGNAL_LOSS | LAN_HARD_ERROR | LAN_XMIT_BEACON)
			) {
			if (lns->lns_ring & LAN_RECOVERY)
				lns->lns_ring = ctl->lan_ssb.status0 | LAN_RECOVERY;
			else {
				lns->lns_ring |= LAN_RECOVERY;
				if (ctl->lan_ssb.status0 & LAN_XMIT_BEACON)
					printf("lan%d: beaconing\n", unit);
				if(!lns->lns_ring_watch) {
					lns->lns_ring_watch++;
					timeout(lan_ring_watch, (caddr_t)unit, SIXTY * hz);
				}
			}
		}
		lns->lns_ring &= ~LAN_RECOVERY;
		if (ctl->lan_ssb.status0 & LAN_SINGLE) {
			printf("lan%d: single station on network\n", unit);
		}
		lan_ssb_clear(addr);
		break;
 
 
	case LAN_CMDSTAT:
		/*
		 * Command status 
		 *
		 * On open, examine completion status.  Set
		 * adapter state and, if failure, print
		 * informational message and retry open if error
		 * not permanent.  If success, set up transmit
		 * lists and begin output.
		 */
 
		DEBUGF(lan_debug, printf("lan%d cmd stat: %x %x %x %x\n",
			unit, ctl->lan_ssb.command, ctl->lan_ssb.status0,
			ctl->lan_ssb.status1, ctl->lan_ssb.status2);
		);
		if (ctl->lan_ssb.command == LAN_OPEN) {
			if (ctl->lan_ssb.status0 == LAN_OPEN_COMPLETE) {
				lan_ssb_clear(addr);
				DEBUGF(lan_debug, printf("lan%d open complete\n", unit);
				);
				lns->lns_adapter &= LAN_ADAP_BIA_READ;
				lns->lns_adapter |= LAN_ADAP_OPEN;
				lns->lns_ring &= ~(
					LAN_RECOVERY |
					LAN_XMIT_BEACON |
					LAN_CABLE_FAIL);
				ctl->lan_bia.adap_addr = lns->lns_node_addr;
				ctl->lan_bia.count = LAN_L_ADDR;
				lan_exec(LAN_RDADAPTR, (u_short *)&ctl->lan_bia, addr, unit);
			} else {
				lns->lns_adapter &=
					~(LAN_ADAP_OPEN | LAN_OPEN_IN_PROGRESS);
				/* adapter open error */
				lns->lns_if.if_flags &= ~IFF_UP;
				DEBUGF(lan_debug,
					printf("lan%d open failure, status = 0x%b, error = 0x%x\n",
						unit, ctl->lan_ssb.status0 & 0xff00,
						LAN_OPEN_STAT_BITS,
						ctl->lan_ssb.status0 & 0x00ff);
				);
				open_phase =
					ctl->lan_ssb.status0 & ~LAN_OPEN_PHASE_MASK;
				open_status =
					ctl->lan_ssb.status0 & LAN_OPEN_PHASE_MASK;
				lan_ssb_clear(addr);
				if (open_status & LAN_OPEN_NODE_ERROR) {
					printf("lan%d: node address error\n", unit);
					lns->lns_adapter |= LAN_ADAP_DOWN;
					break;
				} else if (lns->lns_adapter & LAN_ADAP_AUTOER1) {
					lns->lns_adapter &= ~LAN_ADAP_AUTOER1;
					printf("lan%d: hardware error, ", unit);
					printf("adapter removed from ring\n");
					break;
				}
					printf("lan%d: open err=0x%x",unit, open_status);
					printf(" %s\n", open_errmsg[open_status & 0x0f]);
					switch (open_status) {
					case LAN_OPEN_TIMEOUT:
					case LAN_OPEN_OREQ_PARM:
					case LAN_OPEN_OIMPL:
						if(lns->lns_open_retries < LAN_MAX_OPEN_RETRY)
						{
							lns->lns_open_retries++;
							lan_open(unit,addr);
						}
						else lns->lns_open_retries = 0;
						break;
					case LAN_OPEN_FUNC_FAILURE:
						if ((open_phase == LAN_OPEN_LOBE_TEST) || (open_phase == LAN_OPEN_INSERTION))
						{
							lns->lns_ring |= LAN_CABLE_FAIL;
							if(!lns->lns_ring_watch) {
								lns->lns_ring_watch++;
								timeout(lan_ring_watch, (caddr_t)unit, SIXTY * hz);
							}
						}
						else
						if(lns->lns_adapter & LAN_ADAP_FCTNFAIL == 0)
						{
							lns->lns_adapter |= LAN_ADAP_FCTNFAIL;
							lan_open(unit,addr);
						}
						break;
					case LAN_OPEN_OSIGNAL_LOSS:
					case LAN_OPEN_RING_FAILURE:
					case LAN_OPEN_RING_BEACON:
						if(!lns->lns_ring_watch) {
							lns->lns_ring_watch++;
							timeout(lan_ring_watch, (caddr_t)unit, SIXTY * hz);
						}
					}
			}
		} else if (ctl->lan_ssb.command == LAN_RDADAPTR) {
			lan_ssb_clear(addr);
			if (ctl->lan_bia.flag & LAN_ADDR_PENDING) {
				lns->lns_node_addr = ctl->lan_bia.count;
				ctl->lan_bia.flag &= ~LAN_ADDR_PENDING;
			} else {
				bcopy(&ctl->lan_bia.count, lns->lns_addr, LAN_L_ADDR);
				printf ("lan%d: address ",unit);
				c = (char *)lns->lns_addr;
				for (i = 0; i < LAN_L_ADDR; i++) {
					if (i != 0) printf (":");
					printf ("%x", *c++);
				}
				printf("\n");
				/* allocate transmit list buffers very first time */
				if ((lns->lns_adapter & LAN_ADAP_BIA_READ) == 0) {
					/* allocate xmit lists */
					for (i = 0; i < LAN_XMITLIST_CT; i++) {
						MGET(m, M_DONTWAIT, MT_DATA);
						if (m == 0)
							goto lan_mbuf_fail;
						MCLALLOC(p, 1);
						if (p == 0)
							goto lan_mbuf_fail;
						else
							m->m_off = (int)p - (int)m;
						lns->xp[i] = (struct lan_list *)p;
						lns->lns_xbufp[i] = (char *) ((int)lns->xp[i] + sizeof(struct lan_list));
						tcw = lan_dma_setup(lns->lns_xbufp[i],TCW_RESERVE, lns->lan_dma_chan);
						lns->xp[i]->d_parm[0].d_haddr = DMA_HI_ADDR(tcw);
						lns->xp[i]->d_parm[0].d_laddr = DMA_LO_ADDR(lns->lns_xbufp[i], tcw);
					}
					lns->lns_oactive = 0;
					lns->lns_adapter |= LAN_ADAP_BIA_READ;
				} 
				for (i = 0; i < LAN_XMITLIST_CT; i++) {
					tcw = lan_dma_setup(lns->xp[i],TCW_RESERVE,lns->lan_dma_chan);
					lns->xp[1-i]->xlp_h =
						DMA_HI_ADDR(tcw);
					lns->xp[1-i]->xlp_l =
						(DMA_LO_ADDR(lns->xp[i], tcw)) | LAN_ODD_PTR;
					lns->xp[i]->cstat = LAN_XCSTAT_COMPLETE;
				}
				untimeout(lan_timout, unit);
				wakeup(&lns->lansleep_event);
				/* reset receive lists */
				if (lan_recv(unit, addr) != 0) goto lan_mbuf_fail;
				lan_exec(LAN_RECEIVE, (u_short *)lns->rp[0], addr, unit);
				lns->lns_if.if_flags |= IFF_UP;
				if (lns->lns_if.if_snd.ifq_head) {
					struct mbuf *m;
 
					IF_DEQUEUE(&lns->lns_if.if_snd, m);
					if (lan_put(lns, m, 0, unit) == 0) {
						lan_start(lns, addr, 0, unit);
						if (lns->lns_if.if_snd.ifq_head) {
							IF_DEQUEUE(&lns->lns_if.if_snd,
								m);
							if (lan_put(lns, m, 1, unit) != 0)
								m_freem(m);
						}
					} else
						m_freem(m);
				}
			}
		} else if (ctl->lan_ssb.command == LAN_CLOSE) {
			if (ctl->lan_ssb.status0 == LAN_CLOSE_COMPLETE) {
				lns->lns_adapter |= LAN_ADAP_DOWN;
				lns->lns_adapter &= ~LAN_ADAP_CHANNEL;
				dma_done(lns->lan_dma_chan);
				DEBUGF(lan_debug, printf("lan%d closed\n", unit);
				);
			}
			lan_ssb_clear(addr);
		} else {
			if (ctl->lan_ssb.command == LAN_SSB_REJECT)
				printf("lan%d: command reject, internal software error, ",
					unit);
				printf("reject reason = 0x%b, command = 0x%x.\n",
					ctl->lan_ssb.status0, LAN_CMD_REJ_BITS,
					ctl->lan_ssb.status1);
			lan_ssb_clear(addr);
		}
		break;
lan_mbuf_fail:
		printf("lan%d: mbufs.\n", unit);
		break;
 
 
	default:
		DEBUGF(lan_debug,
			printf("lan%d unexpected interrupt: status = 0x%b, code = 0x%x\n",
				unit, sifrbuf & 0xfff0, LAN_STAT_BITS, sifrbuf & 0x000f);
		);
		lan_ssb_clear(addr);
		break;
	}
	/* end switch */
 
	return (0);
}
 
 
/*
 * lan_ioctl - adapter ioctl
 */
lan_ioctl(ifp, cmd, data)
	register struct ifnet *ifp;
	register int cmd;
	register caddr_t data;
{
	register struct ifaddr *ifa = (struct ifaddr *)data;
	register struct ifreq *ifr = (struct ifreq *)data;
	register int s = splimp();
	register int error = 0;
	register struct lan_softc *lns = &lan_softc[ifp->if_unit];
	struct lan_device *addr = (struct lan_device *)
	lan_info[ifp->if_unit]->iod_addr;
	unsigned short *dump, dump_data;
	int i, j;
 
	switch (cmd) {
	case SIOCSIFADDR:
		if ((ifp->if_flags & IFF_RUNNING) == 0) {
			lan_init(ifp->if_unit);
			lan_sleep(ifp->if_unit);
		}
		if (ifp->if_flags & IFF_RUNNING) {
 
			switch (ifa->ifa_addr.sa_family) {
#ifdef	INET
				case AF_INET:
					((struct arpcom *)ifp)->ac_ipaddr =
						IA_SIN(ifa)->sin_addr;
					if (ifp->if_flags & IFF_UP) {
						arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
					}
					break;
#endif	INET
			}
		}
		break;
	case SIOCSIFFLAGS:
		if((ifr->ifr_flags & IFF_UP)
			&& ((lns->lns_adapter & LAN_ADAP_OPEN) == 0)) {
			lan_init(ifp->if_unit);
			if(ifp->if_addrlist != (struct ifaddr *) 0) {
				lan_sleep(ifp->if_unit);
			}
		} else if ((ifr->ifr_flags & IFF_UP) == 0) {
				if (lns->lns_adapter & LAN_ADAP_OPEN) {
					lan_close(ifp->if_unit);
			        } else if (lns->lns_ring & LAN_CABLE_FAIL) {
					lns->lns_adapter |= LAN_ADAP_DOWN;
					}
			}
		break;
	case SIOCSLANDUMP:
		lns->lns_freezer = u.u_procp;
		sleep(lns,PZERO+1);
		break;
	case SIOCFLANDUMP:
		lan_freeze(ifp->if_unit); /* freeze the adapter */
		timeout(lan_thaw, ifp->if_unit, 180 * hz);
		break;
	case SIOCGLANDUMP:
		if (lns->lns_adapter & LAN_ADAP_FROZEN) {
			dump = ((struct lan_dump *)ifr->ifr_data)->lan_dump_data;
			for (j = 0; j < LAN_FREEZE_DUMP / LAN_FREEZE_CHUNK; j++) {
				if (j) {
					miow(&(addr->lan_cmdstat), LAN_FREEZE_INCR);
					DELAY(LAN_ADAP_MIN_RESET);
				}
				miow(&(addr->lan_address), 0);
				for (i = 0; i < LAN_FREEZE_CHUNK / 2; i++, dump++) {
					dump_data = mior(&(addr->lan_datai));
					if(copyout( (caddr_t)&dump_data,
						 (caddr_t)dump,sizeof(short)) !=0) {
						goto dump_done;
					}
				}
			}
		dump_done:
			lan_unfreeze(ifp->if_unit);
			timeout(lan_reset, ifp->if_unit, 1 * hz);
		}
		break;
	default:
		error = EINVAL;
	}
	splx(s);
	return (error);
}
 
 
/*
 * lan_output - token ring output routine
 */
lan_output(ifp, m0, dst)
	register struct ifnet *ifp;
	register struct mbuf *m0;
	struct sockaddr *dst;
{
	int error;
	char type;
	u_char edst[LAN_L_ADDR];
	struct in_addr idst;
	register struct lan_softc *lns = &lan_softc[ifp->if_unit];
	register struct mbuf *m = m0;
	register struct ether_header *un;
	extern struct ifnet loif;
	int usetrailers; /* not used */
	u_short rcf = 0; 	
	u_short rseg[LAN_MAX_BRIDGE];
 
	DEBUGF(lan_debug, printf("lan%d output request\n", ifp->if_unit);
	);
 
	switch (dst->sa_family) {
#ifdef INET
	case AF_INET:
		idst = ((struct sockaddr_in *)dst)->sin_addr;
		if (!tr_arpresolve(&lns->lns_ac, m, &idst, edst,&usetrailers,&rcf,rseg))
			return (0);
		type = LAN_IPTYPE;
		if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, sizeof(edst))) {
			if (lns->lns_if.if_flags & IFF_BRIDGE) {
				rcf = (((sizeof(rcf)) << 8) & LAN_RCF_LEN_MASK)
					| LAN_RCF_FRAME2K
					| LAN_RCF_BROADCAST; /* all rings broadcast */
			}
		}
		break;
#endif
	case AF_UNSPEC:
		un = (struct ether_header *)dst->sa_data;
		bcopy((caddr_t)un->ether_dhost, (caddr_t)edst, sizeof(edst));
		type = LAN_ARPTYPE;
		if (lns->lns_if.if_flags & IFF_BRIDGE) {
			rcf = (((sizeof(rcf)) << 8) & LAN_RCF_LEN_MASK)
				| LAN_RCF_FRAME2K
				| LAN_RCF_BROADCAST; /* all rings broadcast */
		}
		else {
			rcf = 0;
		}
		break;
	default:
		printf("lan%d: can't handle af%d.\n", ifp->if_unit, dst->sa_family);
		error = EAFNOSUPPORT;
		goto bad;
	}
	return (lan_output_llc(ifp, m0, edst, rcf, rseg, LAN_UI_CMD, type));
bad:
	m_freem(m0);
	return (error);
}
 
 
lan_output_llc(ifp, m0, dst, rcf, rseg, llc_ctl, type)
	register struct ifnet *ifp;
	register struct mbuf *m0;
	u_char * dst;
	u_short rcf;
	u_short rseg[LAN_MAX_BRIDGE];
	char llc_ctl;
	char type;
{
	int s, ri_len, snap_len, i;
	int error = 0;
	struct lan_device *addr = (struct lan_device *)
	lan_info[ifp->if_unit]->iod_addr;
	register struct lan_softc *lns = &lan_softc[ifp->if_unit];
	register struct mbuf *m = m0;
	register struct mac_hdr *mac;
	register struct llc_hdr *llc;
	struct snap_hdr *snap;
	extern struct ifnet loif;
#ifdef NSS
	char *packet;
 
	packet = mtod(m, char *);
#endif NSS
	/* Add token-ring header */
 
	m = m_get(M_DONTWAIT, MT_HEADER);
	if (m == 0) {
		error = ENOBUFS;
		goto bad;
	}
	m->m_next = m0;
	m->m_off = MMINOFF;
	mac = mtod(m, struct mac_hdr *);
	mac->pcf0 = LAN_PCF0;
	mac->pcf1 = LAN_PCF1;
	bcopy((caddr_t)dst, (caddr_t)mac->to_addr, LAN_L_ADDR);
	bcopy((caddr_t)lns->lns_addr, (caddr_t)mac->from_addr, LAN_L_ADDR);
	if (rcf) {
		mac->from_addr[0] |= LAN_RI_PRESENT;
		mac->rcf = rcf;
		ri_len = (rcf & LAN_RCF_LEN_MASK) >> 8;
		bcopy ((caddr_t) rseg, mac->rseg, (ri_len - sizeof(rcf)));
	}
	else ri_len = 0;
	llc = (struct llc_hdr *)((int)(mac) + MIN_MAC_HDR_SIZE + ri_len);
	if (lns->lns_if.if_flags & IFF_SNAP) {
		llc->dsap = LAN_SNAP_SAP;
		llc->ssap = LAN_SNAP_SAP;
		snap_len = sizeof(struct snap_hdr);
		snap = (struct snap_hdr *)((int)(llc) + sizeof(struct llc_hdr));
		for (i=0; i<LAN_NPROT_ID; i++) {
			snap->prot_id[i] = 0;
		}
		snap->ether_type[0] = LAN_SNAP_TYPE;
		switch (type) {
		case LAN_IPTYPE:
			snap->ether_type[1] = LAN_SNAP_IPTYPE;
			break;
		case LAN_ARPTYPE:
			snap->ether_type[1] = LAN_SNAP_ARPTYPE;
			break;
		default:
			break;
		}
	}
	else {
		llc->dsap = type;
		llc->ssap = type;
		snap_len = 0;
	}
	if(llc_ctl != LAN_UI_CMD)
		llc->ssap |= 0x01;
	llc->llc_ctl = llc_ctl;
	m->m_len = MIN_MAC_HDR_SIZE + ri_len + sizeof(struct llc_hdr) + snap_len;
	s = splimp();
#ifndef NSS
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
		error = ENOBUFS;
		goto qfull;
	}
#endif NSS
	if (!(lns->lns_adapter & (LAN_ADAP_BROKEN | LAN_ADAP_DOWN))) {
		if (lns->lns_adapter & LAN_ADAP_OPEN) {
			int next_buf=lns->lns_nextbuf;
			if(lns->xp[next_buf]->cstat & LAN_XCSTAT_COMPLETE) {
				/* Check interface queue for packets to transmit */
				/* before this one */  
				if (ifp->if_snd.ifq_head) {
					IF_ENQUEUE(&ifp->if_snd, m); 
					IF_DEQUEUE(&ifp->if_snd, m); 
				}
				if (lan_put(lns, m, next_buf, ifp->if_unit) == 0) {
					DEBUGF(lan_debug,
						printf("lan%d xmit buf %x filled\n",
							ifp->if_unit, next_buf);
					);
					if (lns->lns_oactive == 0)
						lan_start(lns, addr, next_buf, ifp->if_unit);
				} else
					m_freem(m);
			} else
#ifdef NSS
				if((if_enqueue(&ifp->if_snd, m, ifp, packet))
					< 0) 
					error = ENOBUFS;
#else
				IF_ENQUEUE(&ifp->if_snd, m);
#endif NSS
		} else {
			if ((lns->lns_adapter & LAN_OPEN_IN_PROGRESS) == 0) {
				lan_open(ifp->if_unit, addr);
			}
#ifdef NSS
			if((if_enqueue(&ifp->if_snd, m, ifp, packet))
				< 0) 
				error = ENOBUFS;
#else
			IF_ENQUEUE(&ifp->if_snd, m);
#endif NSS
		}
	} else
		m_freem(m);
 
	splx(s);
	return (error);
#ifndef NSS
qfull:
	m0 = m;
	splx(s);
	DEBUGF(lan_debug, printf("lan%d IP output queue full\n", ifp->if_unit);
	);
#endif NSS
bad:
	m_freem(m0);
	return (error);
}
 
 
/*
 * lan_ring_watch
 *
 * This routine is entered upon expiration of the 60-second interval
 * timer set to examine ring state and print status message.
 * If necessary, attempt to reopen the adapter.
 */
 
lan_ring_watch(unit)
	register int unit;
{
	register struct lan_device *addr = (struct lan_device *)lan_info[unit]->iod_addr;
	register struct lan_softc *lns = &lan_softc[unit];
	int s;
 
	if (lns->lns_ring & LAN_RECOVERY) {
		if (lns->lns_ring & LAN_XMIT_BEACON)
			printf("lan%d: beaconing\n", unit);
	}
	s = splimp();
	if ((lns->lns_adapter & (
		LAN_OPEN_IN_PROGRESS |
		LAN_ADAP_OPEN |
		LAN_ADAP_BROKEN |
		LAN_ADAP_DOWN
	)) == 0) {
		lan_open(unit, addr);
		timeout(lan_ring_watch, (caddr_t)unit, SIXTY * hz);
	}
	else lns->lns_ring_watch = 0;
	splx(s);
}
 
/*
 * lan_put - copy mbufs into large xmit mbuf already mapped for dma operation
 */
 
lan_put(lns, m, xbuf, unit)
	struct lan_softc *lns;
	struct mbuf *m;
	int xbuf;
	int unit;
{
	struct lan_device *addr = (struct lan_device *)lan_info[unit]->iod_addr;
	struct mbuf *mp;
	register struct lan_list *xcp;
	register int total_length = 0;
	int s;
	register char *bp;
 
	xcp = lns->xp[xbuf];
	xcp->xlp_l |= LAN_ODD_PTR;
 
	bp = ((char *) lns->lns_xbufp[xbuf]);
	for (mp = m; mp; mp = mp->m_next) {
		register int len = mp->m_len;
 
		bcopy(mtod(mp, char *), bp, len);
		bp += len;
		total_length += len;
	}
	xcp->frame_size = total_length;
	xcp->d_parm[0].d_cnt = total_length & (~LAN_CHAIN);
	lns->lns_nextbuf = 1-xbuf;
	s = splimp();
	xcp->cstat = LAN_X_CSTAT_REQ;
	if(lns->lns_oactive) {
		lns->xp[1-xbuf]->xlp_l &= ~LAN_ODD_PTR;
		miow(&(addr->lan_cmdstat), LAN_XMTVALID);
	}
	splx(s);
	m_freem(m);
	return (0);
}
 
/*
 * lan_get - copy from driver receive buffers into mbuf's
 */
struct mbuf *lan_get(faddr, totlen, ifp)
	u_char * faddr;
	register unsigned short totlen;
	struct ifnet *ifp;
{
	register struct mbuf *m;
	struct mbuf *top = 0;
	register struct mbuf **mp = &top;
	register unsigned short len;
	register u_char * cp;
 
	cp = faddr;
	while (totlen > 0) {
		char *mcp;
		MGET(m, M_DONTWAIT, MT_DATA);
		if (m == 0)
			goto bad;
		len = totlen;
		if (ifp)
			len += sizeof(ifp);
		if (len >= CLBYTES) {
			register struct mbuf *p;
 
			MCLALLOC(p, 1);
			if (p != 0) {
				m->m_len = len = CLBYTES;
				m->m_off = (int)p - (int)m;
			} else {
				m->m_len = len = MIN(MLEN, len);
				m->m_off = MMINOFF;
			}
		} else {
			m->m_len = len = MIN(MLEN, len);
			m->m_off = MMINOFF;
		}
		mcp = mtod(m, char *);
		if (ifp){
			/* prepend ifp to first mbuf */
			bcopy((char *)&ifp, mcp, sizeof(ifp));
			mcp += sizeof(ifp);
			len -= sizeof(ifp);
			ifp = (struct ifnet *) 0;
		}
		bcopy(cp, mcp, len);
		cp += len;
		*mp = m;
		mp = &m->m_next;
		totlen -= len;
	}
	return (top);
 
bad:
 
	DEBUGF(lan_debug, printf("lan mbuf request failed\n");
	);
	if (top != 0)
		m_freem(top);
	return (0);
}
 
 
/*
 * lan_initialize - initialize adapter;
 *
 * Initialization consists of 3 phases:
 *	1) check of bring-up diagnostics
 *	2) transfer of initialization parameters
 *	3) dma interface check.
 */
 
lan_initialize(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_device *addr;
	struct iocc_ctlr *ic = lanc_info[unit];
	int retry, success, failure, j, k, tcw; /* loop controls */
	unsigned short initparm[LAN_NUM_IPARMS];
	register unsigned short *parmptr, *initptr;
	unsigned short sifrbuf;
	register unsigned short *shortptr;
	struct mbuf *m;
 
	/*
	 * assume initial attempt plus
	 * 3 more retries of full procedure
	 */
 
	DEBUGF(lan_debug, printf("lan%d initialization\n", unit);
	);
	addr = (struct lan_device *)(lanc_info[unit]->ic_addr);
 
	if ((caddr_t)addr == lan_std[0]) {
		lns->lan_dma_chan = DMA_CHAN5;
	} else if ((caddr_t)addr == lan_std[1]) {
		lns->lan_dma_chan = DMA_CHAN6;
	} else if ((caddr_t)addr == lan_std[2]) {
		lns->lan_dma_chan = DMA_CHAN7;
	} else if ((caddr_t)addr == lan_std[3]) {
		lns->lan_dma_chan = DMA_CHAN3;
	}
	if((lns->lns_adapter & LAN_ADAP_CHANNEL) == 0) {
		ic->ic_dmachannel = lns->lan_dma_chan;
		ic->ic_dmaflags = (DMA_CASCADE | DMA_CANTWAIT | DMA_EXCLUSIVE |
		DMA_PAGE | DMA_PHYSICAL);
		ic->ic_dmabuf = NULL;
		if(dma_setup(ic) == DMA_OK_RET) {
			lns->lns_adapter |= LAN_ADAP_CHANNEL;
			}
		else {
			printf("lan%d: dma channel %x\n", unit,lns->lan_dma_chan);
			return(LAN_INIT_ERROR);
		}
	}
	/* begin adapter initialization */
	for (retry = LAN_RETRY; retry > 0; retry--) {
		shortptr = &(addr->lan_cmdstat);
		miow(shortptr, LAN_RESET);
		/* check bring-up diagnostics results */
		DEBUGF(lan_debug,
			printf("lan%d init retry %d\n", unit, LAN_RETRY - retry);
		);
		for (j = LAN_RESET_WAIT, success = failure = 0; j > 0; j--) {
			DELAY(TEN_MS);
			sifrbuf = mior(shortptr);
			DEBUGF(lan_debug,
				printf("lan%d status = 0x%b, int/err code = 0x%x\n",
					unit, sifrbuf & 0xfff0,
					LAN_STAT_BITS, sifrbuf & 0x000f);
			);
			if (sifrbuf & LAN_INITIALIZE) {
				if ((sifrbuf &
					(LAN_TEST |
					LAN_ERROR |
					LAN_ADAP_INT)) == 0) {
					/* diagnostics successful */
					success++;
					break;
			} else if ((sifrbuf & LAN_TEST) &&
					(sifrbuf & LAN_ERROR)) {
					/* unrecoverable error */
					failure++;
					break;
				}
			}
		}
		DEBUGF(lan_debug, printf("lan%d pods complete\n", unit);
		);			/* end bring-up diagnostics */
 
		if (success) {		/* diagnostics ok,now handshake */
			/* transfer initialization parms */
			*LAN_IRQ12 = 0;	/* enable interrupt level */
			success = 0;
			sifrbuf = mior(&(addr->lan_enable));
			/*
			 * use an mbuf to store scb, ssb
			 * to guarantee alignment
			 */
			if(lns->lns_ctl == 0) {
				MGET(m, M_DONTWAIT, MT_DATA);
				if (m == 0) {
					failure++;
					break;
				}
				lns->lns_ctl = mtod(m, struct lan_ctl *);
			}
			tcw = lan_dma_setup(lns->lns_ctl, TCW_RESERVE, lns->lan_dma_chan);
			initparm[0] = LAN_INIT_OPTIONS;
			if((mior(&addr->lan_version)) == LAN_ADAPTER_UPDATE_ID){
				initparm[0] |= LAN_INIT_BURST_DMA;
			}
			initparm[1] = initparm[2] = initparm[3] = 0;
			initparm[4] = LAN_INIT_RBURST;
			initparm[5] = LAN_INIT_XBURST;
			initparm[6] = LAN_INIT_ABORT;
			initparm[7] = DMA_HI_ADDR(tcw);
			initparm[8] = DMA_LO_ADDR(lns->lns_ctl, tcw);
			initparm[9] = initparm[7];
			initparm[10] = DMA_LO_ADDR(&lns->lns_ctl->lan_ssb, tcw);
			/* write adapter init parameters */
			miow(&(addr->lan_address), LAN_INIT_DATAA);
			initptr = &(addr->lan_datai);
			for (k = LAN_NUM_IPARMS, parmptr = initparm;
				k > 0;
				k--, parmptr++) {
				miow(initptr, *parmptr);
			}
			/* read status to clear */
			miow(&(addr->lan_cmdstat), LAN_EXECUTE);
 
			/*
			 * wait at least 10 seconds before detecting
			 * initialization error to allow for dma timeout
			 */
 
			for (k = LAN_DMA_TIMEOUT + 1; k > 0; k--) {
				DELAY(TEN_MS);
				sifrbuf = mior(&(addr->lan_cmdstat));
				if ((sifrbuf &
					(LAN_INITIALIZE |
					LAN_TEST |
					LAN_ERROR)) == 0) {
					DEBUGF(lan_debug,
						printf("lan%d dma xface test ok\n",
							unit);
					);
					success++;
					break;
				} else if (sifrbuf & LAN_ERROR) {
					DEBUGF(lan_debug,
						printf("lan%d dma xface error, ",
							unit);
						printf("status = 0x%b, code = 0x%x\n",
							sifrbuf & 0xfff0,
							LAN_STAT_BITS,
							sifrbuf & 0x000f);
					);
					break;
				}
			}
		}
		/* end handshake */
		if (success)
			return (0);
	}
	/* retry init procedure */
	printf("lan%d: token ring adapter initialization failure, status = 0x%b, error code = 0x%x.\n",
		unit, sifrbuf & 0xfff0, LAN_STAT_BITS, sifrbuf & 0x000f);
	return (LAN_INIT_ERROR);
}
 
 
 /* end lan_initialize */
 
/*
 * close - terminate communication on ring
 */
lan_close(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_device *addr = (struct lan_device *)lan_info[unit]->iod_addr;
	register struct lan_ctl *ctl = lns->lns_ctl;
 
	lns->lns_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
	lns->lns_adapter &= ~(LAN_ADAP_OPEN | LAN_OPEN_IN_PROGRESS);
	lan_exec(LAN_CLOSE, ctl->close_parm, addr, unit);
}
 
 
/*
 * freeze - freeze adapter to enable internal storage dump
 */
lan_freeze(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_device *addr = (struct lan_device *)lan_info[unit]->iod_addr;
	register int s = splimp();
	register int i;
 
	lns->lns_adapter |= LAN_ADAP_FROZEN;
	lns->lns_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
	dma_done(lns->lan_dma_chan);
	/*
	 * generate pulses on adapter reset line
	 * to freeze; write to sif cmd reg for
	 * microcode level 12 compatibility
	 */
	for (i = 0; i < LAN_ADAP_FREEZE_PULSES; i++) {
		if (i)
			DELAY(LAN_ADAP_FREEZE_DELAY);
		miow(&(addr->lan_hreset), 0);
		DELAY(LAN_ADAP_MIN_RESET);
		miow(&(addr->lan_hreset), 0);
	}
	*(char *)(&(addr->lan_cmdstat)) = LAN_FREEZE_SIG_u12;
	splx(s);
}
 
 
/*
 * unfreeze - unfreeze adapter
 */
lan_unfreeze(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct lan_device *addr = (struct lan_device *)lan_info[unit]->iod_addr;
	register int s = splimp();
 
	/* pulses hardware reset line to unfreeze */
	miow(&(addr->lan_hreset), 0);
	DELAY(LAN_ADAP_MIN_RESET);
	miow(&(addr->lan_hreset), 0);
	lns->lns_adapter &= ~LAN_ADAP_FROZEN;
	splx(s);
}
 
 
/*
 * thaw - release adapter frozen state
 */
lan_thaw(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
 
	if (lns->lns_adapter & LAN_ADAP_FROZEN) {
		lan_unfreeze(unit);
		timeout(lan_reset, (caddr_t)unit, 1 * hz);
	}
}
 
 
/*
 * lan_read_adapter - transfer adapter storage to system
 */
lan_read_adapter()
{
/* TO BE IMPLEMENTED */
}
 
 
/*
 * lan_error_log - read and reset adapter error log
 */
lan_error_log()
{
/* TO BE IMPLEMENTED */
}
 
 
/*
 * lan_set_faddr - reset adapter functional address after open
 */
lan_set_faddr()
{
/* TO BE IMPLEMENTED */
}
 
 
/*
 * lan_dma_setup - for a given virtual address, return a tcw entry
 *		corresponding to the real address mapping
 */
lan_dma_setup(vaddr, type, chan)
	register char *vaddr;
	register int type;
	register int chan;
 
{
 
	register int raddr;
	register unsigned int i;
	register short *tcwp;
 
	raddr = vtop((int)vaddr & ~(PAGESIZE - 1));
 
	switch (type) {
	case TCW_RESERVE:
		/*
		 * if tcw is type RESERVE it will not be freed
		 * so try to allocate from end of table down
		 */
		tcwp = (short *)(TCW_BASE + (((chan << 6) + (NUMTCW - 1)) << 1));
		for (i = NUMTCW - 1; i >= 0; i--, tcwp--) {
			if (*tcwp == TCW_FREE) 
				goto alloc_slot;
			if ((((int) *tcwp &
					(PAGESIZE-1)) << 11) == raddr)
				return (i);
		}
		return (TCW_ERROR);
	/*
	 * if tcw is for single use we expect it to be freed
	 * so allocate it from the start of the table up
	 */
	case TCW_SINGLE_USE:
		tcwp = (short *)(TCW_BASE + (chan << 7));
		for (i = 0; i < NUMTCW; i++, tcwp++) {
			if (*tcwp == TCW_FREE)
				goto alloc_slot;
		}
		return (TCW_ERROR);
	default:
		return (TCW_ERROR);
	}
alloc_slot:
	*(short *)(TCW_BASE + (((chan << 6) + i) << 1)) =
		(short)(((int)raddr) >> 11) | TCW_RSC_ACC | TCW_REAL_ACC;
	return (i);
}
/*
 * lan_xid_test - support logical link control type 1 operation
 */
 
lan_xid_test(m, i, unit)
	register struct mbuf *m;
	register int i;
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
	register struct ifnet *ifp = &lns->lns_if;
	char *c;
	int j;
	int rc = 0;
	/* for XID, skip over IFP pointer put on mbuf in lan_get */
	m->m_off += sizeof(ifp);
	m->m_len -= sizeof(ifp);
 
	switch (lns->lns_llc[i]->llc_ctl) {
	case LAN_LLC_XID_CMD0:
	case LAN_LLC_XID_CMD1:
		c = mtod(m, char *);
		for (j = 0; j < LAN_L_XID_RESP; j++) {
			*c++ = lan_xid_resp[j];
		}
		m->m_len = LAN_L_XID_RESP;
	case LAN_LLC_TEST_CMD0:
	case LAN_LLC_TEST_CMD1:
		lan_output_llc(ifp, m, lns->lns_mac[i]->from_addr,
			lns->lns_mac[i]->rcf, lns->lns_mac[i]->rseg,
			lns->lns_llc[i]->llc_ctl, lns->lns_llc[i]->dsap);
		rc = (lns->lns_llc[i]->llc_ctl);
		break;
	default:
		m->m_off -= sizeof(ifp);
		m->m_len += sizeof(ifp);
		rc = 0;
	}
	return(rc);
}
 
 
/*
 * miow - swap bytes before port output
 */
 
miow(ioport, datawd)
	register unsigned short *ioport;
	register unsigned short datawd;
{
	*ioport = ((datawd >> 8) | (datawd << 8));
}
 
 
/*
 * mior - swap bytes after port input
 */
unsigned short
mior(ioport)				/* swap bytes after port input */
	register unsigned short *ioport;
{
	register unsigned short datawd;
 
	datawd = *ioport;
	return ((unsigned short)(datawd >> 8) | (datawd << 8));
}
 
 
/*
 * lan_sleep - sleep to wait for adapter to open
 */
 
lan_sleep(unit)
	register int unit;
{
	register struct lan_softc *lns = &lan_softc[unit];
 
	timeout(lan_timout,unit,hz*30);
	sleep(&lns->lansleep_event, 1);
}
 
 
/*
 * lan_timout - timeout routine if adapter does not open in 30 seconds
 */
 
lan_timout(unit)
	register int unit;
{
	register int error = 0;
	register struct lan_softc *lns = &lan_softc[unit];
	
	printf("Token Ring open timeout\n");
	wakeup(&lns->lansleep_event);
	error = EIO;
	return(error);
}
 
#endif NLAN > 0
 
