/*
 * 5799-CGZ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header: if_lan.c,v 12.5 89/08/10 17:17:19 mlewis Exp $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header: if_lan.c,v 12.5 89/08/10 17:17:19 mlewis Exp $";
#endif

/*
 *	IBM TOKEN-RING NETWORK ADAPTER DRIVER
 *
 * Note:
 *	although the file name is if_lan.c, the entry points etc. are still
 *	called tr... .
 *
 */

#include "lan.h" 
#if NLAN > 0

#include "../machine/pte.h"

#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "vmmac.h"
#include "ioctl.h"
#include "errno.h"

#include "time.h"
#include "kernel.h"

#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"

#ifdef INET 

#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/in_var.h"
#include "../netinet/if_ether.h"

#endif INET 

#include "../machine/io.h"
#include "if_lanreg.h"
#include "if_lanvar.h"
#include "../machineio/ioccvar.h"
#include "../machine/debug.h"
#include "../ca_atr/pcif.h"

int	trprobe(), trattach();

caddr_t	trstd[] = { (caddr_t) 0x00000a20, (caddr_t) 0x00000a24, 0};

struct 	iocc_device *trinfo[NLAN];

int trint(), trinit(), trioctl(), troutput(), trreset(), trtimout();
int trsuspend();

unsigned long tokdebug = 0;		/* flag for debugfs */

struct	iocc_driver landriver =
	{ trprobe, 0, trattach, 0, trstd, "lan", trinfo,
	 /*	      int   csr chanrel flags 		suspend */
		0, 0, trint, 0, 0,     DRIVER_SUSPEND,   trsuspend };


struct mbuf *trget();

/*
 *	Token-Ring software status per adapter
 */
struct	tr_softc {
	struct arpcom trs_ac;		/* like ethernet */
#define	trs_if trs_ac.ac_if		/* network-visible interface */
#define trs_addr trs_ac.ac_enaddr	/* hardware token-ring address */
	unsigned char trs_intlevel;	/* adapter interrupt level */
	short trs_ocount;		/* count of active output requests */
	char srbfree;			/* 0 = SRB free, 1 = SRB in use */
	unsigned char *mmio;		/* address of MMIO region */
	unsigned char *sram;		/* address of shared RAM */
	struct rbcb rbc;		/* receiver buffer control block */
	struct aca *aca;		/* pointer to adapter ACA */
	struct trs_cb cb;		/* struct of pointers to ctl blocks */
	struct init_resp init_rs;	/* adapter initialization response */
	unsigned short exsap_station;	/* station assigned by open sap cmd */
	struct	 trs_cb init_cb;     	/* place to save initial srb address */
	unsigned char wire_fault;    	/* flag a wire fault has occurred */
	caddr_t	 tr_window_addr;   	/* tr card pc window address */
	caddr_t	 sram_window;   	/* tr card sram window addr  */
	caddr_t  trsleep_event;     	/* tr event signalled on successful */
						/* open of adapter  */
	int	trs_flags;		/* saved flags over suspend */
} tr_softc[NLAN];



#define restore_window(window)	set_128_window(window)



/*
 * trprobe - check if PC Token-Ring card is present
 */
trprobe(addr)
register struct trdevice *addr;		  /* pointer to device registers */
{
	register int old_window;

caddr_t window_addr;
register unsigned char *mmio = 0;	/* pointer to MMIO Region */
register struct aca *aca;	/* pointer to Attachment Control Area */

	
	addr = (struct trdevice *) (0xffff & (unsigned int)addr);
	/* Generate interrupt */
	OUT(&addr->reset, 0);	/* latch on an unconditional adapter reset */
	delay(50);		/* delay 50ms */
	/* turn off adapter reset, generate interrupt */
	OUT(&addr->release_reset, 0);

      	/* read card switches and calculate nearest 128k window boundry */
	window_addr = (caddr_t) 0;
	window_addr = (caddr_t) (((IN(&addr->switch_read) & 0xfc) << 11)
			+ TR_MMIO_OFFSET);
	window_addr = (caddr_t) ((int)window_addr & 0xe0000);
	/* Calculate address of Attachment Control Area */
	mmio = (unsigned char *) (((IN(&addr->switch_read) & 0xfc) << 11)
		+ TR_MMIO_OFFSET) - (int)window_addr;
	aca = (struct aca *)(mmio + TR_ACA_OFFSET);	/* get ACA address */

	old_window = get_128_window();		/* save old window */
	set_128_window(window_addr);

	/* allow adapter to interrupt PC */
	MM_OUT(&aca->set.isrp_h, INT_ENABLE);
	pcvec_map[TRIRQ] = 1;

	PROBE_DELAY(1000000); /* delay for 1 seconds, delay is interruptable */
	
	/* since ps-2 uses level trigerring for interrupts, we must */
	/* not allow interupts to go to the pc until we clear the   */
	/* SRB RESPONSE INTERRUPT in TRATTACH                       */

	MM_OUT(&aca->reset.isrp_h, ~(INT_ENABLE));

  	restore_window(old_window);

	return(PROBE_OK);	/* indicate adapter should have interrupted */
}



/*
 * trattach - Make this interface available to the system-network-software.
 * When the system-network-software is ready to accept packets it will
 * initialize the interface by calling trinit().
 */
trattach(iod)
register struct iocc_device *iod;	/* pointer to device structure */
{
	register int old_window;
	/* get pointer to softc struct for this adapter */
	register struct tr_softc *trs = &tr_softc[iod->iod_unit];

	/* get pointer to network-visible interface */
	register struct ifnet *ifp = &trs->trs_if;

	/* get pointer to device I/O space */
	register struct trdevice *addr = (struct trdevice *)((unsigned int)iod->
					 iod_addr & 0xffffff);

	unsigned long i;

	trs->wire_fault = 0x00;


	/* init network-visible interface with */
	ifp->if_unit = iod->iod_unit;	/* unit number, */
	ifp->if_name = "lan";		/* name, and */
	ifp->if_mtu = ETHERMTU;		/* max xmit unit (same as ethernet) */

	/* Save interrupt level in softc struct */
	trs->trs_intlevel = iod->iod_irq;

	/* Read adapter switches and calculate addresses of MMIO and ACA */
	i = IN(&addr->switch_read);	/* Read switches */
	trs->tr_window_addr = (caddr_t) 0;
	trs->tr_window_addr = (caddr_t) (((i & 0xfc) << 11) + TR_MMIO_OFFSET);
	trs->tr_window_addr = (caddr_t) ((int)trs->tr_window_addr & 0xe0000);
	old_window = get_128_window();		/* save old window */
	set_128_window((int)trs->tr_window_addr);
	/* set MMIO address */
	trs->mmio = 0;
	trs->mmio = (unsigned char *) (((i & 0xfc) << 11) + TR_MMIO_OFFSET)
			- ((int)trs->tr_window_addr);
	/* set ACA address */
	trs->aca = (struct aca *)(trs->mmio + TR_ACA_OFFSET);

	/* Get address of adapter shared RAM from PIO read of loc. 0xa22     */
	i = IN(&addr->release_reset) & 0xfe;	/* Read switches */
        
        /* get to the nearest 128K window */
        trs->sram_window = (caddr_t) ((i << 12) & 0xe0000);
	trs->sram = 0;

		/* Get address of sram */
	trs->sram = (unsigned char *) ((caddr_t)(i << 12) - trs->sram_window);

	/* Save address of SRB in softc struct */
	trs->cb.srb = (union sr_srb *)(trs->sram + MM_INW(&trs->aca->rw.wrb));
        trs->init_cb.srb = trs->cb.srb;

	set_128_window(trs->sram_window);

	/* Save SRB response to adapter card initialization */


	for (i = 0; i < sizeof(struct init_resp); i++)
		((char *)&trs->init_rs)[i] = MM_IN(((char *)trs->cb.srb + i));

	/* Save adapter hardware address */

	for (i = 0; i < TR_ADDR_LEN; i++)
		trs->trs_addr[i] =
		MM_IN((trs->init_rs.encoded_addr + trs->sram + i));

	DEBUGF( 1,
		{ printf("lan%d: token-ring address ", ifp->if_unit);
		trprinttraddr(trs->trs_addr);
		printf("\n"); }
	)

	/* Indicate that SRB is used */
	trs->srbfree = 1;


	ifp->if_init = trinit;		/* How to initialize this interface */
	ifp->if_ioctl = trioctl;	/* How to send ioctls to interface */
	ifp->if_output = troutput;	/* How to output packets */
	ifp->if_reset = trreset;	/* How to reset interface */
        ifp->if_flags = IFF_BROADCAST;  /* set broadcast on for 4.3 */  
	if_attach(ifp);	/* Put interface on "active" interface list */

	/* clear the SRB-response interrupt bit */
	set_128_window((int)trs->tr_window_addr);
	MM_OUT(&trs->aca->reset.isrp_l, ~(SRB_RESP_INT));
	MM_OUT(&trs->aca->reset.isra_l, ~(CMD_IN_SRB));
	MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE); /*allow pc ints. */
	pcvec_map[TRIRQ] = 1;

	DEBUGF(tokdebug, printf("lan%d: attached\n", iod->iod_unit);)

  	restore_window(old_window);
}



