xref: /csrg-svn/sys/vax/if/if_hy.c (revision 13058)
1*13058Ssam /*	if_hy.c	4.5	83/06/12	*/
211195Ssam 
311195Ssam #include "hy.h"
411195Ssam #if NHY > 0
511195Ssam 
611195Ssam /*
711195Ssam  * Network Systems Copropration Hyperchanel interface
811195Ssam  *
9*13058Ssam  * UNTESTED WITH 4.2
1011195Ssam  */
1111195Ssam #include "../machine/pte.h"
1211195Ssam 
1311195Ssam #include "../h/param.h"
1411195Ssam #include "../h/systm.h"
1511195Ssam #include "../h/mbuf.h"
1611195Ssam #include "../h/buf.h"
1711195Ssam #include "../h/protosw.h"
1811195Ssam #include "../h/socket.h"
1911195Ssam #include "../h/vmmac.h"
2011195Ssam #include "../h/errno.h"
2111207Ssam #include "../h/time.h"
2211207Ssam #include "../h/kernel.h"
2311207Ssam #include "../h/ioctl.h"
2411195Ssam #include "../net/if.h"
2511207Ssam #include "../net/netisr.h"
2611195Ssam #include "../net/route.h"
2711195Ssam #include "../netinet/in.h"
2811195Ssam #include "../netinet/in_systm.h"
2911195Ssam #include "../netinet/ip.h"
3011195Ssam #include "../netinet/ip_var.h"
3111195Ssam 
3211207Ssam #include "../vax/cpu.h"
3311207Ssam #include "../vax/mtpr.h"
3411207Ssam #include "../vaxuba/ubareg.h"
3511207Ssam #include "../vaxuba/ubavar.h"
3611195Ssam #include "../vaxif/if_hy.h"
3711198Ssam #include "../vaxif/if_hyreg.h"
3811195Ssam #include "../vaxif/if_uba.h"
3911195Ssam 
4011195Ssam #define HYROUTE
4111195Ssam #define HYELOG
4211195Ssam #define	HYMTU	576
4311195Ssam 
44*13058Ssam int	hyprobe(), hyattach(), hyinit(), hyioctl();
45*13058Ssam int	hyoutput(), hyreset(), hywatch();
4611195Ssam struct	uba_device *hyinfo[NHY];
4711195Ssam u_short hystd[] = { 0772410, 0 };
4811195Ssam struct	uba_driver hydriver =
4911195Ssam 	{ hyprobe, 0, hyattach, 0, hystd, "hy", hyinfo };
5011195Ssam 
5111195Ssam /*
5211195Ssam  * Hyperchannel software status per interface.
5311195Ssam  *
5411195Ssam  * Each interface is referenced by a network interface structure,
5511195Ssam  * hy_if, which the routing code uses to locate the interface.
5611195Ssam  * This structure contains the output queue for the interface, its address, ...
5711195Ssam  * We also have, for each interface, a UBA interface structure, which
5811195Ssam  * contains information about the UNIBUS resources held by the interface:
5911195Ssam  * map registers, buffered data paths, etc.  Information is cached in this
6011195Ssam  * structure for use by the if_uba.c routines in running the interface
6111195Ssam  * efficiently.
6211195Ssam  */
6311195Ssam struct	hy_softc {
6411195Ssam 	struct	ifnet hy_if;		/* network-visible interface */
6511195Ssam 	struct	ifuba hy_ifuba;		/* UNIBUS resources */
6611207Ssam 	short	hy_flags;		/* flags */
6711207Ssam 	short	hy_state;		/* driver state */
6811195Ssam 	int	hy_ilen;		/* mp length on input */
6911195Ssam 	int	hy_olen;		/* packet length on output */
7011195Ssam 	int	hy_lastwcr;		/* last command's word count */
7111195Ssam 	short	hy_savedstate;		/* saved for reissue after status */
7211195Ssam 	short	hy_savedcmd;		/* saved command for reissue */
7311195Ssam 	int	hy_savedcount;		/* saved byte count for reissue */
7411195Ssam 	int	hy_savedaddr;		/* saved unibus address for reissue */
7511195Ssam 	int	hy_ntime;		/* number of timeouts since last cmd */
7611195Ssam 	int	hy_retry;		/* retry counter */
7711207Ssam 	struct	hy_stat hy_stat;	/* statistics */
7811207Ssam 	struct	hy_status hy_status;	/* status */
7911195Ssam } hy_softc[NHY];
8011195Ssam 
8111195Ssam #ifdef HYELOG
8211207Ssam #define HYE_MAX	0x18
8311207Ssam u_long	hy_elog[(HYE_MAX+1)*4];
8411195Ssam #endif
8511195Ssam 
8611195Ssam #ifdef DEBUG
8711195Ssam #define printL	lprintf
8811195Ssam #define printD	if (hy_debug_flag) lprintf
8911195Ssam int	hy_debug_flag = 0;
9011195Ssam /*
9111195Ssam  * hy_nodebug bit 0x01	set hy_debug_flag on hycancel
9211195Ssam  * hy_nodebug bit 0x02	set hy_debug_flag on command reissue
9311195Ssam  * hy_nodebug bit 0x04	set hy_debug_flag on abnormal interrupt
9411195Ssam  * hy_nodebug bit 0x08	set hy_debug_flag on hyouput
9511195Ssam  * hy_nodebug bit 0x10	set hy_debug_flag on hyouput with associated data
9611195Ssam  */
9711195Ssam int	hy_nodebug = 0x0;
9811195Ssam #else
9911195Ssam #define printD	hyvoid
10011195Ssam #endif
10111195Ssam 
10211195Ssam /*
10311207Ssam  * Requests for service (in order by descending priority).
10411195Ssam  */
10511195Ssam #define RQ_ENDOP	001	/* end the last adapter function */
10611195Ssam #define RQ_REISSUE	002	/* reissue previous cmd after status */
10711195Ssam #define RQ_STATUS	004	/* get the status of the adapter */
10811195Ssam #define RQ_STATISTICS	010	/* get the statistics of the adapter */
10911195Ssam #define RQ_MARKDOWN	020	/* mark this adapter port down */
11011195Ssam #define RQ_MARKUP	040	/* mark this interface up */
11111195Ssam 
11211195Ssam #define RQ_XASSOC	0100	/* associated data to transmit */
11311195Ssam 
11411195Ssam /*
11511207Ssam  * Driver states.
11611195Ssam  */
11711195Ssam #define	STARTUP		0	/* initial state (before fully there) */
11811195Ssam #define	IDLE		1	/* idle state */
11911195Ssam #define	STATSENT	2	/* status cmd sent to adapter */
12011195Ssam #define	ENDOPSENT	3	/* end operation cmd sent */
12111195Ssam #define	RECVSENT	4	/* input message cmd sent */
12211195Ssam #define	RECVDATASENT	5	/* input data cmd sent */
12311195Ssam #define	XMITSENT	6	/* transmit message cmd sent */
12411195Ssam #define	XMITDATASENT	7	/* transmit data cmd sent */
12511195Ssam #define	WAITING		8	/* waiting for messages */
12611195Ssam #define	CLEARSENT	9	/* clear wait for message cmd sent */
12711195Ssam #define MARKPORT	10	/* mark this host's adapter port down issued */
12811195Ssam #define RSTATSENT	11	/* read statistics cmd sent to adapter */
12911195Ssam 
13011195Ssam #ifdef DEBUG
13111195Ssam char *hy_state_names[] = {
13211195Ssam 	"Startup",
13311195Ssam 	"Idle",
13411195Ssam 	"Status Sent",
13511195Ssam 	"End op Sent",
13611195Ssam 	"Recieve Message Proper Sent",
13711195Ssam 	"Recieve Data Sent",
13811195Ssam 	"Transmit Message Proper Sent",
13911195Ssam 	"Transmit Data Sent",
14011195Ssam 	"Wait for Message Sent",
14111195Ssam 	"Clear Wait for Message Sent",
14211195Ssam 	"Mark Port Down Sent",
14311195Ssam 	"Read Statistics Sent"
14411195Ssam };
14511195Ssam #endif
14611195Ssam 
14711195Ssam #define SCANINTERVAL	10	/* seconds */
14811195Ssam #define MAXINTERVAL	20	/* seconds (max action) */
14911195Ssam 
15011195Ssam /*
15111195Ssam  * Cause a device interrupt.  This code uses a buffer starting at
15211195Ssam  * location zero on the unibus (which is already mapped by the
15311195Ssam  * autoconfigure code in the kernel).
15411195Ssam  */
15511195Ssam hyprobe(reg)
15611195Ssam 	caddr_t reg;
15711195Ssam {
15811195Ssam 	register int br, cvec;		/* r11, r10 value-result */
15911195Ssam 	register struct hydevice *addr = (struct hydevice *) reg;
16011195Ssam 
16111195Ssam #ifdef lint
16211195Ssam 	br = 0; cvec = br; br = cvec;
16311195Ssam 	hyint(0);
16411195Ssam #endif
16511195Ssam 	/*
16611195Ssam 	 * request adapter status to a buffer starting at unibus location 0
16711195Ssam 	 */
16811195Ssam 	addr->hyd_bar = 0;
16911195Ssam 	addr->hyd_wcr = -((sizeof(struct hy_status) + 1) >> 1);
17011195Ssam 	addr->hyd_dbuf = HYF_STATUS;
17111195Ssam #ifdef PI13
17211195Ssam 	addr->hyd_csr |= S_GO | S_IE | S_IATTN;
17311195Ssam #else
17411195Ssam 	addr->hyd_csr |= S_GO | S_IE;
17511195Ssam #endif
17611195Ssam 	DELAY(10000);
17711195Ssam #ifdef PI13
17811195Ssam 	addr->hyd_csr |= S_CLRINT;	/* clear any stacked interrupts */
17911195Ssam #endif
18011195Ssam 	addr->hyd_csr &= ~(S_IE | S_CLRINT);	/* disable further interrupts */
18111195Ssam 	return(1);
18211207Ssam }
18311195Ssam 
18411195Ssam /*
18511195Ssam  * Interface exists: make available by filling in network interface
18611195Ssam  * record.  System will initialize the interface when it is ready
18711195Ssam  * to accept packets.
18811195Ssam  */
18911195Ssam hyattach(ui)
19011195Ssam 	struct uba_device *ui;
19111195Ssam {
19211195Ssam 	register struct hy_softc *is = &hy_softc[ui->ui_unit];
19311195Ssam 	register struct ifnet *ifp = &is->hy_if;
19411195Ssam 
19511195Ssam 	ifp->if_unit = ui->ui_unit;
19611195Ssam 	ifp->if_name = "hy";
19711195Ssam 	ifp->if_mtu = HYMTU;
19811195Ssam 	ifp->if_net = ui->ui_flags;
19911195Ssam 	is->hy_state = STARTUP;		/* don't allow state transitions yet */
20011195Ssam 	ifp->if_init = hyinit;
201*13058Ssam 	ifp->if_ioctl = hyioctl;
20211195Ssam 	ifp->if_output = hyoutput;
20311207Ssam 	ifp->if_reset = hyreset;
20411195Ssam 	ifp->if_watchdog = hywatch;
20511195Ssam 	ifp->if_timer = SCANINTERVAL;
20611195Ssam 	is->hy_ifuba.ifu_flags = UBA_CANTWAIT;
20711207Ssam #ifdef notdef
20811195Ssam 	is->hy_ifuba.ifu_flags |= UBA_NEEDBDP;
20911195Ssam #endif
21011195Ssam 	if_attach(ifp);
21111207Ssam }
21211195Ssam 
21311195Ssam /*
21411195Ssam  * Reset of interface after UNIBUS reset.
21511195Ssam  * If interface is on specified uba, reset its state.
21611195Ssam  */
21711195Ssam hyreset(unit, uban)
21811195Ssam 	int unit, uban;
21911195Ssam {
22011195Ssam 	register struct uba_device *ui = hyinfo[unit];
22111195Ssam 
22211207Ssam 	if (unit >= NHY || ui == 0 || ui->ui_alive == 0 ||
22311207Ssam 	  ui->ui_ubanum != uban)
22411195Ssam 		return;
22511195Ssam 	printf(" hy%d", unit);
22611195Ssam 	hyinit(unit);
22711207Ssam }
22811195Ssam 
22911195Ssam /*
23011195Ssam  * Initialization of interface; clear recorded pending
23111195Ssam  * operations, and reinitialize UNIBUS usage.
23211195Ssam  */
23311195Ssam hyinit(unit)
23411195Ssam 	int unit;
23511195Ssam {
23611195Ssam 	register struct hy_softc *is = &hy_softc[unit];
23711195Ssam 	register struct uba_device *ui = hyinfo[unit];
23811195Ssam 	int s;
23911195Ssam 
24011195Ssam 	if (if_ubainit(&is->hy_ifuba, ui->ui_ubanum,
24111195Ssam 	    sizeof (struct hy_hdr), (int)btoc(HYMTU)) == 0) {
24211195Ssam #ifdef DEBUG
24311207Ssam 		if (hy_nodebug & 4)
24411207Ssam 			hy_debug_flag = 1;
24511195Ssam #endif
24611195Ssam 		printf("hy%d: can't initialize\n", unit);
24711195Ssam 		is->hy_if.if_flags &= ~IFF_UP;
24811195Ssam 		return;
24911195Ssam 	}
25011195Ssam 	/*
25111207Ssam 	 * Issue wait for message and start the state machine
25211195Ssam 	 */
25311195Ssam 	s = splimp();
25411195Ssam 	is->hy_state = IDLE;
25511195Ssam 	is->hy_flags = RQ_STATUS | RQ_STATISTICS | RQ_MARKUP;
25611195Ssam 	is->hy_retry = 0;
25711195Ssam 	hyact(ui);
25811195Ssam 	splx(s);
25911207Ssam }
26011195Ssam 
26111195Ssam /*
26211207Ssam  * Issue a command to the adapter
26311195Ssam  */
26411195Ssam hystart(ui, cmd, count, ubaddr)
26511195Ssam 	struct uba_device *ui;
26611207Ssam 	int cmd, count, ubaddr;
26711195Ssam {
26811195Ssam 	register struct hy_softc *is = &hy_softc[ui->ui_unit];
26911195Ssam 	register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
27011195Ssam 
27111195Ssam #ifdef DEBUG
27211207Ssam 	printD("hy%d: hystart cmd = 0x%x count=%d ubaddr=0x%x\n",
27311207Ssam 		ui->ui_unit, cmd, count, ubaddr);
27411195Ssam 	printD("hy%d: - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
27511207Ssam 		ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
27611207Ssam 		addr->hyd_wcr);
27711195Ssam #endif
27811207Ssam 	if (((is->hy_flags & RQ_REISSUE) == 0) &&
27911207Ssam 	  (cmd != HYF_STATUS) && (cmd != HYF_END_OP) && (cmd != HYF_RSTATS)) {
28011195Ssam 		is->hy_savedstate = is->hy_state;
28111195Ssam 		is->hy_savedcmd = cmd;
28211195Ssam 		is->hy_savedcount = count;
28311195Ssam 		is->hy_savedaddr = ubaddr;
28411195Ssam 	}
28511195Ssam 	addr->hyd_bar = ubaddr & 0xffff;
28611207Ssam 	addr->hyd_wcr = is->hy_lastwcr = -((count+1) >> 1);
28711195Ssam 	addr->hyd_dbuf = cmd;
28811195Ssam #ifdef PI13
28911195Ssam 	addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE | S_IATTN;
29011195Ssam #else
29111195Ssam 	addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE;
29211195Ssam #endif
29311195Ssam #ifdef DEBUG
29411195Ssam 	printD("hy%d: exit hystart - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
29511207Ssam 		ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
29611207Ssam 		addr->hyd_wcr);
29711195Ssam #endif
29811195Ssam #ifdef HYLOG
29911195Ssam 	{
30011195Ssam 		struct {
30111207Ssam 			u_char	hcmd;
30211207Ssam 			u_char	hstate;
30311207Ssam 			short	hcount;
30411195Ssam 		} hcl;
30511195Ssam 
30611195Ssam 		hcl.hcmd = cmd;
30711195Ssam 		hcl.hstate = is->hy_state;
30811195Ssam 		hcl.hcount = count;
30911195Ssam 		hylog(HYL_CMD, sizeof(hcl), (char *)&hcl);
31011195Ssam 	}
31111195Ssam #endif
31211195Ssam 	is->hy_ntime = 0;
31311207Ssam }
31411195Ssam 
31511195Ssam int hyint_active = 0;		/* set during hy interrupt */
31611195Ssam /*
31711207Ssam  * Hyperchannel interface interrupt.
31811195Ssam  *
31911195Ssam  * An interrupt can occur for many reasons.  Examine the status of
32011195Ssam  * the hyperchannel status bits to determine what to do next.
32111195Ssam  *
32211195Ssam  * If input error just drop packet.
32311195Ssam  * Otherwise purge input buffered data path and examine
32411195Ssam  * packet to determine type.  Othewise decapsulate
32511195Ssam  * packet based on type and pass to type specific higher-level
32611195Ssam  * input routine.
32711195Ssam  */
32811195Ssam hyint(unit)
32911195Ssam 	int unit;
33011195Ssam {
33111195Ssam 	register struct hy_softc *is = &hy_softc[unit];
33211195Ssam 	register struct uba_device *ui = hyinfo[unit];
33311207Ssam 	register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
33411195Ssam 
33511207Ssam 	if (hyint_active)
33611195Ssam 		panic("RECURSIVE HYPERCHANNEL INTERRUPT");
33711195Ssam 	hyint_active++;
33811195Ssam #ifdef DEBUG
33911195Ssam 	printD("hy%d: hyint enter - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
34011195Ssam 		unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr);
34111195Ssam #endif
34211195Ssam #ifdef HYLOG
34311195Ssam logit:
34411195Ssam 	{
34511195Ssam 		struct {
34611207Ssam 			u_char	hstate;
34711207Ssam 			u_char	hflags;
34811207Ssam 			short	hcsr;
34911207Ssam 			short	hwcr;
35011195Ssam 		} hil;
35111195Ssam 		hil.hstate = is->hy_state;
35211195Ssam 		hil.hflags = is->hy_flags;
35311195Ssam 		hil.hcsr = addr->hyd_csr;
35411195Ssam 		hil.hwcr = addr->hyd_wcr;
35511195Ssam 		hylog(HYL_INT, sizeof(hil), (char *)&hil);
35611195Ssam 	}
35711195Ssam #endif
35811207Ssam 	if (HYS_ERROR(addr) && ((addr->hyd_csr & S_ATTN) == 0)) {
35911195Ssam 		/*
36011207Ssam 		 * Error bit set, some sort of error in the interface.
36111195Ssam 		 *
36211207Ssam 		 * The adapter sets attn on command completion so that's not
36311207Ssam 		 * a real error even though the interface considers it one.
36411195Ssam 		 */
36511195Ssam #ifdef DEBUG
36611207Ssam 		if (hy_nodebug & 4)
36711207Ssam 			hy_debug_flag = 1;
36811195Ssam #endif
36911207Ssam 		printf("csr = 0x%b\nbar = 0x%x\nwcr = 0x%x\n",
37011207Ssam 			addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
37111207Ssam 			addr->hyd_wcr);
37211207Ssam 		if (addr->hyd_csr & S_NEX) {
37311195Ssam 			printf("hy%d: NEX - Non Existant Memory\n", unit);
37411195Ssam #ifdef PI13
37511195Ssam 			addr->hyd_csr |= S_NEX;  /* as per PI13 manual */
37611195Ssam #else
37711195Ssam 			addr->hyd_csr &= ~S_NEX;
37811195Ssam #endif
37911195Ssam 			hycancel(ui);
38011195Ssam #ifdef PI13
38111207Ssam 		} else if (addr->hyd_csr & S_POWEROFF) {
38211207Ssam 			printf("hy%d: Power Off bit set, trying to reset\n",
38311207Ssam 				unit);
38411195Ssam 			addr->hyd_csr |= S_POWEROFF;
38511195Ssam 			DELAY(100);
38611207Ssam 			if (addr->hyd_csr & S_POWEROFF) {
38711195Ssam 				if_down(&is->hy_if);
38811195Ssam 				is->hy_state = STARTUP;
38911207Ssam 				printf(
39011207Ssam 				  "hy%d: Power Off Error, network shutdown\n",
39111207Ssam 				  unit);
39211195Ssam 			}
39311195Ssam #endif
39411195Ssam 		} else {
39511195Ssam 			printf("hy%d:  BAR overflow\n", unit);
39611195Ssam 			hycancel(ui);
39711195Ssam 		}
39811207Ssam 	} else if (HYS_NORMAL(addr)) {
39911195Ssam 		/*
40011207Ssam 		 * Normal interrupt, bump state machine unless in state
40111195Ssam 		 * waiting and no data present (assumed to be word count
40211207Ssam 		 * zero interrupt or other hardware botch).
40311195Ssam 		 */
40411207Ssam 		if (is->hy_state != WAITING || HYS_RECVDATA(addr))
40511195Ssam 			hyact(ui);
40611207Ssam 	} else if (HYS_ABNORMAL(addr)) {
40711195Ssam 		/*
40811207Ssam 		 * Abnormal termination.
40911195Ssam 		 * bump error counts, retry the last function
41011195Ssam 		 * 'MAXRETRY' times before kicking the bucket.
41111195Ssam 		 *
41211207Ssam 		 * Don't reissue the cmd if in certain states, abnormal
41311207Ssam 		 * on a reissued cmd or max retry exceeded.
41411195Ssam 		 */
41511195Ssam #ifdef HYLOG
41611195Ssam 		if (hy_log.hyl_enable != hy_log.hyl_onerr) {
41711195Ssam 			hy_log.hyl_enable = hy_log.hyl_onerr;
41811195Ssam 			goto logit;
41911195Ssam 		}
42011195Ssam #endif
42111195Ssam #ifdef DEBUG
42211207Ssam 		if (hy_nodebug & 4)
42311207Ssam 			hy_debug_flag = 1;
42411195Ssam 		printD("hy%d: abnormal interrupt, driver state \"%s\" (%d)\n",
42511195Ssam 			unit, hy_state_names[is->hy_state], is->hy_state);
42611207Ssam 		printD("\tflags 0x%x ilen %d olen %d lastwcr %d retry %d\n",
42711207Ssam 			is->hy_flags, is->hy_ilen, is->hy_olen,
42811207Ssam 			is->hy_lastwcr, is->hy_retry);
42911207Ssam 		printD("\tsaved: state %d count %d cmd 0x%x ptr 0x%x\n",
43011207Ssam 			is->hy_savedstate, is->hy_savedcount,
43111207Ssam 			is->hy_savedaddr, is->hy_savedcmd);
43211195Ssam #endif
43311195Ssam #ifdef PI13
43411195Ssam 		addr->hyd_csr &= ~S_C;  /* clear the damned PI-13 */
43511195Ssam #endif
43611207Ssam 		if (is->hy_state == XMITSENT || is->hy_state == XMITDATASENT)
43711195Ssam 			is->hy_if.if_oerrors++;
43811207Ssam 		if (is->hy_state == RECVSENT || is->hy_state == RECVDATASENT)
43911195Ssam 			is->hy_if.if_ierrors++;
44011195Ssam 		if (is->hy_state == XMITDATASENT ||
44111195Ssam 		    is->hy_state == RECVSENT ||
44211195Ssam 		    is->hy_state == RECVDATASENT ||
44311207Ssam 		    (is->hy_flags & RQ_REISSUE) != 0 || is->hy_retry > MAXRETRY)
44411195Ssam 			hycancel(ui);
44511207Ssam 		else {
44611195Ssam #ifdef DEBUG
44711207Ssam 			if (hy_nodebug & 2)
44811207Ssam 				hy_debug_flag = 1;
44911195Ssam #endif
45011195Ssam 			is->hy_retry++;
45111195Ssam 			is->hy_flags |= RQ_ENDOP | RQ_STATUS | RQ_REISSUE;
45211195Ssam 			is->hy_state = IDLE;
45311195Ssam 			hyact(ui);
45411195Ssam 		}
45511195Ssam 	} else {
45611195Ssam 		/*
45711195Ssam 		 * Interrupt is neither normal, abnormal, or interface error.
45811195Ssam 		 * Ignore it. It's either stacked or a word count 0.
45911195Ssam 		 */
46011195Ssam #ifdef HYLOG
46111195Ssam 		if (hy_log.hyl_enable != hy_log.hyl_onerr) {
46211195Ssam 			hy_log.hyl_enable = hy_log.hyl_onerr;
46311195Ssam 			goto logit;
46411195Ssam 		}
46511195Ssam #endif
46611195Ssam #ifdef DEBUG
46711195Ssam 		printD("hy%d: possible stacked interrupt ignored\n", unit);
46811195Ssam #endif
46911195Ssam 	}
47011195Ssam #ifdef DEBUG
47111195Ssam 	printD("hy%d: hyint exit\n\n", unit);
47211195Ssam #endif
47311195Ssam 	hyint_active = 0;
47411195Ssam 
47511207Ssam }
47611195Ssam 
47711195Ssam /*
47811195Ssam  * Encapsulate a packet of type family for the local net.
47911195Ssam  * Use trailer local net encapsulation if enough data in first
48011195Ssam  * packet leaves a multiple of 512 bytes of data in remainder.
48111195Ssam  */
48211195Ssam hyoutput(ifp, m0, dst)
48311195Ssam 	struct ifnet *ifp;
48411195Ssam 	struct mbuf *m0;
48511195Ssam 	struct sockaddr *dst;
48611195Ssam {
48711195Ssam 	register struct hym_hdr *hym;
48811195Ssam 	register struct mbuf *m;
48911195Ssam #ifdef HYROUTE
49011195Ssam 	register struct hyroute *r = &hy_route[ifp->if_unit];
49111195Ssam #endif
49211195Ssam 	short dtype;		/* packet type */
49311195Ssam 	int dhost;		/* destination adapter address */
49411195Ssam 	int dlen;
49511195Ssam 	int mplen = 0;		/* message proper length */
49611195Ssam 	short loopback = 0;	/* hardware loopback requested */
49711195Ssam 	int error = 0;
49811195Ssam 	int s;
49911195Ssam 
50011195Ssam #ifdef DEBUG
50111207Ssam 	if (hy_nodebug & 8)
50211207Ssam 		hy_debug_flag = 1;
50311195Ssam #endif
50411195Ssam 	dlen = 0;
50511195Ssam 	for (m = m0; m; m = m->m_next)
50611195Ssam 		dlen += m->m_len;
50711195Ssam 	m = m0;
50811195Ssam 	switch(dst->sa_family) {
50911195Ssam 
51011195Ssam #ifdef INET
51111195Ssam 	case AF_INET: {
51211195Ssam 		register struct ip *ip = mtod(m, struct ip *);
51311195Ssam 		register struct sockaddr_in *sin = (struct sockaddr_in *)dst;
51411195Ssam 		register long hostaddr = in_lnaof(sin->sin_addr);
51511195Ssam 
51611195Ssam 		dhost = hostaddr & 0xffff;
51711195Ssam 		dtype = HYLINK_IP;
51811195Ssam #ifdef DEBUG
51911207Ssam 		printD("hy%d: output to host %x, dhost %x\n",
52011207Ssam 			ifp->if_unit, sin->sin_addr.s_addr, dhost);
52111195Ssam #endif
52211195Ssam 		/*
52311207Ssam 		 * Debugging loopback support:
52411195Ssam 		 * upper byte of 24 bit host number interpreted as follows
52511195Ssam 		 *	0x00 --> no loopback
52611195Ssam 		 *	0x01 --> hardware loop through remote adapter
52711195Ssam 		 *	other --> software loop through remote ip layer
52811195Ssam 		 */
52911195Ssam 		if (hostaddr & 0xff0000) {
53011195Ssam 			struct in_addr temp;
53111195Ssam 
53211195Ssam 			temp = ip->ip_dst;
53311195Ssam 			ip->ip_dst = ip->ip_src;
53411195Ssam 			ip->ip_src = temp;
53511195Ssam 			if ((hostaddr & 0xff0000) == 0x10000)
53611195Ssam 				loopback = H_LOOPBK;
53711195Ssam 		}
53811195Ssam 		/*
53911195Ssam 		 * If entire packet won't fit in message proper, just
54011195Ssam 		 * send hyperchannel hardware header and ip header in
54111195Ssam 		 * message proper.  If that won't fit either, just send
54211195Ssam 		 * the maximum message proper.
54311195Ssam 		 *
54411195Ssam 		 * This insures that the associated data is at least a
54511195Ssam 		 * TCP/UDP header in length and thus prevents potential
54611195Ssam 		 * problems with very short word counts.
54711195Ssam 		 */
54811195Ssam 		if (dlen > MPSIZE - sizeof (struct hy_hdr)) {
54911195Ssam 			mplen = sizeof(struct hy_hdr) + (ip->ip_hl << 2);
55011195Ssam 			if (mplen > MPSIZE)
55111195Ssam 				mplen = MPSIZE;
55211195Ssam 		}
55311195Ssam 		break;
55411195Ssam 	}
55511195Ssam #endif
55611195Ssam 
55711195Ssam 	default:
55811207Ssam 		printf("hy%d: can't handle af%d\n", ifp->if_unit,
55911207Ssam 			dst->sa_family);
56011195Ssam #ifdef DEBUG
56111207Ssam 		if (hy_nodebug & 4)
56211207Ssam 			hy_debug_flag = 1;
56311195Ssam #endif
56411195Ssam 		error = EAFNOSUPPORT;
56511195Ssam 		goto drop;
56611195Ssam 	}
56711195Ssam 
56811195Ssam 	/*
56911207Ssam 	 * Add the software and hardware hyperchannel headers.
57011195Ssam 	 * If there's not enough space in the first mbuf, allocate another.
57111195Ssam 	 * If that should fail, drop this sucker.
57211195Ssam 	 * No extra space for headers is allocated.
57311195Ssam 	 */
57411195Ssam 	if (m->m_off > MMAXOFF ||
57511195Ssam 	    MMINOFF + sizeof(struct hym_hdr) > m->m_off) {
57611207Ssam 		m = m_get(M_DONTWAIT, MT_HEADER);
57711195Ssam 		if (m == 0) {
57811195Ssam 			m = m0;
57911195Ssam 			error = ENOBUFS;
58011195Ssam 			goto drop;
58111195Ssam 		}
58211195Ssam 		m->m_next = m0;
58311195Ssam 		m->m_off = MMINOFF;
58411195Ssam 		m->m_len = sizeof(struct hym_hdr);
58511195Ssam 	} else {
58611195Ssam 		m->m_off -= sizeof(struct hym_hdr);
58711195Ssam 		m->m_len += sizeof(struct hym_hdr);
58811195Ssam 	}
58911195Ssam 	hym = mtod(m, struct hym_hdr *);
59011195Ssam 	hym->hym_mplen = mplen;
59111195Ssam 	hym->hym_hdr.hyh_type = dtype;
59211195Ssam 	hym->hym_hdr.hyh_off = 0;
59312772Ssam 	hym->hym_hdr.hyh_from = htons((u_short)ifp->if_host[0]);
59411195Ssam 	hym->hym_hdr.hyh_param = loopback;
59511195Ssam #ifdef HYROUTE
59611207Ssam 	if (r->hyr_lasttime.tv_sec != 0) {
59711195Ssam 		register struct hy_hash *rh;
59811195Ssam 		register int i;
59911195Ssam 
60011195Ssam 		i = HYRHASH(dhost);
60111195Ssam 		rh = &r->hyr_hash[i];
60211195Ssam 		i = 0;
60311195Ssam 		while (rh->hyr_key != dhost) {
60411195Ssam 			rh++; i++;
60511195Ssam 			if (rh > &r->hyr_hash[HYRSIZE])
60611195Ssam 				rh = &r->hyr_hash[0];
60711195Ssam 			if (rh->hyr_flags == 0 || i > HYRSIZE)
60811195Ssam 				goto notfound;
60911195Ssam 		}
61011195Ssam 		if (rh->hyr_flags & HYR_GATE) {
61111195Ssam 			loopback = 0;	/* no hardware loopback on gateways */
61211195Ssam 			i = rh->hyr_nextgate;
61311195Ssam 			if (i >= rh->hyr_egate)
61411195Ssam 				rh->hyr_nextgate = rh->hyr_pgate;
61511195Ssam 			else
61611195Ssam 				rh->hyr_nextgate++;
61711195Ssam 			rh = &r->hyr_hash[r->hyr_gateway[i]];
61811195Ssam 			if ((rh->hyr_flags & HYR_DIR) == 0)
61911195Ssam 				goto notfound;
62011195Ssam 		}
62111195Ssam 		hym->hym_hdr.hyh_ctl = rh->hyr_ctl;
62211195Ssam 		hym->hym_hdr.hyh_access = rh->hyr_access;
62311195Ssam 		hym->hym_hdr.hyh_to = rh->hyr_dst;
62411195Ssam 	} else {
62511195Ssam 		hym->hym_hdr.hyh_ctl = H_XTRUNKS | H_RTRUNKS;
62611195Ssam 		hym->hym_hdr.hyh_access = 0;
62712772Ssam 		hym->hym_hdr.hyh_to = htons((u_short)dhost);
62811195Ssam 	}
62911195Ssam #else
63011195Ssam 	hym->hym_hdr.hyh_ctl = H_XTRUNKS | H_RTRUNKS;
63111195Ssam 	hym->hym_hdr.hyh_access = 0;
63211195Ssam 	hym->hym_hdr.hyh_to = htons(dhost);
63311195Ssam #endif
63411195Ssam 
63511195Ssam 	if (hym->hym_mplen) {
63611195Ssam 		hym->hym_hdr.hyh_ctl |= H_ASSOC;
63711195Ssam #ifdef DEBUG
63811207Ssam 		if (hy_nodebug & 16)
63911207Ssam 			hy_debug_flag = 1;
64011195Ssam #endif
64111207Ssam 	} else
64211207Ssam 		hym->hym_hdr.hyh_ctl &= ~H_ASSOC;
64311195Ssam #ifdef DEBUG
64411207Ssam 	printD("hy%d: output mplen=%x ctl=%x access=%x to=%x",
64511195Ssam 		ifp->if_unit, hym->hym_mplen, hym->hym_hdr.hyh_ctl,
64611207Ssam 		hym->hym_hdr.hyh_access, hym->hym_hdr.hyh_to);
64711207Ssam 	printD(" (adapter %x) from=%x param=%x type=%x off=%x\n",
64811195Ssam 		hym->hym_hdr.hyh_to_adapter,
64911195Ssam 		hym->hym_hdr.hyh_from, hym->hym_hdr.hyh_param,
65011195Ssam 		hym->hym_hdr.hyh_type, hym->hym_hdr.hyh_off);
65111195Ssam #endif
65211195Ssam 	s = splimp();
65311195Ssam 	if (IF_QFULL(&ifp->if_snd)) {
65411195Ssam 		IF_DROP(&ifp->if_snd);
65511195Ssam 		error = ENOBUFS;
65611195Ssam 		splx(s);
65711195Ssam 		goto drop;
65811195Ssam 	}
65911195Ssam 	IF_ENQUEUE(&ifp->if_snd, m);
66011195Ssam 	if (hy_softc[ifp->if_unit].hy_state == WAITING)
66111195Ssam 		hyact(hyinfo[ifp->if_unit]);
66211195Ssam 	splx(s);
66311207Ssam 	return (0);
66411195Ssam notfound:
66511207Ssam 	error = ENETUNREACH;			/* XXX */
66611195Ssam drop:
66711195Ssam 	m_freem(m);
66811207Ssam 	return (error);
66911207Ssam }
67011195Ssam 
67111195Ssam hyact(ui)
67211195Ssam 	register struct uba_device *ui;
67311195Ssam {
67411195Ssam 	register struct hy_softc *is = &hy_softc[ui->ui_unit];
67511195Ssam 	register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
67611195Ssam 
67711195Ssam actloop:
67811195Ssam #ifdef DEBUG
67911207Ssam 	printD("hy%d: hyact, enter state \"%s\"\n", ui->ui_unit,
68011207Ssam 		hy_state_names[is->hy_state]);
68111195Ssam #endif
68211195Ssam 	switch (is->hy_state) {
68311195Ssam 
68411195Ssam 	case STARTUP:
68511195Ssam 		goto endintr;
68611195Ssam 
68711195Ssam 	case IDLE: {
68811195Ssam 		register rq = is->hy_flags;
68911195Ssam 
69011195Ssam 		if (rq & RQ_STATUS) {
69111195Ssam 			is->hy_flags &= ~RQ_STATUS;
69211195Ssam 			is->hy_state = STATSENT;
69311207Ssam 			hystart(ui, HYF_STATUS, sizeof (is->hy_status),
69411207Ssam 				is->hy_ifuba.ifu_r.ifrw_info);
69511195Ssam 		} else if (rq & RQ_ENDOP) {
69611195Ssam 			is->hy_flags &= ~RQ_ENDOP;
69711195Ssam 			is->hy_state = ENDOPSENT;
69811195Ssam 			hystart(ui, HYF_END_OP, 0, 0);
69911195Ssam 		} else if (rq & RQ_STATISTICS) {
70011195Ssam 			is->hy_flags &= ~RQ_STATISTICS;
70111195Ssam 			is->hy_state = RSTATSENT;
70211207Ssam 			hystart(ui, HYF_RSTATS, sizeof (is->hy_stat),
70311207Ssam 				is->hy_ifuba.ifu_r.ifrw_info);
70411207Ssam 		} else if (HYS_RECVDATA(addr)) {
70511195Ssam 			is->hy_state = RECVSENT;
70611195Ssam 			is->hy_retry = 0;
70711207Ssam 			hystart(ui, HYF_INPUTMSG, MPSIZE,
70811207Ssam 				is->hy_ifuba.ifu_r.ifrw_info);
70911195Ssam 		} else if (rq & RQ_REISSUE) {
71011195Ssam 			is->hy_flags &= ~RQ_REISSUE;
71111195Ssam 			is->hy_state = is->hy_savedstate;
71211195Ssam #ifdef DEBUG
71311207Ssam 			printD("hy%d: reissue cmd=0x%x count=%d",
71411207Ssam 			  ui->ui_unit, is->hy_savedcmd, is->hy_savedcount);
71511207Ssam 			printD(" ubaddr=0x%x retry=%d\n",
71611207Ssam 			  is->hy_savedaddr, is->hy_retry);
71711195Ssam #endif
71811207Ssam 			hystart(ui, is->hy_savedcmd, is->hy_savedcount,
71911207Ssam 				is->hy_savedaddr);
72011195Ssam 		} else {
72111195Ssam 			register struct mbuf *m;
72211195Ssam 
72311195Ssam 			IF_DEQUEUE(&is->hy_if.if_snd, m);
72411207Ssam 			if (m != NULL) {
72511195Ssam 				register struct hym_hdr *hym;
72611195Ssam 				register int mplen;
72711195Ssam 				register int cmd;
72811195Ssam 
72911195Ssam 				is->hy_state = XMITSENT;
73011195Ssam 				is->hy_retry = 0;
73111195Ssam 				hym = mtod(m, struct hym_hdr *);
73211195Ssam #ifdef HYLOG
73311207Ssam 				hylog(HYL_XMIT, sizeof(struct hym_hdr),
73411207Ssam 					(char *)hym);
73511195Ssam #endif
73611195Ssam 				mplen = hym->hym_mplen;
73711207Ssam 				if (hym->hym_hdr.hyh_to_adapter ==
73811207Ssam 				  hym->hym_hdr.hyh_from_adapter)
73911207Ssam 					cmd = HYF_XMITLOCMSG;
74011207Ssam 				else
74111207Ssam 					cmd = HYF_XMITMSG;
74211195Ssam #ifdef DEBUG
74311195Ssam 				printD("hy%d: hym_hdr = ", ui->ui_unit);
74411207Ssam 				if (hy_debug_flag)
74511207Ssam 					hyprintdata((char *)hym,
74611207Ssam 					  sizeof (struct hym_hdr));
74711195Ssam #endif
74811195Ssam 				/*
74911207Ssam 				 * Strip off the software part of
75011195Ssam 				 * the hyperchannel header
75111195Ssam 				 */
75211195Ssam 				m->m_off += sizeof(struct hym_data);
75311195Ssam 				m->m_len -= sizeof(struct hym_data);
75411195Ssam 				is->hy_olen = if_wubaput(&is->hy_ifuba, m);
75511195Ssam 				if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
75611207Ssam 					UBAPURGE(is->hy_ifuba.ifu_uba,
75711207Ssam 						is->hy_ifuba.ifu_w.ifrw_bdp);
75811195Ssam #ifdef DEBUG
75911207Ssam 				printD(
76011207Ssam 		"hy%d: sending packet (mplen = %d, hy_olen = %d) data = ",
76111195Ssam 					ui->ui_unit, mplen, is->hy_olen);
76211207Ssam 				if (hy_debug_flag)
76311207Ssam 					hyprintdata(
76411207Ssam 					  is->hy_ifuba.ifu_w.ifrw_addr,
76511207Ssam 					  is->hy_olen);
76611195Ssam #endif
76711207Ssam 				hystart(ui, cmd,
76811207Ssam 				   (mplen == 0) ? is->hy_olen : mplen,
76911207Ssam 				   is->hy_ifuba.ifu_w.ifrw_info);
77011195Ssam 				if (mplen != 0)
77111195Ssam 					is->hy_flags |= RQ_XASSOC;
77211195Ssam 			} else if (rq & RQ_MARKDOWN) {
77311195Ssam 				is->hy_flags &= ~(RQ_MARKUP | RQ_MARKDOWN);
77411195Ssam 				is->hy_state = MARKPORT;
77511195Ssam 				is->hy_retry = 0;
77611195Ssam 				/*
77711207Ssam 				 * Port number is taken from status data
77811195Ssam 				 */
77911207Ssam 				hystart(ui,
78012772Ssam 				 (int)(HYF_MARKP0|(PORTNUM(&is->hy_status)<<2)),
78112772Ssam 				 0, 0);
78211195Ssam 			} else if (rq & RQ_MARKUP) {
78311195Ssam 				register struct ifnet *ifp = &is->hy_if;
78411207Ssam 				register struct sockaddr_in *sin =
78511207Ssam 				   (struct sockaddr_in *)&ifp->if_addr;
78611195Ssam 
78711207Ssam 				is->hy_flags &= ~RQ_MARKUP;
78811195Ssam 				is->hy_retry = 0;
78911195Ssam 				/*
79011207Ssam 				 * Fill in the Internet address
79111207Ssam 				 * from the status buffer
79211195Ssam 				 */
79311207Ssam 				printf(
79411207Ssam 	"hy%d: unit number 0x%x port %d type %x microcode level 0x%x\n",
79511195Ssam 					ui->ui_unit,
79611195Ssam 					is->hy_stat.hyc_uaddr,
79711195Ssam 					PORTNUM(&is->hy_status),
79811207Ssam 					(is->hy_stat.hyc_atype[0]<<8) |
79911207Ssam 						is->hy_stat.hyc_atype[1],
80011195Ssam 					is->hy_stat.hyc_atype[2]);
80111195Ssam 
80211207Ssam 				ifp->if_host[0] =
80311207Ssam 				  (is->hy_stat.hyc_uaddr << 8) |
80411207Ssam 					PORTNUM(&is->hy_status);
80511195Ssam 				sin->sin_family = AF_INET;
80611207Ssam 				sin->sin_addr =
80711207Ssam 				   if_makeaddr(ifp->if_net, ifp->if_host[0]);
80811195Ssam 				ifp->if_flags |= IFF_UP;
80911195Ssam 				if_rtinit(ifp, RTF_UP);
81011195Ssam #ifdef HYLOG
81111195Ssam 				hylog(HYL_UP, 0, (char *)0);
81211195Ssam #endif
81311195Ssam 			} else {
81411195Ssam 				is->hy_state = WAITING;
81511195Ssam 				is->hy_retry = 0;
81611195Ssam 				hystart(ui, HYF_WAITFORMSG, 0, 0);
81711195Ssam 			}
81811195Ssam 		}
81911207Ssam 		break;
82011195Ssam 	}
82111195Ssam 
82211195Ssam 	case STATSENT:
82312772Ssam 		bcopy(is->hy_ifuba.ifu_r.ifrw_addr, (caddr_t)&is->hy_status,
82411207Ssam 		  sizeof (struct hy_status));
82511195Ssam #ifdef DEBUG
82611207Ssam 		printD("hy%d: status - %x %x %x %x %x %x %x %x\n",
82711207Ssam 			ui->ui_unit, is->hy_status.hys_gen_status,
82811207Ssam 			is->hy_status.hys_last_fcn,
82911207Ssam 			is->hy_status.hys_resp_trunk,
83011207Ssam 			is->hy_status.hys_status_trunk,
83111207Ssam 			is->hy_status.hys_recd_resp,
83211207Ssam 			is->hy_status.hys_error,
83311207Ssam 			is->hy_status.hys_caddr,
83411207Ssam 			is->hy_status.hys_pad);
83511195Ssam #endif
83611195Ssam 		is->hy_state = IDLE;
83711195Ssam #ifdef HYLOG
83811207Ssam 		hylog(HYL_STATUS, sizeof (struct hy_status),
83911207Ssam 			(char *)&is->hy_status);
84011195Ssam #endif
84111195Ssam #ifdef HYELOG
84211195Ssam 		{
84311195Ssam 			register int i;
84411195Ssam 
84511195Ssam 			i = is->hy_status.hys_error;
84611195Ssam 			if (i < HYE_MAX)
84711195Ssam 				i = HYE_MAX;
84811195Ssam 			switch (is->hy_status.hys_last_fcn) {
84911195Ssam 				case HYF_XMITLOCMSG:
85011195Ssam 					i += HYE_MAX+1;	/* fall through */
85111195Ssam 				case HYF_XMITLSTDATA:
85211195Ssam 					i += HYE_MAX+1;	/* fall through */
85311195Ssam 				case HYF_XMITMSG:
85411195Ssam 					i += HYE_MAX+1;
85511195Ssam 			}
85611195Ssam 			hy_elog[i]++;
85711195Ssam 		}
85811195Ssam #endif
85911195Ssam 		break;
86011195Ssam 
86111195Ssam 	case RSTATSENT: {
86211207Ssam 		register struct hy_stat *p =
86311207Ssam 			(struct hy_stat *)is->hy_ifuba.ifu_r.ifrw_addr;
86411195Ssam 
86511195Ssam 		is->hy_stat.hyc_msgcnt = ntohl(p->hyc_msgcnt);
86611195Ssam 		is->hy_stat.hyc_dbcnt = ntohl(p->hyc_dbcnt);
86711195Ssam 		is->hy_stat.hyc_tbusy = ntohl(p->hyc_tbusy);
86811195Ssam 		is->hy_stat.hyc_hwret = ntohl(p->hyc_hwret);
86911195Ssam 		is->hy_stat.hyc_crcbad = ntohl(p->hyc_crcbad);
87011195Ssam 		is->hy_stat.hyc_mcret = ntohl(p->hyc_mcret);
87111195Ssam 		is->hy_stat.hyc_tdabort = ntohl(p->hyc_tdabort);
87211195Ssam 		is->hy_stat.hyc_atype[0] = p->hyc_atype[0];
87311195Ssam 		is->hy_stat.hyc_atype[1] = p->hyc_atype[1];
87411195Ssam 		is->hy_stat.hyc_atype[2] = p->hyc_atype[2];
87511195Ssam 		is->hy_stat.hyc_uaddr = p->hyc_uaddr;
87611195Ssam #ifdef DEBUG
87711207Ssam 		printD(
87811207Ssam 	"hy%d: statistics - msgcnt %d dbcnt %d hwret %d tbusy %d crcbad %d\n",
87911195Ssam 			ui->ui_unit,
88011195Ssam 			is->hy_stat.hyc_msgcnt, is->hy_stat.hyc_dbcnt,
88111195Ssam 			is->hy_stat.hyc_tbusy, is->hy_stat.hyc_hwret,
88211195Ssam 			is->hy_stat.hyc_crcbad);
88311195Ssam 		printD("	mcret %d tdabort %d atype %x %x %x uaddr %x\n",
88411195Ssam 			is->hy_stat.hyc_mcret, is->hy_stat.hyc_tdabort,
88511195Ssam 			is->hy_stat.hyc_atype[0], is->hy_stat.hyc_atype[1],
88611195Ssam 			is->hy_stat.hyc_atype[2], is->hy_stat.hyc_uaddr);
88711195Ssam #endif
88811195Ssam 		is->hy_state = IDLE;
88911195Ssam #ifdef HYLOG
89011207Ssam 		hylog(HYL_STATISTICS, sizeof (struct hy_stat),
89111207Ssam 			(char *)&is->hy_stat);
89211195Ssam #endif
89311195Ssam 		break;
89411195Ssam 	}
89511195Ssam 
89611195Ssam 	case CLEARSENT:
89711195Ssam 		is->hy_state = IDLE;
89811195Ssam 		break;
89911195Ssam 
90011195Ssam 	case ENDOPSENT:
90111195Ssam 		is->hy_state = IDLE;
90211195Ssam 		break;
90311195Ssam 
90411195Ssam 	case RECVSENT: {
90511207Ssam 		register struct hy_hdr *hyh;
90611207Ssam 		register unsigned len;
90711195Ssam 
90811207Ssam 		if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
90911207Ssam 			UBAPURGE(is->hy_ifuba.ifu_uba,
91011207Ssam 				is->hy_ifuba.ifu_r.ifrw_bdp);
91111207Ssam 		hyh = (struct hy_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr);
91211207Ssam 		len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
91311207Ssam 		if (len > MPSIZE) {
91411207Ssam 			printf("hy%d: RECVD MP > MPSIZE (%d)\n",
91511207Ssam 				ui->ui_unit, len);
91611195Ssam #ifdef DEBUG
91711207Ssam 			hy_debug_flag = 1;
91811207Ssam 			printD("hy%d: csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
91911207Ssam 				ui->ui_unit, addr->hyd_csr, HY_CSR_BITS,
92011207Ssam 				addr->hyd_bar, addr->hyd_wcr);
92111195Ssam #endif
92211207Ssam 		}
92311195Ssam #ifdef DEBUG
92411207Ssam 		printD("hy%d: recvd mp, len = %d, data = ", ui->ui_unit, len);
92511207Ssam 		if (hy_debug_flag)
92611207Ssam 			hyprintdata((char *)hyh, len);
92711195Ssam #endif
92811207Ssam 		if (hyh->hyh_ctl & H_ASSOC) {
92911207Ssam 			is->hy_state = RECVDATASENT;
93011207Ssam 			is->hy_ilen = len;
93111207Ssam 			is->hy_retry = 0;
93211207Ssam 			hystart(ui, HYF_INPUTDATA,
93312772Ssam 			  (int)(HYMTU-len+sizeof (struct hy_hdr)),
93412772Ssam 			  (int)(is->hy_ifuba.ifu_r.ifrw_info + len));
93511207Ssam 		} else {
93612772Ssam 			hyrecvdata(ui, hyh, (int)len);
93711207Ssam 			is->hy_state = IDLE;
93811195Ssam 		}
93911207Ssam 		break;
94011207Ssam 	}
94111195Ssam 
94211195Ssam 	case RECVDATASENT: {
94311207Ssam 		register struct hy_hdr *hyh;
94411207Ssam 		register unsigned len;
94511195Ssam 
94611207Ssam 		if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
94711207Ssam 			UBAPURGE(is->hy_ifuba.ifu_uba,
94811207Ssam 				is->hy_ifuba.ifu_r.ifrw_bdp);
94911207Ssam 		hyh = (struct hy_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr);
95011207Ssam 		len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
95111195Ssam #ifdef DEBUG
95211207Ssam 		printD("hy%d: recvd assoc data, len = %d, data = ",
95311207Ssam 			ui->ui_unit, len);
95411207Ssam 		if (hy_debug_flag)
95511207Ssam 			hyprintdata((char *)hyh + is->hy_ilen, len);
95611195Ssam #endif
95712772Ssam 		hyrecvdata(ui, hyh, (int)(len + is->hy_ilen));
95811207Ssam 		is->hy_state = IDLE;
95911207Ssam 		break;
96011207Ssam 	}
96111195Ssam 
96211195Ssam 	case XMITSENT:
96311207Ssam 		if (is->hy_flags & RQ_XASSOC) {
96411207Ssam 			register unsigned len;
96511195Ssam 
96611207Ssam 			is->hy_flags &= ~RQ_XASSOC;
96711207Ssam 			is->hy_state = XMITDATASENT;
96811207Ssam 			is->hy_retry = 0;
96911207Ssam 			len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
97011207Ssam 			if (len > is->hy_olen) {
97111207Ssam 				printf(
97211207Ssam 				"hy%d: xmit error - len > hy_olen [%d > %d]\n",
97311207Ssam 				ui->ui_unit, len, is->hy_olen);
97411195Ssam #ifdef DEBUG
97511207Ssam 				hy_debug_flag = 1;
97611195Ssam #endif
97711195Ssam 			}
97811207Ssam 			hystart(ui, HYF_XMITLSTDATA, is->hy_olen - len,
97911207Ssam 				is->hy_ifuba.ifu_w.ifrw_info + len);
98011207Ssam 			break;
98111207Ssam 		}
98211207Ssam 		/* fall through to ... */
98311195Ssam 
98411195Ssam 	case XMITDATASENT:
98511207Ssam 		hyxmitdata(ui);
98611207Ssam 		is->hy_state = IDLE;
98711207Ssam 		break;
98811195Ssam 
98911195Ssam 	case WAITING:	/* wait for message complete or output requested */
99011207Ssam 		if (HYS_RECVDATA(addr))
99111195Ssam 			is->hy_state = IDLE;
99211195Ssam 		else {
99311195Ssam 			is->hy_state = CLEARSENT;
99411195Ssam 			is->hy_retry = 0;
99511195Ssam 			hystart(ui, HYF_CLRWFMSG, 0, 0);
99611195Ssam 		}
99711195Ssam 		break;
99811195Ssam 
99911195Ssam 	case MARKPORT:
100011195Ssam 		is->hy_state = STARTUP;
100111195Ssam 		is->hy_if.if_flags &= ~IFF_UP;
100211195Ssam 		goto endintr;
100311195Ssam 
100411195Ssam 	default:
100511207Ssam 		printf("hy%d: DRIVER BUG - INVALID STATE %d\n",
100611207Ssam 			ui->ui_unit, is->hy_state);
100711195Ssam 		panic("HYPERCHANNEL IN INVALID STATE");
100811195Ssam 		/*NOTREACHED*/
100911207Ssam 	}
101011195Ssam 	if (is->hy_state == IDLE)
101111195Ssam 		goto actloop;
101211195Ssam endintr:
101311195Ssam #ifdef DEBUG
101411207Ssam 	printD("hy%d: hyact, exit at \"%s\"\n", ui->ui_unit,
101511207Ssam 		hy_state_names[is->hy_state]);
101611195Ssam #endif
101711207Ssam }
101811195Ssam 
101911195Ssam /*
102011195Ssam  * Called from device interrupt when recieving data.
102111195Ssam  * Examine packet to determine type.  Decapsulate packet
102211195Ssam  * based on type and pass to type specific higher-level
102311195Ssam  * input routine.
102411195Ssam  */
102511195Ssam hyrecvdata(ui, hyh0, len)
102611195Ssam 	struct uba_device *ui;
102711195Ssam 	struct hy_hdr *hyh0;
102811195Ssam 	int len;
102911195Ssam {
103011195Ssam 	register struct hy_softc *is = &hy_softc[ui->ui_unit];
103111195Ssam 	register struct hy_hdr *hyh = hyh0;
103211195Ssam     	struct mbuf *m;
103311195Ssam 	register struct ifqueue *inq;
103411195Ssam 
103511195Ssam 	is->hy_if.if_ipackets++;
103611195Ssam #ifdef DEBUG
103711207Ssam 	printD("hy%d: recieved packet, len = %d (actual %d)\n",
103811207Ssam 		ui->ui_unit, len,
103911207Ssam 		len - (hyh->hyh_off + sizeof (struct hy_hdr)));
104011195Ssam #endif
104111195Ssam #ifdef HYLOG
104211195Ssam 	{
104311195Ssam 		struct {
104411195Ssam 			short hlen;
104511195Ssam 			struct hy_hdr hhdr;
104611195Ssam 		} hh;
104711195Ssam 		hh.hlen = len;
104811195Ssam 		hh.hhdr = *hyh;
104911195Ssam 		hylog(HYL_RECV, sizeof(hh), (char *)&hh);
105011195Ssam 	}
105111195Ssam #endif
105211195Ssam 	if (len > HYMTU + MPSIZE || len == 0)
105311195Ssam 		return;			/* sanity */
105411195Ssam 	/*
105511195Ssam 	 * Pull packet off interface.
105611195Ssam 	 */
105711195Ssam 	m = if_rubaget(&is->hy_ifuba, len, 0);
105811207Ssam 	if (m == NULL)
105911195Ssam 		return;
106011195Ssam 	switch (hyh->hyh_type) {
106111195Ssam 
106211195Ssam #ifdef INET
106311195Ssam 	case HYLINK_IP:
106411195Ssam 		/*
106511207Ssam 		 * Strip the variable portion of the hyperchannel header
106611207Ssam 		 * (fixed portion stripped in if_rubaget).
106711195Ssam 		 */
106811195Ssam 		m->m_len -= hyh->hyh_off;
106911195Ssam 		m->m_off += hyh->hyh_off;
107011195Ssam 		schednetisr(NETISR_IP);
107111195Ssam 		inq = &ipintrq;
107211195Ssam 		break;
107311195Ssam #endif
107411195Ssam 	default:
107511195Ssam 		m_freem(m);
107611195Ssam 		return;
107711195Ssam 	}
107811195Ssam 	if (IF_QFULL(inq)) {
107911195Ssam 		IF_DROP(inq);
108011195Ssam 		m_freem(m);
108111195Ssam 	} else
108211195Ssam 		IF_ENQUEUE(inq, m);
108311207Ssam }
108411195Ssam 
108511195Ssam /*
108611207Ssam  * Transmit done, release resources, bump counters.
108711195Ssam  */
108811195Ssam hyxmitdata(ui)
108911195Ssam 	struct uba_device *ui;
109011195Ssam {
109111195Ssam 	register struct hy_softc *is = &hy_softc[ui->ui_unit];
109211195Ssam 
109311195Ssam 	is->hy_if.if_opackets++;
109411207Ssam 	if (is->hy_ifuba.ifu_xtofree) {
109511195Ssam 		m_freem(is->hy_ifuba.ifu_xtofree);
109611195Ssam 		is->hy_ifuba.ifu_xtofree = 0;
109711195Ssam 	}
109811207Ssam }
109911195Ssam 
110011195Ssam hycancel(ui)
110111195Ssam 	register struct uba_device *ui;
110211195Ssam {
110311195Ssam 	register struct hy_softc *is = &hy_softc[ui->ui_unit];
110411195Ssam 
110511207Ssam 	if (is->hy_ifuba.ifu_xtofree) {
110611195Ssam 		m_freem(is->hy_ifuba.ifu_xtofree);
110711195Ssam 		is->hy_ifuba.ifu_xtofree = 0;
110811195Ssam 	}
110911195Ssam #ifdef DEBUG
111011207Ssam 	if (hy_nodebug & 1)
111111207Ssam 		hy_debug_flag = 1;
111211195Ssam #endif
111311195Ssam #ifdef DEBUG
111411195Ssam 	printD("hy%d: cancel from state \"%s\" cmd=0x%x count=%d ptr=0x%x\n",
111511195Ssam 		ui->ui_unit, hy_state_names[is->hy_state], is->hy_savedcmd,
111611195Ssam 		is->hy_savedcount, is->hy_savedaddr);
111711207Ssam 	printD("\tflags 0x%x ilen %d olen %d lastwcr %d retry %d\n",
111811207Ssam 		is->hy_flags, is->hy_ilen, is->hy_olen, is->hy_lastwcr,
111911207Ssam 		is->hy_retry);
112011207Ssam 	printD("\tsaved: state %d count %d ptr 0x%x cmd 0x%x\n",
112111207Ssam 		is->hy_savedstate, is->hy_savedcount, is->hy_savedaddr,
112211207Ssam 		is->hy_savedcmd);
112311195Ssam #endif
112411195Ssam 	is->hy_state = IDLE;
112511195Ssam 	is->hy_flags |= (RQ_ENDOP | RQ_STATUS);
112611195Ssam 	hyact(ui);
112711207Ssam }
112811195Ssam 
112911195Ssam #ifdef DEBUG
113011195Ssam hyprintdata(cp, len)
113111195Ssam 	register char *cp;
113211195Ssam 	register int len;
113311195Ssam {
113411195Ssam 	register int count = 16;
113511195Ssam 	register char *fmt;
113611195Ssam 	static char regfmt[] = "\n\t %x";
113711195Ssam 
113811195Ssam 	fmt = &regfmt[2];
113911195Ssam 	while (--len >= 0) {
114011195Ssam 		printL(fmt, *cp++ & 0xff);
114111195Ssam 		fmt = &regfmt[2];
114211195Ssam 		if (--count <= 0) {
114311195Ssam 			fmt = &regfmt[0];
114411195Ssam 			count = 16;
114511195Ssam 		}
114611195Ssam 	}
114711195Ssam 	printL("\n");
114811195Ssam }
114911195Ssam #endif
115011195Ssam 
115111195Ssam hywatch(unit)
115211195Ssam int unit;
115311195Ssam {
115411195Ssam 	register struct hy_softc *is = &hy_softc[unit];
115511195Ssam 	register struct uba_device *ui = hyinfo[unit];
115611195Ssam 	register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
115711195Ssam 	int s;
115811195Ssam 
115911195Ssam 	s = splimp();
116011195Ssam 	is->hy_if.if_timer = SCANINTERVAL;
116111207Ssam 	if (is->hy_ntime > 2 && is->hy_state != WAITING &&
116211207Ssam 	  is->hy_state != STARTUP && is->hy_state != IDLE) {
116311195Ssam 		printf("hy%d: watchdog timer expired\n", unit);
116411195Ssam 		hycancel(ui);
116511195Ssam 	}
116611195Ssam #ifdef PI13
116711195Ssam 	if ((addr->hyd_csr & S_POWEROFF) != 0) {
116811195Ssam 		addr->hyd_csr |= S_POWEROFF;
116911195Ssam 		DELAY(100);
117011195Ssam 		if ((addr->hyd_csr & S_POWEROFF) == 0) {
117111195Ssam 			printf("hy%d: adapter power restored\n", unit);
117211195Ssam 			is->hy_state = IDLE;
117311207Ssam 			is->hy_flags |=
117411207Ssam 			  (RQ_MARKUP | RQ_STATISTICS | RQ_ENDOP | RQ_STATUS);
117511195Ssam 			hyact(ui);
117611195Ssam 		}
117711195Ssam 	}
117811195Ssam #endif
117911195Ssam 	splx(s);
118011195Ssam }
118111195Ssam 
118211195Ssam #ifdef HYLOG
118311195Ssam hylog(code, len, ptr)
118411195Ssam 	int code;
118511195Ssam 	int len;
118611195Ssam 	char *ptr;
118711195Ssam {
118811195Ssam 	register unsigned char *p;
118911195Ssam 	int s;
119011195Ssam 
119111195Ssam 	s = splimp();
119211195Ssam 	if (hy_log.hyl_self != &hy_log) {
119311195Ssam 		hy_log.hyl_eptr = &hy_log.hyl_buf[HYL_SIZE];
119411195Ssam 		hy_log.hyl_ptr = &hy_log.hyl_buf[0];
119511195Ssam 		hy_log.hyl_self = &hy_log;
119611195Ssam 		hy_log.hyl_enable = HYL_DISABLED;
119711195Ssam 		hy_log.hyl_onerr = HYL_CATCH1;
119811195Ssam 	}
119911207Ssam 	if (hy_log.hyl_enable == HYL_DISABLED ||
120011207Ssam 	  hy_log.hyl_enable == HYL_CAUGHT1 ||
120111207Ssam 	  hy_log.hyl_enable == HYL_CAUGHTSTATUS ||
120211207Ssam 	  (hy_log.hyl_enable == HYL_CATCHSTATUS && code != HYL_STATUS))
120311207Ssam 		goto out;
120411195Ssam 	p = hy_log.hyl_ptr;
120511195Ssam 	if (p + len + 2 >= hy_log.hyl_eptr) {
120612772Ssam 		bzero((caddr_t)p, (unsigned)(hy_log.hyl_eptr - p));
120711195Ssam 		p = &hy_log.hyl_buf[0];
120811195Ssam 		if (hy_log.hyl_enable == HYL_CATCH1) {
120911195Ssam 			hy_log.hyl_enable = hy_log.hyl_onerr = HYL_CAUGHT1;
121011195Ssam 			goto out;
121111195Ssam 		}
121211195Ssam 		if (hy_log.hyl_enable == HYL_CATCHSTATUS) {
121311195Ssam 			hy_log.hyl_enable = hy_log.hyl_onerr = HYL_CAUGHTSTATUS;
121411195Ssam 			goto out;
121511195Ssam 		}
121611195Ssam 	}
121711195Ssam 	*p++ = code;
121811195Ssam 	*p++ = len;
121912772Ssam 	bcopy(ptr, (caddr_t)p, (unsigned)len);
122011195Ssam 	hy_log.hyl_ptr = p + len;
122111195Ssam out:
122211195Ssam 	splx(s);
122311195Ssam }
122411195Ssam #endif
122511195Ssam 
122612772Ssam /*ARGSUSED*/
1227*13058Ssam hyioctl(ifp, cmd, data)
1228*13058Ssam 	register struct ifnet *ifp;
122911207Ssam 	int cmd;
123011207Ssam 	caddr_t	data;
123111195Ssam {
123211207Ssam 	int s = splimp(), error = 0;
123311195Ssam 
123411195Ssam 	switch(cmd) {
123511195Ssam 
123611195Ssam 	case HYSETROUTE:
123711207Ssam 		if (!suser()) {
123811207Ssam 			error = EPERM;
123911207Ssam 			goto bad;
124011195Ssam 		}
1241*13058Ssam 		hy_route[ifp->if_unit] = *(struct hyroute *)ifr->ifr_data;
1242*13058Ssam 		hy_route[ifp->if_unit].hyr_lasttime = time;
124311195Ssam 		break;
124411195Ssam 
124511195Ssam 	case HYGETROUTE:
1246*13058Ssam 		*(struct hyroute *)ifr->ifr_data = hy_route[ifp->if_unit];
124711195Ssam 		break;
124811195Ssam 
124911195Ssam 	default:
1250*13058Ssam 		error = EINVAL;
125111195Ssam 		break;
125211195Ssam 	}
125311207Ssam bad:
125411195Ssam 	splx(s);
125511207Ssam 	return (error);
125611195Ssam }
125711207Ssam #endif
1258