/* * @(#)if_hy.c 7.1 (Berkeley) 6/5/86 */ /* * 4.2 BSD Unix Kernel - Vax Network Interface Support * * $Header: if_hy.c,v 10.1 84/07/22 21:02:56 steveg Exp $ * $Locker: $ * * Modifications from Berkeley 4.2 BSD * Copyright (c) 1983, Tektronix Inc. * All Rights Reserved * * $Log: if_hy.c,v $ * Revision 10.1 84/07/22 21:02:56 steveg * define PI13 (moved from if_hyreg.h, somehow got dropped in the process) * rework hywatch to check for power fails first * * Revision 10.0 84/06/30 19:54:27 steveg * Big Build * * Revision 3.17 84/06/20 19:20:28 steveg * increment hy_ntime in hywatch * print out state name, csr, last command, and hy_flags when watchdog timer * expires * * Revision 3.16 84/06/20 19:09:34 steveg * turn on continuous logging by default * * Revision 3.15 84/05/30 22:19:09 steveg * changes to reflect new layout ot statistics data * * Revision 3.14 84/05/30 19:25:15 steveg * move driver states to if_hy.h so log printing programs can use them * * Revision 3.13 84/05/30 17:13:26 steveg * make it compile * * Revision 3.12 84/05/30 13:46:16 steveg * rework logging * * Revision 3.11 84/05/18 19:35:02 steveg * clear IFF_RUNNING and IFF_UP on unibus reset to force resource allocation * by the init routine * * Revision 3.10 84/05/04 12:14:44 steveg * more rework to make it actually work under 4.2 * * Revision 3.9 84/05/01 23:34:52 steveg * fix typo so it compiles (unit -> ui->ui_unit) * * Revision 3.8 84/05/01 23:18:30 steveg * changes after talking with rickk * - check power off more closely * - support remote loopback through A710 adapters * - IMPLINK -> HYLINK * - return EHOSTUNREACH on hyroute failure * - bump if_collisions on abnormal interrupts that aren't input or output * * */ #include "hy.h" #if NHY > 0 /* * Network Systems Copropration Hyperchanel interface */ #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 "errno.h" #include "time.h" #include "kernel.h" #include "ioctl.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/in_var.h" #include "../netinet/ip.h" #endif #include "../vax/cpu.h" #include "../vax/mtpr.h" #include "../vaxuba/ubareg.h" #include "../vaxuba/ubavar.h" /* * configuration specific paramters * - change as appropriate for particular installaions */ #define HYROUTE #define HYELOG #define HYLOG #define HYMTU 1100 #define PI13 #ifdef DEBUG #define HYLOG #endif #include "if_hy.h" #include "if_hyreg.h" #include "if_uba.h" int hyprobe(), hyattach(), hyinit(), hyioctl(); int hyoutput(), hyreset(), hywatch(); struct uba_device *hyinfo[NHY]; u_short hystd[] = { 0772410, 0 }; struct uba_driver hydriver = { hyprobe, 0, hyattach, 0, hystd, "hy", hyinfo }; /* * Hyperchannel software status per interface. * * Each interface is referenced by a network interface structure, * hy_if, which the routing code uses to locate the interface. * This structure contains the output queue for the interface, its address, ... * We also have, for each interface, a UBA interface structure, which * contains information about the UNIBUS resources held by the interface: * map registers, buffered data paths, etc. Information is cached in this * structure for use by the if_uba.c routines in running the interface * efficiently. */ struct hy_softc { struct ifnet hy_if; /* network-visible interface */ struct ifuba hy_ifuba; /* UNIBUS resources */ short hy_flags; /* flags */ short hy_state; /* driver state */ u_short hy_host; /* local host number */ struct in_addr hy_addr; /* internet address */ int hy_olen; /* packet length on output */ int hy_lastwcr; /* last command's word count */ short hy_savedstate; /* saved for reissue after status */ short hy_savedcmd; /* saved command for reissue */ int hy_savedcount; /* saved byte count for reissue */ int hy_savedaddr; /* saved unibus address for reissue */ int hy_ntime; /* number of timeouts since last cmd */ int hy_retry; /* retry counter */ struct hy_stat hy_stat; /* statistics */ struct hy_status hy_status; /* status */ } hy_softc[NHY]; #ifdef HYELOG u_long hy_elog[HYE_SIZE]; #endif #ifdef HYLOG struct hy_log hy_log; #endif #ifdef HYROUTE struct hy_route hy_route[NHY]; #endif #ifdef DEBUG #define printL printf #define printD if (hy_debug_flag) printf int hy_debug_flag = 0; /* * hy_nodebug bit 0x01 set hy_debug_flag on hycancel * hy_nodebug bit 0x02 set hy_debug_flag on command reissue * hy_nodebug bit 0x04 set hy_debug_flag on abnormal interrupt */ int hy_nodebug = 0x0; #endif #define SCANINTERVAL 10 /* seconds */ #define MAXINTERVAL 20 /* seconds (max action) */ /* * Cause a device interrupt. This code uses a buffer starting at * location zero on the unibus (which is already mapped by the * autoconfigure code in the kernel). */ hyprobe(reg) caddr_t reg; { register int br, cvec; /* r11, r10 value-result */ register struct hydevice *addr = (struct hydevice *) reg; #ifdef lint br = 0; cvec = br; br = cvec; hyint(0); #endif /* * request adapter status to a buffer starting at unibus location 0 */ addr->hyd_bar = 0; addr->hyd_wcr = -((sizeof(struct hy_status) + 1) >> 1); addr->hyd_dbuf = HYF_STATUS; #ifdef PI13 addr->hyd_csr |= S_GO | S_IE | S_IATTN; #else addr->hyd_csr |= S_GO | S_IE; #endif DELAY(10000); #ifdef PI13 addr->hyd_csr |= S_CLRINT; /* clear any stacked interrupts */ #endif addr->hyd_csr &= ~(S_IE | S_CLRINT); /* disable further interrupts */ return(sizeof(struct hydevice)); } /* * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. */ hyattach(ui) struct uba_device *ui; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; register struct ifnet *ifp = &is->hy_if; ifp->if_unit = ui->ui_unit; ifp->if_name = "hy"; ifp->if_mtu = HYMTU; is->hy_state = STARTUP; /* don't allow state transitions yet */ ifp->if_init = hyinit; ifp->if_ioctl = hyioctl; ifp->if_output = hyoutput; ifp->if_reset = hyreset; ifp->if_watchdog = hywatch; ifp->if_timer = SCANINTERVAL; is->hy_ifuba.ifu_flags = UBA_CANTWAIT; #ifdef notdef is->hy_ifuba.ifu_flags |= UBA_NEEDBDP; #endif if_attach(ifp); } /* * Reset of interface after UNIBUS reset. * If interface is on specified uba, reset its state. */ hyreset(unit, uban) int unit, uban; { register struct uba_device *ui; register struct hy_softc *is; if (unit >= NHY || (ui = hyinfo[unit]) == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban) return; printf(" hy%d", unit); is = &hy_softc[unit]; /* force unibus resource allocation */ is->hy_if.if_flags &= ~(IFF_UP|IFF_RUNNING); hyinit(unit); } /* * Initialization of interface; clear recorded pending * operations, and reinitialize UNIBUS usage. */ hyinit(unit) int unit; { register struct hy_softc *is = &hy_softc[unit]; register struct uba_device *ui = hyinfo[unit]; register struct mbuf *m; int s; if (is->hy_if.if_addrlist == 0) /* address still unknown */ return; if (is->hy_if.if_flags & IFF_RUNNING) /* just reset the device */ goto justreset; if (if_ubainit(&is->hy_ifuba, ui->ui_ubanum, sizeof (struct hym_hdr), (int)btoc(HYMTU)) == 0) { #ifdef DEBUG if (hy_nodebug & 4) hy_debug_flag = 1; #endif printf("hy%d: can't initialize\n", unit); is->hy_if.if_flags &= ~IFF_UP; return; } is->hy_if.if_flags |= IFF_RUNNING; justreset: /* * remove any left over outgoing messages, reset the hardware and * start the state machine */ s = splimp(); #ifdef HYLOG hylog(HYL_RESET, 0, (char *)0); #endif is->hy_state = IDLE; is->hy_flags = RQ_STATUS | RQ_STATISTICS | RQ_MARKUP; is->hy_retry = 0; for(;;) { IF_DEQUEUE(&is->hy_if.if_snd, m); if (m != NULL) m_freem(m); else break; } hycancel(ui); /* also bumps the state machine */ splx(s); } /* * Issue a command to the adapter */ hystart(ui, cmd, count, ubaddr) struct uba_device *ui; int cmd, count, ubaddr; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; register struct hydevice *addr = (struct hydevice *)ui->ui_addr; #ifdef DEBUG printD("hy%d: hystart cmd = 0x%x count=%d ubaddr=0x%x\n", ui->ui_unit, cmd, count, ubaddr); printD("hy%d: - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n", ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr); #endif if (((is->hy_flags & RQ_REISSUE) == 0) && (cmd != HYF_STATUS) && (cmd != HYF_END_OP) && (cmd != HYF_RSTATS)) { is->hy_savedstate = is->hy_state; is->hy_savedcmd = cmd; is->hy_savedcount = count; is->hy_savedaddr = ubaddr; } #ifdef PI13 if (addr->hyd_csr & S_POWEROFF) { printf("hy%d: \"Soft\" Adapter Power Failure (hystart)\n", ui->ui_unit); addr->hyd_csr |= S_POWEROFF; DELAY(100); if (addr->hyd_csr & S_POWEROFF) { printf( "hy%d: \"Hard\" Adapter Power Failure, Network Shutdown (hystart)\n", ui->ui_unit); if_down(&is->hy_if); is->hy_if.if_flags &= ~IFF_UP; is->hy_state = STARTUP; } else { printf("hy%d: Adapter Power Restored (hystart)\n", ui->ui_unit); } return; } #endif addr->hyd_bar = ubaddr & 0xffff; addr->hyd_wcr = is->hy_lastwcr = -((count+1) >> 1); addr->hyd_dbuf = cmd; #ifdef PI13 addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE | S_IATTN; #else addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE; #endif #ifdef DEBUG printD("hy%d: exit hystart - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n", ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr); #endif #ifdef HYLOG { struct { u_char hcmd; u_char hstate; short hcount; } hcl; hcl.hcmd = cmd; hcl.hstate = is->hy_state; hcl.hcount = count; hylog(HYL_CMD, sizeof(hcl), (char *)&hcl); } #endif is->hy_ntime = 0; } int hyint_active = 0; /* set during hy interrupt */ /* * Hyperchannel interface interrupt. * * An interrupt can occur for many reasons. Examine the status of * the hyperchannel status bits to determine what to do next. * * If input error just drop packet. * Otherwise purge input buffered data path and examine * packet to determine type. Othewise decapsulate * packet based on type and pass to type specific higher-level * input routine. */ hyint(unit) int unit; { register struct hy_softc *is = &hy_softc[unit]; register struct uba_device *ui = hyinfo[unit]; register struct hydevice *addr = (struct hydevice *)ui->ui_addr; if (hyint_active) panic("RECURSIVE HYPERCHANNEL INTERRUPT"); hyint_active++; #ifdef DEBUG printD("hy%d: hyint enter - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n", unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr); #endif #ifdef HYLOG logit: { struct { u_char hstate; u_char hflags; short hcsr; short hwcr; } hil; hil.hstate = is->hy_state; hil.hflags = is->hy_flags; hil.hcsr = addr->hyd_csr; hil.hwcr = addr->hyd_wcr; hylog(HYL_INT, sizeof(hil), (char *)&hil); } #endif if (HYS_ERROR(addr) && ((addr->hyd_csr & S_ATTN) == 0)) { /* * Error bit set, some sort of error in the interface. * * The adapter sets attn on command completion so that's not * a real error even though the interface considers it one. */ #ifdef DEBUG if (hy_nodebug & 4) hy_debug_flag = 1; #endif printf("csr = 0x%b\nbar = 0x%x\nwcr = 0x%x\n", addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr); if (addr->hyd_csr & S_NEX) { printf("hy%d: NEX - Non Existant Memory\n", unit); #ifdef PI13 addr->hyd_csr |= S_NEX; /* as per PI13 manual */ #else addr->hyd_csr &= ~S_NEX; #endif hycancel(ui); #ifdef PI13 } else if (addr->hyd_csr & S_POWEROFF) { printf("hy%d: \"Soft\" Adapter Power Failure (hyint)\n", unit); addr->hyd_csr |= S_POWEROFF; DELAY(100); if (addr->hyd_csr & S_POWEROFF) { printf( "hy%d: \"Hard\" Adapter Power Failure, Network Shutdown (hyint)\n", unit); if_down(&is->hy_if); is->hy_if.if_flags &= ~IFF_UP; is->hy_state = STARTUP; } else { printf("hy%d: Adapter Power Restored (hyint)\n", unit); } #endif } else { printf("hy%d: BAR overflow\n", unit); hycancel(ui); } } else if (HYS_NORMAL(addr)) { /* * Normal interrupt, bump state machine unless in state * waiting and no data present (assumed to be word count * zero interrupt or other hardware botch). */ if (is->hy_state != WAITING || HYS_RECVDATA(addr)) hyact(ui); } else if (HYS_ABNORMAL(addr)) { /* * Abnormal termination. * bump error counts, retry the last function * 'MAXRETRY' times before kicking the bucket. * * Don't reissue the cmd if in certain states, abnormal * on a reissued cmd or max retry exceeded. */ #ifdef HYLOG if (hy_log.hyl_enable != hy_log.hyl_onerr) { hy_log.hyl_enable = hy_log.hyl_onerr; goto logit; } #endif #ifdef DEBUG if (hy_nodebug & 4) hy_debug_flag = 1; printD("hy%d: abnormal interrupt, driver state \"%s\" (%d)\n", unit, hy_state_names[is->hy_state], is->hy_state); printD("\tflags 0x%x olen %d lastwcr %d retry %d\n", is->hy_flags, is->hy_olen, is->hy_lastwcr, is->hy_retry); printD("\tsaved: state %d count %d cmd 0x%x ptr 0x%x\n", is->hy_savedstate, is->hy_savedcount, is->hy_savedaddr, is->hy_savedcmd); #endif #ifdef PI13 addr->hyd_csr &= ~S_C; /* clear the damned PI-13 */ #endif if (is->hy_state == XMITSENT || is->hy_state == XMITDATASENT) is->hy_if.if_oerrors++; else if (is->hy_state == RECVSENT || is->hy_state == RECVDATASENT) is->hy_if.if_ierrors++; else is->hy_if.if_collisions++; /* other errors */ if (is->hy_state == XMITDATASENT || is->hy_state == RECVSENT || is->hy_state == RECVDATASENT || (is->hy_flags & RQ_REISSUE) != 0 || is->hy_retry > MAXRETRY) hycancel(ui); else { #ifdef DEBUG if (hy_nodebug & 2) hy_debug_flag = 1; #endif is->hy_retry++; is->hy_flags |= RQ_ENDOP | RQ_STATUS | RQ_REISSUE; is->hy_state = IDLE; hyact(ui); } } else { /* * Interrupt is neither normal, abnormal, or interface error. * Ignore it. It's either stacked or a word count 0. */ #ifdef HYLOG if (hy_log.hyl_enable != hy_log.hyl_onerr) { hy_log.hyl_enable = hy_log.hyl_onerr; goto logit; } #endif #ifdef DEBUG printD("hy%d: possible stacked interrupt ignored\n", unit); #endif } #ifdef DEBUG printD("hy%d: hyint exit\n\n", unit); #endif hyint_active = 0; } int hyoutprint = 0; /* * Encapsulate a packet of type family for the local net. */ hyoutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { register struct hym_hdr *hym; register struct mbuf *m; register char *mp; int dlen; /* packet size, incl hardware header, but not sw header */ int error = 0; int s; /* * Calculate packet length for later deciding whether it will fit * in a message proper or we also need associated data. */ dlen = 0; for (m = m0; m; m = m->m_next) dlen += m->m_len; m = m0; if (dst->sa_family == AF_HYLINK) { /* don't add header */ dlen -= HYM_SWLEN; goto headerexists; } /* * Add the software and hardware hyperchannel headers. * If there's not enough space in the first mbuf, allocate another. * If that should fail, drop this sucker. * No extra space for headers is allocated. */ mp = mtod(m, char *); /* save pointer to real message */ if (m->m_off > MMAXOFF || MMINOFF + sizeof(struct hym_hdr) > m->m_off) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == 0) { m = m0; error = ENOBUFS; goto drop; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = sizeof(struct hym_hdr); } else { m->m_off -= sizeof(struct hym_hdr); m->m_len += sizeof(struct hym_hdr); } dlen += sizeof(struct hym_hdr) - HYM_SWLEN; hym = mtod(m, struct hym_hdr *); bzero((caddr_t)hym, sizeof(struct hym_hdr)); switch(dst->sa_family) { #ifdef INET case AF_INET: { int i; /* * if loopback address, swizzle ip header so when * it comes back it looks like it was addressed to us */ i = hyroute(ifp, (u_long)in_lnaof(((struct sockaddr_in *)dst)->sin_addr), hym); if (i < 0) goto notfound; if (i > 0) { struct in_addr temp; temp.s_addr = ((struct ip *)mp)->ip_dst.s_addr; ((struct ip *)mp)->ip_dst.s_addr = ((struct ip *)mp)->ip_src.s_addr; ((struct ip *)mp)->ip_src.s_addr = temp.s_addr; } /* * If entire packet won't fit in message proper, just * send hyperchannel hardware header and ip header in * message proper. * * This insures that the associated data is at least a * TCP/UDP header in length and thus prevents potential * problems with very short word counts. */ if (dlen > MPSIZE) hym->hym_mplen = sizeof(struct hy_hdr) + (((struct ip *)mp)->ip_hl << 2); hym->hym_type = HYLINK_IP; break; } #endif default: printf("hy%d: can't handle af%d\n", ifp->if_unit, dst->sa_family); error = EAFNOSUPPORT; goto drop; } headerexists: /* * insure message proper is below the maximum */ if (hym->hym_mplen > MPSIZE || (dlen > MPSIZE && hym->hym_mplen == 0)) hym->hym_mplen = MPSIZE; hym->hym_from = htons(hy_softc[ifp->if_unit].hy_host); if (hym->hym_mplen) hym->hym_ctl |= H_ASSOC; else hym->hym_ctl &= ~H_ASSOC; if (hyoutprint) printf("hy%d: output mplen=%x ctl=%x access=%x to=%x from=%x param=%x type=%x\n", ifp->if_unit, hym->hym_mplen, hym->hym_ctl, hym->hym_access, hym->hym_to, hym->hym_from, hym->hym_param, hym->hym_type); #ifdef DEBUG printD("hy%d: output mplen=%x ctl=%x access=%x to=%x from=%x param=%x type=%x\n", ifp->if_unit, hym->hym_mplen, hym->hym_ctl, hym->hym_access, hym->hym_to, hym->hym_from, hym->hym_param, hym->hym_type); #endif s = splimp(); if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); error = ENOBUFS; splx(s); goto drop; } IF_ENQUEUE(&ifp->if_snd, m); if (hy_softc[ifp->if_unit].hy_state == WAITING) hyact(hyinfo[ifp->if_unit]); splx(s); return (0); notfound: error = EHOSTUNREACH; drop: m_freem(m); return (error); } int hyroute(ifp, dest, hym) register struct ifnet *ifp; u_long dest; register struct hym_hdr *hym; { #ifdef HYROUTE register struct hy_route *rt = &hy_route[ifp->if_unit]; register struct hyr_hash *rhash; register int i; #endif hym->hym_param = 0; #ifdef HYROUTE if (rt->hyr_lasttime != 0) { i = HYRHASH(dest); rhash = &rt->hyr_hash[i]; i = 0; while (rhash->hyr_key != dest) { if (rhash->hyr_flags == 0 || i > HYRSIZE) return(-1); rhash++; i++; if (rhash >= &rt->hyr_hash[HYRSIZE]) rhash = &rt->hyr_hash[0]; } if (rhash->hyr_flags & HYR_GATE) { i = rhash->hyr_nextgate; if (i >= rhash->hyr_egate) rhash->hyr_nextgate = rhash->hyr_pgate; else rhash->hyr_nextgate++; rhash = &rt->hyr_hash[rt->hyr_gateway[i]]; if ((rhash->hyr_flags & HYR_DIR) == 0) return(-1); } else if (rhash->hyr_flags & HYR_LOOP) { hym->hym_param = H_LOOPBK; /* adapter loopback */ } else if (rhash->hyr_flags & HYR_RLOOP) { hym->hym_param = H_RLOOPBK; /* A710 remote loopback */ } hym->hym_ctl = rhash->hyr_ctl; hym->hym_access = rhash->hyr_access; hym->hym_to = rhash->hyr_dst; } else { #endif hym->hym_ctl = H_XTRUNKS | H_RTRUNKS; hym->hym_access = 0; hym->hym_to = htons((u_short)dest); if (dest & 0x010000) hym->hym_param = H_LOOPBK; /* adapter loopback */ else if (dest & 0x020000) hym->hym_param = H_RLOOPBK; /* A710 remote loopback */ #ifdef HYROUTE } #endif if (hym->hym_param == 0) return(0); else return(1); } hyact(ui) register struct uba_device *ui; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; register struct hydevice *addr = (struct hydevice *)ui->ui_addr; actloop: #ifdef DEBUG printD("hy%d: hyact, enter state \"%s\"\n", ui->ui_unit, hy_state_names[is->hy_state]); #endif switch (is->hy_state) { case STARTUP: goto endintr; case IDLE: { register rq = is->hy_flags; if (rq & RQ_STATUS) { is->hy_flags &= ~RQ_STATUS; is->hy_state = STATSENT; hystart(ui, HYF_STATUS, sizeof (is->hy_status), is->hy_ifuba.ifu_r.ifrw_info); } else if (rq & RQ_ENDOP) { is->hy_flags &= ~RQ_ENDOP; is->hy_state = ENDOPSENT; hystart(ui, HYF_END_OP, 0, 0); } else if (rq & RQ_STATISTICS) { is->hy_flags &= ~RQ_STATISTICS; is->hy_state = RSTATSENT; hystart(ui, HYF_RSTATS, sizeof (is->hy_stat), is->hy_ifuba.ifu_r.ifrw_info); } else if (HYS_RECVDATA(addr)) { is->hy_state = RECVSENT; is->hy_retry = 0; hystart(ui, HYF_INPUTMSG, MPSIZE, is->hy_ifuba.ifu_r.ifrw_info + HYM_SWLEN); } else if (rq & RQ_REISSUE) { is->hy_flags &= ~RQ_REISSUE; is->hy_state = is->hy_savedstate; #ifdef DEBUG printD("hy%d: reissue cmd=0x%x count=%d", ui->ui_unit, is->hy_savedcmd, is->hy_savedcount); printD(" ubaddr=0x%x retry=%d\n", is->hy_savedaddr, is->hy_retry); #endif hystart(ui, is->hy_savedcmd, is->hy_savedcount, is->hy_savedaddr); } else { register struct mbuf *m; IF_DEQUEUE(&is->hy_if.if_snd, m); if (m != NULL) { register struct hym_hdr *hym; register int mplen; register int cmd; is->hy_state = XMITSENT; is->hy_retry = 0; hym = mtod(m, struct hym_hdr *); #ifdef HYLOG hylog(HYL_XMIT, sizeof(struct hym_hdr), (char *)hym); #endif mplen = hym->hym_mplen; if (hym->hym_to_adapter == hym->hym_from_adapter) cmd = HYF_XMITLOCMSG; else cmd = HYF_XMITMSG; #ifdef DEBUG printD("hy%d: hym_hdr = ", ui->ui_unit); if (hy_debug_flag) hyprintdata((char *)hym, sizeof (struct hym_hdr)); #endif is->hy_olen = if_wubaput(&is->hy_ifuba, m) - HYM_SWLEN; if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_w.ifrw_bdp); #ifdef DEBUG printD( "hy%d: sending packet (mplen = %d, hy_olen = %d) data = ", ui->ui_unit, mplen, is->hy_olen); if (hy_debug_flag) hyprintdata( is->hy_ifuba.ifu_w.ifrw_addr, is->hy_olen + HYM_SWLEN); #endif if (mplen == 0) { is->hy_flags &= ~RQ_XASSOC; mplen = is->hy_olen; } else { is->hy_flags |= RQ_XASSOC; } hystart(ui, cmd, mplen, is->hy_ifuba.ifu_w.ifrw_info + HYM_SWLEN); } else if (rq & RQ_MARKDOWN) { is->hy_flags &= ~(RQ_MARKUP | RQ_MARKDOWN); is->hy_state = MARKPORT; is->hy_retry = 0; /* * Port number is taken from status data */ hystart(ui, (int)(HYF_MARKP0|(PORTNUM(&is->hy_status)<<2)), 0, 0); } else if (rq & RQ_MARKUP) { register struct ifnet *ifp = &is->hy_if; is->hy_flags &= ~RQ_MARKUP; is->hy_retry = 0; /* * Fill in the host number * from the status buffer */ printf( "hy%d: unit number 0x%x port %d type %x microcode level 0x%x\n", ui->ui_unit, is->hy_stat.hyc_uaddr, PORTNUM(&is->hy_status), (is->hy_stat.hyc_atype[0]<<8) | is->hy_stat.hyc_atype[1], is->hy_stat.hyc_atype[2]); is->hy_host = (is->hy_stat.hyc_uaddr << 8) | PORTNUM(&is->hy_status); ifp->if_flags |= IFF_UP; #ifdef HYLOG hylog(HYL_UP, 0, (char *)0); #endif } else { is->hy_state = WAITING; is->hy_retry = 0; hystart(ui, HYF_WAITFORMSG, 0, 0); } } break; } case STATSENT: bcopy(is->hy_ifuba.ifu_r.ifrw_addr, (caddr_t)&is->hy_status, sizeof (struct hy_status)); #ifdef DEBUG printD("hy%d: status - %x %x %x %x %x %x %x %x\n", ui->ui_unit, is->hy_status.hys_gen_status, is->hy_status.hys_last_fcn, is->hy_status.hys_resp_trunk, is->hy_status.hys_status_trunk, is->hy_status.hys_recd_resp, is->hy_status.hys_error, is->hy_status.hys_caddr, is->hy_status.hys_pad); #endif is->hy_state = IDLE; #ifdef HYLOG hylog(HYL_STATUS, sizeof (struct hy_status), (char *)&is->hy_status); #endif #ifdef HYELOG { register int i; i = is->hy_status.hys_error; if (i > HYE_MAX) i = HYE_MAX; switch (is->hy_status.hys_last_fcn) { case HYF_XMITLOCMSG: i += HYE_MAX+1; /* fall through */ case HYF_XMITLSTDATA: i += HYE_MAX+1; /* fall through */ case HYF_XMITMSG: i += HYE_MAX+1; } hy_elog[i]++; } #endif break; case RSTATSENT: { register struct hy_stat *p = (struct hy_stat *)is->hy_ifuba.ifu_r.ifrw_addr; bcopy((caddr_t)p, (caddr_t)&is->hy_stat, sizeof(struct hy_stat)); #ifdef DEBUG printD("hy%d: statistics - df0 %d df1 %d df2 %d df3 %d\n", ui->ui_unit, (is->hy_stat.hyc_df0[0]<<16) | (is->hy_stat.hyc_df0[1]<<8) | is->hy_stat.hyc_df0[2], (is->hy_stat.hyc_df1[0]<<16) | (is->hy_stat.hyc_df1[1]<<8) | is->hy_stat.hyc_df1[2], (is->hy_stat.hyc_df2[0]<<16) | (is->hy_stat.hyc_df2[1]<<8) | is->hy_stat.hyc_df2[2], (is->hy_stat.hyc_df3[0]<<16) | (is->hy_stat.hyc_df3[1]<<8) | is->hy_stat.hyc_df3[2]); printD(" ret0 %d ret1 %d ret2 %d ret3 %d\n", (is->hy_stat.hyc_ret0[0]<<16) | (is->hy_stat.hyc_ret0[1]<<8) | is->hy_stat.hyc_ret0[2], (is->hy_stat.hyc_ret1[0]<<16) | (is->hy_stat.hyc_ret1[1]<<8) | is->hy_stat.hyc_ret1[2], (is->hy_stat.hyc_ret2[0]<<16) | (is->hy_stat.hyc_ret2[1]<<8) | is->hy_stat.hyc_ret2[2], (is->hy_stat.hyc_ret3[0]<<16) | (is->hy_stat.hyc_ret3[1]<<8) | is->hy_stat.hyc_ret3[2]); printD(" cancel %d abort %d atype %x %x %x uaddr %x\n", (is->hy_stat.hyc_cancel[0]<<8) | is->hy_stat.hyc_cancel[1], (is->hy_stat.hyc_abort[0]<<8) | is->hy_stat.hyc_abort[1], is->hy_stat.hyc_atype[0], is->hy_stat.hyc_atype[1], is->hy_stat.hyc_atype[2], is->hy_stat.hyc_uaddr); #endif is->hy_state = IDLE; #ifdef HYLOG hylog(HYL_STATISTICS, sizeof (struct hy_stat), (char *)&is->hy_stat); #endif break; } case CLEARSENT: is->hy_state = IDLE; break; case ENDOPSENT: is->hy_state = IDLE; break; case RECVSENT: { register struct hym_hdr *hym; register unsigned len; if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_r.ifrw_bdp); hym = (struct hym_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr); len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1; if (len > MPSIZE) { printf("hy%d: RECVD MP > MPSIZE (%d)\n", ui->ui_unit, len); is->hy_state = IDLE; #ifdef DEBUG hy_debug_flag = 1; printD("hy%d: csr = 0x%b, bar = 0x%x, wcr = 0x%x\n", ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr); #endif } hym->hym_mplen = len; #ifdef DEBUG printD("hy%d: recvd mp, len = %d, data = ", ui->ui_unit, len); if (hy_debug_flag) hyprintdata((char *)hym, len + HYM_SWLEN); #endif if (hym->hym_ctl & H_ASSOC) { is->hy_state = RECVDATASENT; is->hy_retry = 0; hystart(ui, HYF_INPUTDATA, (int)(HYMTU + sizeof (struct hy_hdr) - len), (int)(HYM_SWLEN + is->hy_ifuba.ifu_r.ifrw_info + len)); } else { hyrecvdata(ui, hym, (int)len + HYM_SWLEN); is->hy_state = IDLE; } break; } case RECVDATASENT: { register struct hym_hdr *hym; register unsigned len; if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_r.ifrw_bdp); hym = (struct hym_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr); len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1; #ifdef DEBUG printD("hy%d: recvd assoc data, len = %d, data = ", ui->ui_unit, len); if (hy_debug_flag) hyprintdata((char *)hym + hym->hym_mplen, len); #endif hyrecvdata(ui, hym, (int)(len + hym->hym_mplen + HYM_SWLEN)); is->hy_state = IDLE; break; } case XMITSENT: if (is->hy_flags & RQ_XASSOC) { register int len; is->hy_flags &= ~RQ_XASSOC; is->hy_state = XMITDATASENT; is->hy_retry = 0; len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1; if (len > is->hy_olen) { printf( "hy%d: xmit error - len > hy_olen [%d > %d]\n", ui->ui_unit, len, is->hy_olen); #ifdef DEBUG hy_debug_flag = 1; #endif } hystart(ui, HYF_XMITLSTDATA, is->hy_olen - len, is->hy_ifuba.ifu_w.ifrw_info + HYM_SWLEN + len); break; } /* fall through to ... */ case XMITDATASENT: hyxmitdata(ui); is->hy_state = IDLE; break; case WAITING: /* wait for message complete or output requested */ if (HYS_RECVDATA(addr)) is->hy_state = IDLE; else { is->hy_state = CLEARSENT; is->hy_retry = 0; hystart(ui, HYF_CLRWFMSG, 0, 0); } break; case MARKPORT: is->hy_state = STARTUP; if_down(&is->hy_if); is->hy_if.if_flags &= ~IFF_UP; goto endintr; default: printf("hy%d: DRIVER BUG - INVALID STATE %d\n", ui->ui_unit, is->hy_state); panic("HYPERCHANNEL IN INVALID STATE"); /*NOTREACHED*/ } if (is->hy_state == IDLE) goto actloop; endintr: ; #ifdef DEBUG printD("hy%d: hyact, exit at \"%s\"\n", ui->ui_unit, hy_state_names[is->hy_state]); #endif } struct sockproto hypproto = { PF_HYLINK }; struct sockaddr_in hypdst = { AF_HYLINK }; struct sockaddr_in hypsrc = { AF_HYLINK }; /* * Called from device interrupt when receiving data. * Examine packet to determine type. Decapsulate packet * based on type and pass to type specific higher-level * input routine. */ hyrecvdata(ui, hym, len) struct uba_device *ui; register struct hym_hdr *hym; int len; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; struct mbuf *m; register struct ifqueue *inq; is->hy_if.if_ipackets++; #ifdef DEBUG printD("hy%d: recieved packet, len = %d\n", ui->ui_unit, len); #endif #ifdef HYLOG { struct { short hlen; struct hym_hdr hhdr; } hh; hh.hlen = len; hh.hhdr = *hym; hylog(HYL_RECV, sizeof(hh), (char *)&hh); } #endif if (len > HYMTU + MPSIZE || len == 0) return; /* sanity */ /* * Pull packet off interface. */ m = if_rubaget(&is->hy_ifuba, len, 0, &is->hy_if); if (m == NULL) return; /* * if normal or adapter loopback response packet believe hym_type, * otherwise, use the raw input queue cause it's a response from an * adapter command. */ if (hym->hym_param != 0 && (u_short)hym->hym_param != 0x80ff) goto rawlinkin; switch (hym->hym_type) { #ifdef INET case HYLINK_IP: schednetisr(NETISR_IP); inq = &ipintrq; break; #endif default: rawlinkin: { struct mbuf *m0; MGET(m0, M_DONTWAIT, MT_DATA); if (m0 == 0) { m_freem(m); return; } m0->m_off = MMINOFF; m0->m_len = sizeof(struct hym_hdr); m0->m_next = m; bcopy((caddr_t)hym, mtod(m0, caddr_t), sizeof(struct hym_hdr)); m = m0; hypproto.sp_protocol = 0; hypdst.sin_addr = is->hy_addr; hypsrc.sin_addr = is->hy_addr; raw_input(m, &hypproto, (struct sockaddr *)&hypsrc, (struct sockaddr *)&hypdst); return; } } if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(m); } else IF_ENQUEUE(inq, m); } /* * Transmit done, release resources, bump counters. */ hyxmitdata(ui) struct uba_device *ui; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; is->hy_if.if_opackets++; if (is->hy_ifuba.ifu_xtofree) { m_freem(is->hy_ifuba.ifu_xtofree); is->hy_ifuba.ifu_xtofree = 0; } } hycancel(ui) register struct uba_device *ui; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; if (is->hy_ifuba.ifu_xtofree) { m_freem(is->hy_ifuba.ifu_xtofree); is->hy_ifuba.ifu_xtofree = 0; } #ifdef HYLOG hylog(HYL_CANCEL, 0, (char *)0); #endif #ifdef DEBUG if (hy_nodebug & 1) hy_debug_flag = 1; #endif #ifdef DEBUG printD("hy%d: cancel from state \"%s\" cmd=0x%x count=%d ptr=0x%x\n", ui->ui_unit, hy_state_names[is->hy_state], is->hy_savedcmd, is->hy_savedcount, is->hy_savedaddr); printD("\tflags 0x%x olen %d lastwcr %d retry %d\n", is->hy_flags, is->hy_olen, is->hy_lastwcr, is->hy_retry); printD("\tsaved: state %d count %d ptr 0x%x cmd 0x%x\n", is->hy_savedstate, is->hy_savedcount, is->hy_savedaddr, is->hy_savedcmd); #endif is->hy_state = IDLE; is->hy_flags |= (RQ_ENDOP | RQ_STATUS); hyact(ui); } #ifdef DEBUG hyprintdata(cp, len) register char *cp; register int len; { register int count = 16; register char *fmt; static char regfmt[] = "\n\t %x"; fmt = ®fmt[2]; while (--len >= 0) { printL(fmt, *cp++ & 0xff); fmt = ®fmt[2]; if (--count <= 0) { fmt = ®fmt[0]; count = 16; } } printL("\n"); } #endif hywatch(unit) int unit; { register struct hy_softc *is = &hy_softc[unit]; register struct uba_device *ui = hyinfo[unit]; register struct hydevice *addr = (struct hydevice *)ui->ui_addr; int s; s = splimp(); #ifdef PI13 if ((addr->hyd_csr & S_POWEROFF) != 0) { addr->hyd_csr |= S_POWEROFF; DELAY(100); if ((addr->hyd_csr & S_POWEROFF) == 0) { printf("hy%d: Adapter Power Restored (hywatch)\n", unit); is->hy_state = IDLE; is->hy_flags |= (RQ_MARKUP | RQ_STATISTICS | RQ_ENDOP | RQ_STATUS); hyact(ui); } } #endif if (++is->hy_ntime >= 2 && is->hy_state != WAITING && is->hy_state != STARTUP && is->hy_state != IDLE) { #ifdef HYLOG printf("hy%d: watchdog timer expired in state \"%s\"\n", unit, hy_state_names[is->hy_state]); #else printf("hy%d: watchdog timer expired in state %d\n", unit, is->hy_state); #endif printf("hy%d: last command 0x%x, flags 0x%x, csr 0x%b\n", unit, is->hy_savedcmd, is->hy_flags, addr->hyd_csr, HY_CSR_BITS); hycancel(ui); } splx(s); is->hy_if.if_timer = SCANINTERVAL; } #ifdef HYLOG hylog(code, len, ptr) int code, len; char *ptr; { register unsigned char *p; int s; s = splimp(); if (hy_log.hyl_self != &hy_log) { hy_log.hyl_eptr = &hy_log.hyl_buf[HYL_SIZE]; hy_log.hyl_ptr = &hy_log.hyl_buf[0]; hy_log.hyl_self = &hy_log; hy_log.hyl_enable = HYL_CONTINUOUS; hy_log.hyl_onerr = HYL_CONTINUOUS; hy_log.hyl_count = 0; hy_log.hyl_icount = 16; hy_log.hyl_filter = 0xffff; /* enable all */ } if (hy_log.hyl_enable == HYL_DISABLED || ((1 << code) & hy_log.hyl_filter) == 0) goto out; p = hy_log.hyl_ptr; if (p + len + 3 >= hy_log.hyl_eptr) { bzero((caddr_t)p, (unsigned)(hy_log.hyl_eptr - p)); p = &hy_log.hyl_buf[0]; if (hy_log.hyl_enable != HYL_CONTINUOUS) { hy_log.hyl_enable = HYL_DISABLED; goto out; } } *p++ = code; *p++ = len; bcopy((caddr_t)ptr, (caddr_t)p, (unsigned)len); if (hy_log.hyl_count != 0 && --hy_log.hyl_count == 0) { *p++ = '\0'; hy_log.hyl_enable = HYL_DISABLED; hy_log.hyl_count = hy_log.hyl_icount; } p += len; if (hy_log.hyl_wait != 0) { /* wakeup HYGETLOG if wanted */ if (hy_log.hyl_wait <= p - hy_log.hyl_ptr) { wakeup((caddr_t)&hy_log); hy_log.hyl_wait = 0; } else hy_log.hyl_wait -= p - hy_log.hyl_ptr; } hy_log.hyl_ptr = p; out: splx(s); } #endif /*ARGSUSED*/ hyioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *) data; struct hyrsetget *sg = (struct hyrsetget *)data; #if defined(HYLOG) || defined(HYELOG) struct hylsetget *sgl = (struct hylsetget *)data; #endif struct hy_route *r = (struct hy_route *)&hy_route[ifp->if_unit]; int s = splimp(), error = 0; #ifdef HYLOG struct hy_softc *is = &hy_softc[ifp->if_unit]; struct { u_char hstate; u_char hflags; u_short iflags; int hcmd; int herror; u_long haddr; u_long hmisc; } hil; hil.hmisc = -1; hil.hstate = is->hy_state; hil.hflags = is->hy_flags; hil.hcmd = cmd; #endif switch(cmd) { case SIOCSIFADDR: if (ifa->ifa_addr.sa_family != AF_INET) return(EINVAL); if ((ifp->if_flags & IFF_RUNNING) == 0) hyinit(ifp->if_unit); hy_softc[ifp->if_unit].hy_addr = IA_SIN(ifa)->sin_addr; #ifdef HYLOG hil.haddr = is->hy_addr.s_addr; #endif break; case HYSETROUTE: if (!suser()) { error = EPERM; goto out; } if (sg->hyrsg_len != sizeof(struct hy_route)) { error = EINVAL; goto out; } if (copyin((caddr_t)(sg->hyrsg_ptr), (caddr_t)r, sg->hyrsg_len)) { r->hyr_lasttime = 0; /* disable further routing if trouble */ error = EFAULT; goto out; } r->hyr_lasttime = time.tv_sec; #ifdef HYLOG hil.hmisc = r->hyr_lasttime; #endif break; case HYGETROUTE: if (sg->hyrsg_len < sizeof(struct hy_route)) { error = EINVAL; goto out; } if (copyout((caddr_t)r, (caddr_t) (sg->hyrsg_ptr), sizeof(struct hy_route))) { error = EFAULT; goto out; } break; #ifdef HYELOG case HYGETELOG: if (sgl->hylsg_len < sizeof(hy_elog)) { error = EINVAL; goto out; } if (copyout((caddr_t)hy_elog, sgl->hylsg_ptr, sizeof(hy_elog))) { error = EFAULT; goto out; } if (sgl->hylsg_cmd) { if (!suser()) { error = EPERM; goto out; } bzero((caddr_t)hy_elog, sizeof(hy_elog)); } break; #endif #ifdef HYLOG case HYSETLOG: if (!suser()) { error = EPERM; goto out; } hy_log.hyl_enable = HYL_DISABLED; hylog(HYL_NOP, 0, (char *)0); /* force log init */ hy_log.hyl_enable = sgl->hylsg_cmd & 0x0f; hy_log.hyl_onerr = (sgl->hylsg_cmd >> 4) & 0x0f; hy_log.hyl_filter = (sgl->hylsg_cmd >> 8) & 0xffffff; hy_log.hyl_count = hy_log.hyl_icount = sgl->hylsg_len; wakeup((caddr_t)&hy_log); /* wakeup sleeping HYGETLOG */ break; case HYGETLOG: if (sgl->hylsg_len < sizeof(hy_log)) { error = EINVAL; goto out; } if (sgl->hylsg_cmd != 0) { if (hy_log.hyl_wait) { error = EBUSY; goto out; } hy_log.hyl_wait = sgl->hylsg_cmd; sleep((caddr_t)&hy_log, PZERO - 1); } if (copyout((caddr_t)&hy_log, sgl->hylsg_ptr, sizeof(hy_log))) { error = EFAULT; goto out; } break; #endif default: error = EINVAL; break; } out: #ifdef HYLOG hil.herror = error; hil.iflags = ifp->if_flags; hil.haddr = is->hy_addr.s_addr; hylog(HYL_IOCTL, sizeof(hil), (char *)&hil); #endif splx(s); return (error); } #endif