/*
 * trreset - reset interface
 */
trreset(unit)
register int unit;
{
	register int old_window;
	register struct iocc_device *iod;   /* pointer to device structure */
	register struct tr_softc *trs = &tr_softc[unit];
	register union sr_srb *srb = trs->cb.srb;
	register struct ifnet *ifp = &trs->trs_if;


	old_window = get_128_window();
	set_128_window((int)trs->tr_window_addr);

	if (unit < NLAN && (iod = trinfo[unit]) != 0 && iod->iod_alive != 0){
/***************************************************************************
 * To do a reset we will issue a DIR CLOSE ADAPTER command                 *
 ***************************************************************************/
                if (ifp->if_flags & IFF_RUNNING) {
			set_128_window(trs->sram_window);
			trs->srbfree = 1;	/* indicate SRB in use */
			/* close command */
			MM_OUT(&srb->close.command, DIR_CLOSE);
			/* tell adapter: command in SRB */
			set_128_window((int)trs->tr_window_addr);
			MM_OUT(&trs->aca->set.isra_l, CMD_IN_SRB);
			/* wait for it to complete */
			trsleep(ifp->if_unit);
		}
		MM_OUT(&trs->aca->reset.isrp_h, ~(INT_ENABLE)); /* paranoia */
		DEBUGF(tokdebug, printf("lan%d: reset\n", unit);)
	}
  	restore_window(old_window);
}

/*
 * routine called when a suspend command has been issued that 
 * shuts down (by calling trreset) the interface when we're about
 * to return control to DOS, and then brings it back again after
 * we regain control. We keep a local copy of the ifp->if_flags 
 * in trs->trs_softc so that we can tell if we should get the adapter
 * running again (trreset clears IFF_RUNNING etc).
 */
trsuspend(iod, idr, how)
	register struct iocc_device *iod;
	struct iocc_driver *idr;
{
	int unit = iod->iod_unit;
	register struct tr_softc *trs = &tr_softc[unit];
	register struct ifnet *ifp = &trs->trs_if;

	if (how == SUSPEND_START) {
		trs->trs_flags = ifp->if_flags;	/* save flag state */
		if (ifp->if_flags & IFF_RUNNING)
			trreset(unit);		/* stop it running */
	} else if (how == SUSPEND_DONE) {
		int old_window = get_128_window();
		/* clear the SRB-response interrupt bit */
		set_128_window((int)trs->tr_window_addr);
		MM_OUT(&trs->aca->reset.isrp_l, ~(SRB_RESP_INT));
		MM_OUT(&trs->aca->reset.isra_l, ~(CMD_IN_SRB));
		if (trs->trs_flags & IFF_RUNNING) {
			MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE); /*allow pc ints. */
			ifp->if_flags &= ~IFF_RUNNING;	/* pretend not running */
			trinit(unit);
		restore_window(old_window);
		}
	}
		
}


/*
 *  trinit - initialize network interface, open adapter for packet
 *	     reception and start any pending output
 */
trinit(unit)
register int unit;
{
	register int old_window;
	register struct tr_softc *trs = &tr_softc[unit];
	register struct ifnet *ifp = &trs->trs_if;
	register union sr_srb *srb = trs->init_cb.srb;
	register int i;
	int s;


        if (ifp->if_addrlist == (struct ifaddr *) 0) {
                /* no address */
		return;
        }
        if ((ifp->if_flags & IFF_RUNNING) == 0) { /* if not running */
		s = splimp();
		trs->trs_ocount = 0;	/* reset active-output-request count */

		/* open adapter.  the open-complete interrupt handler will
		 * start any pending output.
		 */
		trs->srbfree = 1;	/* indicate SRB in use */

		old_window = get_128_window();
		set_128_window(trs->sram_window);


		/* zero SRB */
                trs->cb.srb = trs->init_cb.srb;   /* restore initial srb in trs structure */

		for (i=0; i < sizeof(*trs->cb.srb); i++)
			MM_OUT(((char *)(trs->cb.srb) + i), 0);


		/* set open parameters in SRB */
		MM_OUT(&srb->open_cmd.command, DIR_OPEN_ADAPTER); /* open cmd */
		MM_OUTW(&srb->open_cmd.open_options, OPEN_PASS_BCON_MAC);
		MM_OUTW(&srb->open_cmd.num_rcv_buf, NUM_RCV_BUF);
		MM_OUTW(&srb->open_cmd.rcv_buf_len, RCV_BUF_LEN);
		MM_OUTW(&srb->open_cmd.dhb_length, DHB_LENGTH);
		MM_OUT(&srb->open_cmd.num_dhb, NUM_DHB);
		MM_OUT(&srb->open_cmd.dlc_max_sap, 2); /* allow MAX of two SAPS */
		MM_OUT(&srb->open_cmd.dlc_max_sta, 8);	/* allow MAX of 8  stations */
		

		set_128_window((int)trs->tr_window_addr);
		MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE);	/* allow interrupts */
		pcvec_map[TRIRQ] = 1;

		/* tell adapter: command in SRB */
		set_128_window((int)trs->tr_window_addr);
		MM_OUT(&trs->aca->set.isra_l, CMD_IN_SRB);

  		restore_window(old_window);

		splx(s);
	}
	DEBUGF(tokdebug, printf("lan%d: init'ed\n", unit);)
}



/*
 *  trstart - Present transmit request to adapter
 */
trstart(trs,m0)
register struct tr_softc *trs;	/* pointer to softc struct */
register struct mbuf *m0;	/* pointer to mbuf struct */
{
	register int old_window;

	old_window = get_128_window();
	set_128_window(trs->sram_window);

	register union sr_srb *srb = trs->cb.srb;	/* pointer to SRB */


	DEBUGF(tokdebug, printf("lan%d: trstart\n",trs->trs_if.if_unit);)
	trs->srbfree = 1;	/* indicate SRB in use */

	/* load SRB to request transmit */

      	MM_OUT(&srb->xmit.command, XMIT_UI_FRM); /* xmit ui frame */

 	MM_OUTW(&srb->xmit.station_id, trs->exsap_station);
	/* tell adapter that SRB loaded and ask for SRB free interrupt */
	set_128_window((int)trs->tr_window_addr);
	MM_OUT(&trs->aca->set.isra_l, (CMD_IN_SRB + SRB_FREE));

  	restore_window(old_window);
}


/*
 *  trint - interrupt handler.  Find the cause of the interrupt and
 *  service it.
 */
trint(unit)
register int unit;
{
	/* get address of softc struct for this adapter */
	register struct tr_softc *trs = &tr_softc[unit];
	register struct ifnet *ifp = &trs->trs_if;


	/* holds status read from adapter status register */
	register unsigned char status;

	/* holds command read from status or request block */
	register unsigned char command;

	/* 1 = unclaimed interrupt, 0 = interrupt claimed */
	int rc = 1;

	int close_signal = 0;		/* remember if now closed */

	register int old_window;

	old_window = get_128_window();
	set_128_window((int)trs->tr_window_addr);

	/* do not allow interrupts until we finish and reset the */
	/* corresponding system block.                           */
	MM_OUT(&trs->aca->reset.isrp_h, ~(INT_ENABLE));

	/* Is this interrupt caused by an adapter check? */
	if ((status = MM_IN(&trs->aca->rw.isrp_l)) & ADAP_CHK_INT) {
		printf("lan%d: adapter check\n", unit);
	
		/* Clear this interrupt bit */
		MM_OUT(&trs->aca->reset.isrp_l, ~(ADAP_CHK_INT));
		MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE);
		pcvec_map[TRIRQ] = 1;
	
		rc = 0;		/* Claim responsibility for interrupt */
	}

	/* Is this interrupt caused by an adapter error or access violation? */
	else if (MM_IN(&trs->aca->rw.isrp_h) & TCR_INT + ERR_INT + ACCESS_INT) {
		printf("lan%d: adapter error, ISRPH = %x\n",
			unit, MM_IN(&trs->aca->rw.isrp_h));

		/* Clear these interrupt bits */
		MM_OUT(&trs->aca->reset.isrp_h, ~(TCR_INT + ERR_INT + ACCESS_INT));
		MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE);
		pcvec_map[TRIRQ] = 1;

		rc = 0;		/* Claim responsibility for interrupt */

	}

	/* Is this interrupt caused by normal operation of adapter? */
	else if (status & SRB_RESP_INT + ASB_FREE_INT
			+ ARB_CMD_INT + SSB_RESP_INT) {

		DEBUGF(tokdebug, printf("lan%d: ISRPL = %x\n", unit, status);)

		/* Process SRB_RESP_INT, ASB_FREE_INT, ARB_CMD_INT
		   & SSB_RESP_INT in that order, ISRP-L Hi to Lo */

		if (status & SRB_RESP_INT) {	/* Adapter response in SRB? */
		register union sr_srb *srb = trs->cb.srb;  /* pointer to SRB */

		set_128_window(trs->sram_window);
		
			command = MM_IN(&srb->xmit.command);


			switch(command) {
			
			case XMIT_DIR_FRAME:	/* Response to xmit request */
				/* Response not valid? */
				if (MM_IN(&srb->xmit.retcode) != 0xff)
				printf("lan%d: SRB error on xmit request =%x\n",
				unit, MM_IN(&srb->xmit.retcode));
				else {
					/* inc count of active xmit cmds */
					trs->trs_ocount++;
					DEBUGF(tokdebug,
					printf("lan%d: trs_ocount = %d\n",
						unit, trs->trs_ocount);)
				}
				break;

			case XMIT_UI_FRM:	/* Response to xmit request */
				if (MM_IN(&srb->xmit.retcode) != 0xff)
				printf("lan%d: SRB error on xmit request =%x\n",
				unit, MM_IN(&srb->xmit.retcode));
				else {
					/* inc count of active xmit cmds */
					trs->trs_ocount++;
					DEBUGF(tokdebug,
					printf("lan%d: trs_ocount = %d\n",
						unit, trs->trs_ocount);)
				}
				break;
			case DIR_OPEN_ADAPTER:	/* open-adapter-cmd response */
				/* Open successful? */


				if (MM_IN(&srb->open_resp.retcode) == 0){
					trs->trs_if.if_flags |= IFF_UP | IFF_RUNNING;
					/* Save new ACA ctrl block addresses */
					trs->cb.ssb = (struct sr_ssb *)
						(MM_INW(&srb->open_resp.ssb_addr) + trs->sram);
					trs->cb.arb = (union sr_arb *)
						(MM_INW(&srb->open_resp.arb_addr) + trs->sram);
					trs->cb.srb = (union sr_srb *)
						(MM_INW(&srb->open_resp.srb_addr) + trs->sram);
					trs->cb.asb = (union sr_asb *)
						(MM_INW(&srb->open_resp.asb_addr) + trs->sram);

			/* re enable interrupts to the PC so we do not */
			/* miss the open sap interrupt....             */

					set_128_window((int)trs->tr_window_addr);
					MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE);
					pcvec_map[TRIRQ] = 1;

				/* reset the wire fault signal */

					set_128_window(trs->sram_window);
					trs->wire_fault = 0x00;
					tropensap(unit,EXTENDED_SAP); 

				} else {
					printf("lan%d: Open error = %x\n",
					unit, MM_IN(&srb->open_resp.retcode));
					ifp->if_flags &= ~IFF_RUNNING;
					ifp->if_flags &= ~IFF_UP;
					timeout(trinit,ifp->if_unit,hz*30);
				}
				break;

			case DIR_CLOSE:	/* Response to close adapter command */
				/* close not successful? */
				if (MM_IN(&srb->close.retcode) != 0)
					printf("lan%d: close error = %x\n",
					unit, MM_IN(&srb->close.retcode));
				else if(MM_IN(&srb->close.retcode) == 0) {
					close_signal = 1;
					set_128_window((int)trs->tr_window_addr);
					ifp->if_flags &= ~IFF_RUNNING;
					ifp->if_flags &= ~IFF_UP;
					MM_OUT(&trs->aca->reset.isrp_h, ~(INT_ENABLE));
					untimeout(trtimout, unit);
					wakeup(&trs->trsleep_event);
				}
				break;

			case DIR_INTERRUPT:  /* Response to generate-intr cmd */
				/* generate-interrupt cmd not successful? */
				if (MM_IN(&srb->intr.retcode) != 0)
					printf("lan%d: int cmd error =%x\n",
					unit, MM_IN(&srb->intr.retcode));
				break;

			case DIR_MOD_OPEN_PARAMS:   /* Response to mod params */
				/* cmd not successful? */
				if (MM_IN(&srb->mod_params.retcode) != 0)
					printf("lan%d: mod params cmd err =%x\n",
					unit, MM_IN(&srb->mod_params.retcode));
				break;

			case DIR_SET_GRP_ADDR:	/* Response to set-grp-addr */
			case DIR_SET_FUNC_ADDR:	/* Response to set-func-addr */
				/* cmd not successful? */
				if (MM_IN(&srb->set_addr.retcode) != 0)
				printf("lan%d: set grp or func addr err =%x\n",
				unit, MM_IN(&srb->set_addr.retcode));
				break;

			case OPEN_SAP:         	/* Response to open sap cmd */

				if (MM_IN(&srb->open_sap.sap_value) == EXTENDED_SAP) {
					trs->exsap_station = MM_INW(&srb->open_sap.station_id);
				}
				printf("\n");
				printf("lan%d: Token Ring opened\n", unit);
				untimeout(trtimout, unit);
				wakeup(&trs->trsleep_event);
				break;
			case CLOSE_SAP: /* Response to close sap cmd */
				break;
			case DIR_READ_LOG:   /* Response to read log */
				/* cmd not successful? */
				if (MM_IN(&srb->log.retcode) != 0)
					printf("lan%d: read error log cmd err =%x\n",
					unit, MM_IN(&srb->log.retcode));
				printf("lan%d: ERROR LOG:\n",unit);
				printf("lan%d: Line=%d, Internal=%d, Burst=%d\n",unit,(MM_IN(&srb->log.data[0])),(MM_IN(&srb->log.data[1])),(MM_IN(&srb->log.data[2])));
				printf("lan%d: A/C=%d, Abort=%d, Lost frames=%d\n",unit,(MM_IN(&srb->log.data[3])),(MM_IN(&srb->log.data[4])),(MM_IN(&srb->log.data[6])));
				printf("lan%d: Receive congestion=%d, Frame copied=%d, Frequency=%d\n",unit,(MM_IN(&srb->log.data[7])),(MM_IN(&srb->log.data[8])),(MM_IN(&srb->log.data[9])));
				printf("lan%d: Token=%d\n",unit,(MM_IN(&srb->log.data[10])));
				trs->srbfree = 0;	/* done with SRB */
				break;
			default:
				printf("lan%d: bad SRB command encountered\n",
				unit);
				break;
			}
			/* clear the SRB-response interrupt bit */

			set_128_window((int)trs->tr_window_addr);
			MM_OUT(&trs->aca->reset.isrp_l, ~(SRB_RESP_INT));

			/* Try to start output? */
			set_128_window(trs->sram_window);
			if (command == OPEN_SAP &&
			MM_IN(&srb->open_resp.retcode) == 0) {
				
				trs->srbfree = 0;
				/* if output not active */
				if (trs->trs_ocount == 0 && 
				/* and data on send queue */
				trs->trs_if.if_snd.ifq_head)
				/* Continue output from queue */
				trstart(trs,trs->trs_if.if_snd.ifq_head);
			}
			set_128_window((int)trs->tr_window_addr);
		}

		if (status & ASB_FREE_INT){  /* Is Adapter Status Block Free? */
		register union sr_asb *asb = trs->cb.asb;  /* pointer to ASB */

		set_128_window(trs->sram_window);

			switch (command = MM_IN(&asb->rec_resp.command)) {
			case REC_DATA:		/* Receive */
				/* Response not valid? */
				if (MM_IN(&asb->rec_resp.retcode) != 0xff)
				printf("lan%d: ASB bad receive response =%x\n",
				unit, MM_IN(&asb->rec_resp.retcode));
				break;

			case XMIT_DIR_FRAME:	/* Transmit */
				/* Response not valid? */
				if (MM_IN(&asb->xmit_resp.retcode) != 0xff)
				printf("lan%d: ASB response err on xmit =%x\n",
				unit, MM_IN(&asb->xmit_resp.retcode));
				break;

			case XMIT_UI_FRM:   	/* Transmit */
				/* Response not valid? */
				if (MM_IN(&asb->xmit_resp.retcode) != 0xff)
				printf("lan%d: ASB response err on xmit =%x\n",
				unit, MM_IN(&asb->xmit_resp.retcode));
				break;

			default:
				printf("lan%d: Invalid command in ASB =%x\n",
				unit, command);
				break;
			}
		
			/* Clear this interrupt bit */
			set_128_window((int)trs->tr_window_addr);
			MM_OUT(&trs->aca->reset.isrp_l, ~(ASB_FREE_INT));
		}

		if (status & ARB_CMD_INT) {	/* Command for PC to process? */

			set_128_window(trs->sram_window);
			switch (command = MM_IN(&trs->cb.arb->stat.command)) {
			case DLC_STATUS:    /* DLC status change */	
				printf("lan%d: ARB new DLC  status = 0x%x\n",
					unit, MM_INW(&trs->cb.arb->dlc.status));
				break;

			case REC_DATA:		/* Adapter has data for PC */
				/* Call receive interrupt handler */
				trrint(unit);
				break;

			case RING_STAT_CHANGE:	/* Ring status change */
				if (MM_INW(&trs->cb.arb->stat.ring_status) & (SIGNAL_LOSS + LOBE_FAULT)){
				printf("lan%d: SIGNAL LOSS/LOBE FAULT\n",
					unit);

					ifp->if_flags &= ~IFF_RUNNING;
					ifp->if_flags &= ~IFF_UP;
					trs->wire_fault = 0x01;
					timeout(trinit,ifp->if_unit,hz*30);
				} else {
				if (MM_INW(&trs->cb.arb->stat.ring_status) & ~(SOFT_ERR))
				printf("lan%d: ARB new ring status = 0x%x\n",
					unit, MM_INW(&trs->cb.arb->stat.ring_status));
				}
				if (MM_INW(&trs->cb.arb->stat.ring_status) & LOG_OFLOW){
					trs->srbfree = 1;	/* using SRB */
					MM_OUT(&trs->cb.srb->log.command, DIR_READ_LOG);

					/* read & reset err log cmnd in SRB */

					set_128_window((int)trs->tr_window_addr);
					/* enable PC ints so we get response */

					MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE);
					pcvec_map[TRIRQ] = 1;
					MM_OUT(&trs->aca->set.isra_l, CMD_IN_SRB);
					DEBUGF(tokdebug, printf("lan%d: read adapter error log\n", unit);)
				}
				break;

			case XMIT_DATA_REQ: /* Adapter wants data to transmit */
				/* Call transmit interrupt handler */
				trxint(unit);


				break;

			default:
				printf("lan%d: Invalid command in ARB =%x\n",
				unit, command);
				break;
			}

			/* Clear this interrupt bit */
			set_128_window((int)trs->tr_window_addr);
			MM_OUT(&trs->aca->reset.isrp_l, ~(ARB_CMD_INT)); 

			/* Tell adapter that ARB is now free */
			MM_OUT(&trs->aca->set.isra_l, ARB_FREE);
		}


		if (status & SSB_RESP_INT){   /* SSB response to SRB command? */

			/* pointer to SSB */
			register struct sr_ssb *ssb = trs->cb.ssb;
			set_128_window(trs->sram_window);

			switch(MM_IN(&ssb->command)) {

			case XMIT_DIR_FRAME:  /* SSB response to SRB xmit cmd */
				if (trs->trs_ocount <= 0)
					printf("lan%d: bad active-xmit count\n",
					unit);

				/* collect status on last packet */
				if (MM_IN(&ssb->retcode) != 0) {    /* error on xmit? */
					/* increment output error count */
					trs->trs_if.if_oerrors++;
					DEBUGF(tokdebug,
					{printf("lanint: xmit err retcode = %x ",
					MM_IN(&ssb->retcode));
					printf(" xmit_error = 0x%x\n",
					MM_IN(&ssb->xmit_err));})
				} else
					/* increment output packet count */
					trs->trs_if.if_opackets++;

				/* decrement active output count */
				trs->trs_ocount--;

				/* if data on send queue */
				if (trs->trs_if.if_snd.ifq_head &&
				/* and SRB is free */
				trs->srbfree == 0)
					trstart(trs,trs->trs_if.if_snd.ifq_head);
				break;

			case XMIT_UI_FRM:     /* SSB response to SRB xmit cmd */
				trs->srbfree = 0;
				if (trs->trs_ocount <= 0)
					printf("lan%d: bad active-xmit count\n",
					unit);

				/* collect status on last packet */
				if (MM_IN(&ssb->retcode) != 0) {    /* error on xmit? */
					/* increment output error count */
					trs->trs_if.if_oerrors++;
					DEBUGF(tokdebug,
					{printf("lanint: xmit err retcode = %x ",
					MM_IN(&ssb->retcode));
					printf(" xmit_error = 0x%x\n",
					MM_IN(&ssb->xmit_err));})
				} else
					/* increment output packet count */
					trs->trs_if.if_opackets++;

				/* decrement active output count */
				trs->trs_ocount--;

				/* if data on send queue */
				if (trs->trs_if.if_snd.ifq_head &&
				/* and SRB is free */
				trs->srbfree == 0) {
					trstart(trs,trs->trs_if.if_snd.ifq_head);
				}
				break;

			case XMIT_XID_CMD:
				printf("lanint: xmit XID return code = 0x%x\n",
				MM_IN(&ssb->retcode));
				break;

			default:
				printf("lan%d: SSB error, invalid command =%x\n",
				unit, MM_IN(&ssb->command));
			}
			/* clear this interrupt bit */
			set_128_window((int)trs->tr_window_addr);
			MM_OUT(&trs->aca->reset.isrp_l, ~(SSB_RESP_INT));

			/* tell adapter that SSB is available */
			MM_OUT(&trs->aca->set.isra_l, SSB_FREE);
		}
		rc = 0;		/* Claim responsibility for interrupt */
	}
	DEBUGF(tokdebug, printf("lan%d: rc=%d\n", unit, rc);)
	if (!close_signal) {
		set_128_window((int)trs->tr_window_addr);
		MM_OUT(&trs->aca->set.isrp_h, INT_ENABLE);
		pcvec_map[TRIRQ] = 1;
	}
	restore_window(old_window);
	return(rc);
}



/*
 *  trrint - interrupt handler for packet reception
 *
 *		determine lan message type, pull packet off interface and
 *		pass to an appropriate higher-level routine
 */
trrint(unit)
int unit;
{
	register struct tr_softc *trs = &tr_softc[unit];
	register union sr_arb *arb = trs->cb.arb;	/* pointer to ARB */
	register union sr_asb *asb = trs->cb.asb;	/* pointer to ASB */
	register struct rbcb *rbc = &trs->rbc;/* pointer to rec buf ctl block */
	register int totlen;	/* length of received data */
	int i;
	register int hsize;	/* holds size of token-ring header */
	register struct mbuf *m;
	unsigned char type;
	unsigned short etype;
	struct ifqueue *inq;
	unsigned char *llcptr;	/* pointer to logical link control fields */
	register struct tr_head *trhead;      /* pointer to token-ring header */
	char trheadbuf[64];		/* buffer to hold tr header */

	DEBUGF(tokdebug, printf("lanrint: arb.command = %x, arb.station_id= %x\n",
				MM_IN(&arb->rec.command),
				MM_INW(&arb->rec.station_id));)
	DEBUGF(tokdebug, printf("arb.buf_addr = %x, arb.lan_hdr_len = %x\n",
				MM_INW(&arb->rec.buf_addr),
				MM_IN(&arb->rec.lan_hdr_len));)
	DEBUGF(tokdebug, printf("arb.dlc_hdr_len = %d, arb.frame_len = %d\n",
				MM_IN(&arb->rec.dlc_hdr_len),
				MM_INW(&arb->rec.frame_len));)
	DEBUGF(tokdebug, printf("arb.msg_type = %x\n", MM_IN(&arb->rec.msg_type));)


	/* load Receive Buffer Control Block with info on first buffer */
	/* get address of 1st receive buffer */
	rbc->rbufp = (struct rec_buf *)(MM_INW(&arb->rec.buf_addr) + trs->sram);
	
	/* calculate pointer to data in 1st receive buffer */
	rbc->rbuf_datap = rbc->rbufp->data;

	/* Check to see if the incoming frame is an unnumbered info frame */
	/* Set llcptr to point past the LAN physical hdr. including       */
	/* routing information, and also skip the dsap and ssap fields    */
	/* of the LLC frame.                                              */

	llcptr = (unsigned char *)((int)rbc->rbuf_datap + MM_IN(&arb->rec.lan_hdr_len)) + SKIP_DSAP_SSAP;

	if ((type = MM_IN(llcptr)) !=UI_CMD) {
	DEBUGF(tokdebug, printf("lanrint: non-UI frame received, type = %x\n",type);)
		goto chuckit;
	}
	llcptr = (unsigned char *)((int)rbc->rbuf_datap + MM_IN(&arb->rec.lan_hdr_len));
	

	/* get ethertype from snap header */
	etype = MM_INW(&((struct tr_llc *)llcptr)->ethertype);

	/* calculate pointer to next (2nd) receive buffer */
	if (MM_INW(&rbc->rbufp->buf_pointer))
		rbc->rbufp_next =
		(struct rec_buf *)(MM_INW(&rbc->rbufp->buf_pointer) + trs->sram - 2);
	else
		rbc->rbufp_next = 0;	/* at end of receive buffer chain */
	

	/* get size of lan and dlc headers */
	/* also add 5 for the SNAP LENGTH  */
	hsize = MM_IN(&arb->rec.lan_hdr_len) + MM_IN(&arb->rec.dlc_hdr_len) + SNAP_LENGTH;

	/* get total length of data in frame */
	totlen = MM_INW(&arb->rec.frame_len) - hsize;

	DEBUGF(tokdebug & 2,{
		int i;
		printf("lan%d: trrint - physical header\n", unit);
		for (i=0; i < hsize; i++) 
			printf("0x%x ",MM_IN((rbc->rbuf_datap + i)));
		printf("\n");
	})

	/* read in the tr header and store it in trheadbuf */
	for (i=0; i < hsize; i++) 
		trheadbuf[i] = MM_IN(rbc->rbuf_datap + i);
	/* set trhead to point to lan header for tr_arpinput */
	trhead = (struct tr_head *)trheadbuf;
		
	/* adjust data pointer to skip headers not needed */
	rbc->rbuf_datap += hsize;

	/* calculate length of data in 1st receive buffer */
	rbc->data_len = MM_INW(&rbc->rbufp->buf_len) - hsize;

	/* get packet from adapter into chain of mbufs */
	m = trget(trs,totlen,&trs->trs_if);

	if (m == 0)	/* no mbuf chain returned? */
		goto chuckit;

	switch (etype) {
#ifdef INET 
	case TR_IPTYPE:
	    {
		int s;
		schednetisr(NETISR_IP);
		s = splimp();
		inq = &ipintrq;
		if (IF_QFULL(inq)) {
			DEBUGF(tokdebug & 0x2, printf(" qfull\n");)
			IF_DROP(inq);
			m_freem(m);
		} else {
			IF_ENQUEUE(inq, m);
			DEBUGF(tokdebug & 0x2, printf(" queued\n");)
		}
		splx(s);
		break;
	    }

	case TR_ARPTYPE:
		tr_arpinput(&trs->trs_ac, m, trhead);  /* arpinput frees m */
			DEBUGF(tokdebug & 0x2, printf(" arpinput\n");)
		break;
#endif 
	default:
		m_freem(m);
		break;
	}
	
	trs->trs_if.if_ipackets++;	/* increment input packet count */

if (MM_IN(&asb->rec_resp.retcode) != 0xff)
	printf("lanrint: ASB IS NOT FREE!!!\n");

	/* load receive response into ASB */
	MM_OUT(&asb->rec_resp.command, REC_DATA);
	MM_OUTW(&asb->rec_resp.station_id, MM_INW(&arb->rec.station_id));
	MM_OUTW(&asb->rec_resp.rec_buf_addr, MM_INW(&arb->rec.buf_addr));
	MM_OUT(&asb->rec_resp.retcode, 0);	/* indicate successful receive */

	set_128_window((int)trs->tr_window_addr);
	MM_OUT(&trs->aca->set.isra_l, RESP_IN_ASB); /* tell adapter response in ASB */

	return;
chuckit:

trs->trs_if.if_ierrors++;	/* increment input error count */

if (MM_IN(&asb->rec_resp.retcode) != 0xff)
	printf("lanrint: ASB IS NOT FREE!!!\n");

	DEBUGF(tokdebug, printf("lanrint: packet dropped\n");)
	/* load receive response into ASB */
	MM_OUT(&asb->rec_resp.command, REC_DATA);
	MM_OUTW(&asb->rec_resp.station_id, MM_INW(&arb->rec.station_id));
	MM_OUTW(&asb->rec_resp.rec_buf_addr, MM_INW(&arb->rec.buf_addr));
	MM_OUT(&asb->rec_resp.retcode, 0x20);	/* tell adapter data lost, no mbufs */

	set_128_window((int)trs->tr_window_addr);
	MM_OUT(&trs->aca->set.isra_l, RESP_IN_ASB); /* tell adapter response in ASB */
}




/*
 *  Interrupt handler for "adapter requires data to transmit" interrupt
 */
trxint(unit)
int unit;
{
	register struct tr_softc *trs = &tr_softc[unit];
	register union sr_arb *arb = trs->cb.arb;	/* pointer to ARB */
	register union sr_asb *asb = trs->cb.asb;	/* pointer to ASB */
	register char *dhb;				/* pointer to DHB */
	register struct mbuf *m0;	/* pointer to top of mbuf chain */
	register unsigned short size = 0;
	int i;
	unsigned char ri_present_byt;
	u_short rcf = 0;
	int ri_len;

	DEBUGF(tokdebug, printf("lan%d: trxint\n", unit);)
if (MM_IN(&asb->xmit_resp.retcode) != 0xff)
	printf("lanxint: ASB IS NOT FREE!!!\n");

	/* load parameters into ASB */
	MM_OUT(&asb->xmit_resp.command, XMIT_UI_FRM);  
	MM_OUT(&asb->xmit_resp.cmd_corr, MM_IN(&arb->xmit.cmd_corr));
	MM_OUTW(&asb->xmit_resp.station_id, MM_INW(&arb->xmit.station_id));
	MM_OUT(&asb->xmit_resp.retcode, 0);
	MM_OUT(&asb->xmit_resp.rsap_value, EXTENDED_SAP);

if (MM_IN(&trs->cb.srb->xmit.command) == 0x0e) {
	MM_OUT(&asb->xmit_resp.command, 0x0e);	/* command is TRANSMIT.XID.CMD */
	MM_OUTW(&asb->xmit_resp.frame_len, 0x11);	/* length of frame */
	MM_OUT(&asb->xmit_resp.header_len, 0x0e);	/* length of LAN header */
	MM_OUT(&asb->xmit_resp.rsap_value, EXTENDED_SAP);	/* destination SAP */
	/* get address of data holding buffer */
	dhb = (char *)(MM_INW(&arb->xmit.dhb_addr) + trs->sram);

	/* load dhb */
	MM_OUT(&dhb[0], AC);
	MM_OUT(&dhb[1], FC);
	/* load destination and source addresses */
	for (i=0; i < TR_ADDR_LEN; i++) {
		MM_OUT(&dhb[2 + i], 0xff);
		MM_OUT(&dhb[8 + i], 0x00);
	}
} else if (MM_IN(&trs->cb.srb->xmit.command) == 0x11) {
	MM_OUT(&asb->xmit_resp.command, 0x11);	/* command is TRANSMIT.TEST.CMD */
	MM_OUTW(&asb->xmit_resp.frame_len, 0x11);	/* length of frame */
	MM_OUT(&asb->xmit_resp.header_len, 0x0e);	/* length of LAN header */
	MM_OUT(&asb->xmit_resp.rsap_value, EXTENDED_SAP);	/* destination SAP */
	/* get address of data holding buffer */
	dhb = (char *)(MM_INW(&arb->xmit.dhb_addr) + trs->sram);

	/* load dhb */
	MM_OUT(&dhb[0], AC);
	MM_OUT(&dhb[1], FC);
	/* load destination and source addresses */
	for (i=0; i < TR_ADDR_LEN; i++) {
		MM_OUT(&dhb[2 + i], 0xff);
		MM_OUT(&dhb[8 + i], 0x00);
	}
} else {

	/* if data in queue, copy mbuf chain to DHB */
	if (trs->trs_if.if_snd.ifq_head){	/* more data on output queue? */
		/* compute address of DHB */
		dhb = (char *)(MM_INW(&arb->xmit.dhb_addr) + trs->sram);
		
		/* pull packet off interface send queue, fill DHB */
		IF_DEQUEUE(&trs->trs_if.if_snd, m0);
#ifdef notdef
		for (m = m0; m; m = m->m_next) {
			register unsigned len = m->m_len;

			for (i = 0; i < len; i++)
				MM_OUT((dhb +i), (mtod(m, char *))[i]);

			dhb += len;
			size += len;	/* measure size of data */
		}
#else
		size = trmbcopy(dhb, m0);
#endif
		
		m_freem(m0);		/* free mbuf chain */

		DEBUGF(tokdebug & 2,{  		
		int i;
		char *dhbp = (char *)(MM_INW(&arb->xmit.dhb_addr) + trs->sram);
		printf("lan%d: trxint - contents of xmit buffer\n", unit);
		for (i=0; i < size; i++)
		printf("0x%x ",MM_IN(&dhbp[i]));
		} 
		) 

		/* check to see if there is routing info... */
		/* if yes, figure out length of the routing */
		/* info and update the ASB. Otherwise set   */
		/* the ASB length to 14                     */

		/* compute address of DHB */
		dhb = (char *)(MM_INW(&arb->xmit.dhb_addr) + trs->sram);

		ri_present_byt = MM_IN(&dhb[SOURCE_ADDR_BYT0]);


		if (ri_present_byt & TR_RI_PRESENT) {
			rcf = MM_IN(&dhb[RCF_BYT0]) << 8;
			ri_len = (rcf & TR_RCF_LEN_MASK) >> 8;
			MM_OUT(&asb->xmit_resp.header_len,  
				(HDR_LNGTH_NOROUTE + ri_len));
		} else
			MM_OUT(&asb->xmit_resp.header_len, HDR_LNGTH_NOROUTE);

		/* set size of transmission frame in ASB */
		MM_OUTW(&asb->xmit_resp.frame_len, size);
		
	} else {
		printf("lan%d: unexpected empty mbuf send queue\n", unit);
		
		/* set size of transmission frame in ASB to zero */
		MM_OUTW(&asb->xmit_resp.frame_len, 0);
	}
}
	/* tell adapter that there is a response in the ASB */
	set_128_window((int)trs->tr_window_addr);
	MM_OUT(&trs->aca->set.isra_l, RESP_IN_ASB);
}



/*
 * copy out the packet byte-by-byte in resonably optimal fashion
 */
trmbcopy(dhb, m0)
char *dhb;
struct mbuf *m0;
{
	char *addr =  dhb + pcif_128_b;		/* make a pointer */
	int size = 0;
	struct mbuf *m;

	for (m = m0; m; m = m->m_next) {
		int len = m->m_len;
		char *ptr = mtod(m, char *);
		int i;

		for (i=0; i<len; ++i) {
			* (int *) addr = *ptr++;
			++addr;
		}
		size += len;	/* measure size of data */
	}
	pc_copy_out += size;
	return(size);
}

/*
 *  troutput - token-ring output routine.  encapsulate a packet of type
 *	       family for the local net.  put packet on send queue.
 */
troutput(ifp, m0, dst)
register struct ifnet *ifp;	/* network interface pointer */
register struct mbuf *m0;	/* pointer to 1st mbuf in chain */
struct sockaddr *dst;		/* pointer to destination socket */

{
	int s;
	int error;
	register int i;
	unsigned char trdst[TR_ADDR_LEN];    /* token-ring dest hardware addr */
	struct in_addr idst;	/* internet destination address */
	register struct tr_softc *trs = &tr_softc[ifp->if_unit];
	register struct mbuf *m;
	register struct ether_header *eh;	/* pointer to ethernet header */
	register struct tr_head *trhead;      /* pointer to token-ring header */
	register struct tr_llc *trllc;      /* pointer to token-ring llc */
	struct mbuf *m_get();	/* function returning pointer to mbuf */
	int usetrailers;                /* for 4.3 -- jer         */
	u_short rcf = 0;	/* routing control field */
	u_short rseg[TR_MAX_BRIDGE]; /* routing registers */
	int ri_len;	/* route info length */
	unsigned short ethtype;

	DEBUGF(tokdebug, printf("lan output request\n");)

	rcf = 0;

        if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)){
           error = ENETDOWN;
           goto bad;
        }

	/* add token-ring physical header.  allocate another mbuf for
	 * this purpose.
	 */
	m = m_get(M_DONTWAIT, MT_HEADER);	/* allocate an mbuf */
	if (m == 0) {	/* no mbuf allocated? */
		error = ENOBUFS;
		goto bad;
	}

	switch (dst->sa_family) {

#ifdef	INET 
	case AF_INET:
		/* get internet destination address */
		idst = ((struct sockaddr_in *)dst)->sin_addr;

		/* get token-ring destination hardware address */
		if (!tr_arpresolve(&trs->trs_ac, m0, &idst, trdst, &usetrailers,&rcf, rseg)) return(0);

		ethtype = TR_IPTYPE;
		if (!bcmp((caddr_t)trdst, (caddr_t)etherbroadcastaddr, sizeof(trdst))) {

			rcf = (((sizeof(rcf)) << 8) & TR_RCF_LEN_MASK)
				| TR_RCF_FRAME2K
				| TR_RCF_BROADCAST;
		} 
		break;
#endif 
	case AF_UNSPEC:
		/* get pointer to ethernet header */
		eh = (struct ether_header *)dst->sa_data;
		for (i=0; i < sizeof(trdst); i++)
			trdst[i] = *((char *)eh->ether_dhost + i);

		ethtype = TR_ARPTYPE;
		rcf = (((sizeof(rcf)) << 8) & TR_RCF_LEN_MASK)
			| TR_RCF_FRAME2K
			| TR_RCF_BROADCAST;
		break;
	default:
		printf("lan%d: can't handle af%d\n",
			ifp->if_unit, dst->sa_family);
		error = EAFNOSUPPORT;
		goto bad;
		break;
	}

	m->m_next = m0;		/* put new mbuf on top of chain */
	m->m_off = MMINOFF;	/* initialize data offset */
	trhead = mtod(m, struct tr_head *);  /* get pointer to header in mbuf */
	trhead->ac = AC;			/* access control field */
	trhead->fc = FC;			/* frame control field */

	/* load source address with zeros, adapter will fill in address */
	for (i=0; i < TR_ADDR_LEN; i++)
		trhead->saddr[i] = 0;


	if (rcf) {
		trhead->saddr[0] |= TR_RI_PRESENT;
		trhead -> rcf = rcf;
		ri_len = (rcf & TR_RCF_LEN_MASK) >> 8;
		bcopy ((caddr_t)rseg, trhead->rseg, (ri_len - sizeof(rcf)));
	} else ri_len = 0; 

	/* set up pointer to llc structure (14 past trhead) */
	trllc = (struct tr_llc *)((int)trhead + HDR_LNGTH_NOROUTE + ri_len);
	trllc->protid[0] = 0x00;
	trllc->protid[1] = 0x00;
	trllc->protid[2] = 0x00;
	trllc->dsap = trllc->ssap = EXTENDED_SAP; /* set source and dest SAPs */
	trllc->llc = UI_CMD;			/* LLC control byte */
	trllc->ethertype = ethtype;

	/* load destination address */
	for (i=0; i < TR_ADDR_LEN; i++)
		trhead->daddr[i] = trdst[i];
	DEBUGF(tokdebug, {printf("lanoutput: destination address = ");
			trprinttraddr(trhead->daddr);
			printf("\n");}
	)
	/* make room for token-ring header */
	m->m_len = HDR_LNGTH_NOROUTE + sizeof(struct tr_llc) + ri_len;
	/*
	 * queue packet for transmission, if output not active then start it 
	 */
	s = splimp();
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
		error = ENOBUFS;
		goto qfull;
	}
	IF_ENQUEUE(&ifp->if_snd, m);	/* put packet on send queue */
	/* if SRB is free and output not active */
	if (trs->srbfree == 0 && trs->trs_ocount == 0) {
		trstart(trs,trs->trs_if.if_snd.ifq_head); /* start output */ 
        }
	splx(s);
	return(0);

qfull:
	m0 = m;
	splx(s);
	DEBUGF(tokdebug, printf("lan%d: IP output queue full\n",ifp->if_unit);)
bad:
	m_freem(m0);	/* free mbuf chain */
	return(error);
}




/*
 *  trget - copy packet from adapter's receive buffers into chain
 *	    of mbufs.  return a pointer to this chain.
 */
struct mbuf *trget(trs, totlen,ifp)
register struct tr_softc *trs;	/* pointer to softc struct for this adapter */
register int totlen;		/* total amount of data in receive buffers */
struct ifnet *ifp;  
{
	register struct mbuf *m;
	register int len;
	struct mbuf *top = 0;
	register struct mbuf **mp = &top;

	while (totlen > 0) {	/* while more data to transfer */
              	char *mcp;

		MGET(m, M_DONTWAIT, MT_DATA);	/* allocate an mbuf */
		if (m == 0)	/* mbuf not allocated? */
			goto bad;
		len = totlen;
       		if (ifp)
			len += sizeof(ifp);
		/* data amount in receive bufs >= data space in mbuf cluster? */
		if (len >= NBPG) {
			MCLGET(m);
			if (m->m_len == CLBYTES)
				m->m_len = len = MIN(len, CLBYTES);
			else
				m->m_len = len = MIN(MLEN, len);
		} else {
			m->m_len = len = MIN(MLEN, len);
			m->m_off = MMINOFF;
		}
		mcp = mtod(m, char *);
		if (ifp){
			/* prepend ifp to first mbuf */
			/*
			 * bcopy is used since since word moves must
			 * be on 4 byte boundaries on the RT PC
			 */
			bcopy((char *) &ifp, mcp, sizeof(ifp));
			mcp += sizeof(ifp);
			len -= sizeof(ifp);
			ifp = (struct ifnet *) 0;
		}
		/* get len bytes of data from receive buffers into mbuf */
		trbcopy(trs, mcp, len);
		*mp = m;	    /* add mbuf pointed to by m to mbuf chain */
		mp = &m->m_next;    /* get pointer to m_next field in mbuf */
		totlen -= len;	    /* subtract number of bytes transferred */
	}
	return(top);	/* return pointer to top of mbuf chain */
bad:
	m_freem(top);	/* free mbuf chain */
	return(0);	/* indicate no mbuf pointer to return */
}




/*
 *  trioctl - process an ioctl request
 */
trioctl(ifp, cmd, data)
register struct ifnet *ifp;
register int cmd;
register caddr_t data;
{
	register struct ifaddr *ifa = (struct ifaddr *)data;
	register int s = splimp();
	register int error = 0;

	switch (cmd) {
	case SIOCSIFADDR:
                ifp->if_flags |= IFF_UP;

                switch (ifa->ifa_addr.sa_family) {
#ifdef INET
				case AF_INET:
				/* if not running */
				if ((ifp->if_flags & IFF_RUNNING) == 0) {
					trinit(ifp->if_unit);   /* before arpwhohas */
					trsleep(ifp->if_unit);
				}

	             ((struct arpcom *) ifp)->ac_ipaddr =
				   IA_SIN(ifa)->sin_addr;
				   arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
#endif INET
	   break;
		   default:
		   /* if not running */
				if ((ifp->if_flags & IFF_RUNNING) == 0) {
					trinit(ifp->if_unit);   /* before arpwhohas */
					trsleep(ifp->if_unit);
					}
					break;
				}
				break;
	case SIOCSIFFLAGS:
        /*********************************************************/
        /* 1- If the adapter is DOWN , turn the device off       */  
        /*       ie. adapter down but still running              */
        /* 2- If the adapter is UP, turn the device on           */
        /*       ie. adapter up but not running yet              */
        /*********************************************************/
                if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags &
                     IFF_RUNNING){
                        trreset(ifp->if_unit);
                }  else if (ifp->if_flags & IFF_UP && (ifp->if_flags &
                            IFF_RUNNING) == 0) {
                       		trinit(ifp->if_unit);
					 if (ifp->if_addrlist != (struct ifaddr *) 0) {
						 trsleep(ifp->if_unit);
					 }
		}
                break;
        default:
               error = EINVAL;
        }
	splx(s);
	return(error);
}


/*
 *  trbcopy - like bcopy except that it knows about the structure of
 *	      adapter receive buffers.
 */

trbcopy(trs, dest, len)
register struct tr_softc *trs;	/* pointer to softc struct for this adapter */
register char *dest;		/* destination address */
register int len;		/* number of bytes to copy */
{
	register int i;
	register struct rbcb *rbc = &trs->rbc;	/* pointer to rec buf ctl blk */

	/* while amount of data needed >= amount in current receive buffer */
	while (len >= rbc->data_len) {
		/* copy all data from receive buffer to destination */

		for (i = 0; i < rbc->data_len; i++)
			dest[i] = MM_IN(rbc->rbuf_datap + i);

		DEBUGF(tokdebug & 2,{
			int i;
			printf("lanrint - packet data\n");
			for (i=0; i < rbc->data_len; i++)
				printf("0x%x ",*(dest + i));
			printf("\n");
		})
		len -= rbc->data_len;	/* update length left to transfer */
		dest += rbc->data_len;	/* update destination address */

		/* make next receive buffer current receive buffer */
		if (rbc->rbufp = rbc->rbufp_next) { /* more receive buffers? */

			/* calculate pointer to next receive buffer */
			if (MM_INW(&rbc->rbufp->buf_pointer))
				rbc->rbufp_next =
				(struct rec_buf *)(trs->sram +
				MM_INW(&rbc->rbufp->buf_pointer) - 2);
			else
				rbc->rbufp_next = 0;	/* at end of chain */
	
			/* get pointer to data in current receive buffer */
			rbc->rbuf_datap = rbc->rbufp->data;

			/* get length of data in current receive buffer */
			rbc->data_len = MM_INW(&rbc->rbufp->buf_len);
		} else {
			if (len != 0)	/* len should equal zero */
				printf("lanbcopy: residual data not copied\n");
			return;
		}
	}

	/* amount of data needed is < amount in current receive buffer */

		for (i = 0; i < len; i++)
			dest[i] = MM_IN(rbc->rbuf_datap + i);

		DEBUGF(tokdebug & 2,{
			int i;
			printf("lanrint - packet data\n");
			for (i=0; i < len; i++)
				printf("0x%x ",*(dest + i));
			printf("\n");
		})
	rbc->data_len -= len;	/* update count of data in receive buffer */
	rbc->rbuf_datap += len;	/* update pointer to receive buffer data */
	pc_copy_in += len;
}




/*
 *  trprinttraddr - print a token-ring address
 */
trprinttraddr(p)
register char *p;
{
	register int i;

	for (i = 0; i < TR_ADDR_LEN; i++) {
		if (i != 0) printf(":");
		printf("%x", *p++);
	}
}

/*
 *  tropensap - open the token ring SAP interface
 */
tropensap(unit,type) 
register int unit;
unsigned char type;
{
	register struct tr_softc *trs = &tr_softc[unit];
	register union sr_srb *srb = trs->cb.srb;

/************************************************************************
 ** To use the SAP level interface, we will have to execute a          ** 
 ** DLC.OPEN.SAP (pg.6-61 of the Token RIng Tech. Ref.) after we have  **
 ** received a good return code from the DIR.OPEN.ADAPTER command.     **
 ** We will open the IP SAP x'aa'.                                     **
 **                                                                    **
 ** STEPS:                                                             **
 **      1) Reset SRB response interrupt bit                           **
 **      2) Use the open_sap srb.                                      **
 **      3) Fill the following fields:                                 **
 **            command    - x'15'                                      **
 **            sap_value  - x'aa'                                      **
 **            sap_options- x'24'                                      **
 **                                                                    **
 ***********************************************************************/

	set_128_window((int)trs->tr_window_addr);

	MM_OUT(&trs->aca->reset.isrp_l, ~(SRB_RESP_INT));

	set_128_window(trs->sram_window);

	MM_OUT(&srb->open_sap.command, OPEN_SAP);  
	MM_OUT(&srb->open_sap.retcode, 0x00);  
	MM_OUTW(&srb->open_sap.station_id, 0x0000);
        MM_OUT(&srb->open_sap.timer_t1, 0x00);
        MM_OUT(&srb->open_sap.timer_t2, 0x00);
        MM_OUT(&srb->open_sap.timer_ti, 0x00);
        MM_OUT(&srb->open_sap.maxout, 0x00);
        MM_OUT(&srb->open_sap.maxin, 0x00);
        MM_OUT(&srb->open_sap.maxout_incr, 0x00);
        MM_OUT(&srb->open_sap.maxretry, 0x00);
        MM_OUT(&srb->open_sap.gsapmaxmem, 0x00);
	MM_OUT(&srb->open_sap.sap_value, type);     
        MM_OUTW(&srb->open_sap.max_i_field, 0x0088);  
	MM_OUT(&srb->open_sap.sap_options, 0x24);
        MM_OUT(&srb->open_sap.station_cnt, 0x01);
        MM_OUT(&srb->open_sap.sap_gsap_mems, 0x00);

	set_128_window((int)trs->tr_window_addr);

	MM_OUT(&trs->aca->set.isra_l, CMD_IN_SRB);
}

/*
 *  trsleep - sleep to wait for adapter to open
 */
trsleep(unit)
register int unit;
{
  	register struct tr_softc *trs = &tr_softc[unit];

  	timeout(trtimout,unit,hz*30);
  	sleep(&trs->trsleep_event, 1);
}

/*
 *  trtimout - timeout routine if adapter does not open in 30 seconds
 */
trtimout(unit)
register int unit;
{
  	register int error = 0;
  	register struct tr_softc *trs = &tr_softc[unit];

  	printf("Token Ring timeout\n");
  	wakeup(&trs->trsleep_event);
  	error = EIO;
  	return(error);
}

#endif NLAN > 0
