/* if_hy.c 4.3 83/02/21 */ #include "hy.h" #if NHY > 0 /* * Network Systems Copropration Hyperchanel interface * * UNTESTED WITH 4.1C */ #include "../machine/pte.h" #include "../h/param.h" #include "../h/systm.h" #include "../h/mbuf.h" #include "../h/buf.h" #include "../h/protosw.h" #include "../h/socket.h" #include "../h/vmmac.h" #include "../h/errno.h" #include "../h/time.h" #include "../h/kernel.h" #include "../h/ioctl.h" #include "../net/if.h" #include "../net/netisr.h" #include "../net/route.h" #include "../netinet/in.h" #include "../netinet/in_systm.h" #include "../netinet/ip.h" #include "../netinet/ip_var.h" #include "../vax/cpu.h" #include "../vax/mtpr.h" #include "../vaxuba/ubareg.h" #include "../vaxuba/ubavar.h" #include "../vaxif/if_hy.h" #include "../vaxif/if_hyreg.h" #include "../vaxif/if_uba.h" #define HYROUTE #define HYELOG #define HYMTU 576 int hyprobe(), hyattach(), hyinit(), 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 */ int hy_ilen; /* mp length on input */ 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 #define HYE_MAX 0x18 u_long hy_elog[(HYE_MAX+1)*4]; #endif #ifdef DEBUG #define printL lprintf #define printD if (hy_debug_flag) lprintf 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 * hy_nodebug bit 0x08 set hy_debug_flag on hyouput * hy_nodebug bit 0x10 set hy_debug_flag on hyouput with associated data */ int hy_nodebug = 0x0; #else #define printD hyvoid #endif /* * Requests for service (in order by descending priority). */ #define RQ_ENDOP 001 /* end the last adapter function */ #define RQ_REISSUE 002 /* reissue previous cmd after status */ #define RQ_STATUS 004 /* get the status of the adapter */ #define RQ_STATISTICS 010 /* get the statistics of the adapter */ #define RQ_MARKDOWN 020 /* mark this adapter port down */ #define RQ_MARKUP 040 /* mark this interface up */ #define RQ_XASSOC 0100 /* associated data to transmit */ /* * Driver states. */ #define STARTUP 0 /* initial state (before fully there) */ #define IDLE 1 /* idle state */ #define STATSENT 2 /* status cmd sent to adapter */ #define ENDOPSENT 3 /* end operation cmd sent */ #define RECVSENT 4 /* input message cmd sent */ #define RECVDATASENT 5 /* input data cmd sent */ #define XMITSENT 6 /* transmit message cmd sent */ #define XMITDATASENT 7 /* transmit data cmd sent */ #define WAITING 8 /* waiting for messages */ #define CLEARSENT 9 /* clear wait for message cmd sent */ #define MARKPORT 10 /* mark this host's adapter port down issued */ #define RSTATSENT 11 /* read statistics cmd sent to adapter */ #ifdef DEBUG char *hy_state_names[] = { "Startup", "Idle", "Status Sent", "End op Sent", "Recieve Message Proper Sent", "Recieve Data Sent", "Transmit Message Proper Sent", "Transmit Data Sent", "Wait for Message Sent", "Clear Wait for Message Sent", "Mark Port Down Sent", "Read Statistics Sent" }; #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(1); } /* * 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; ifp->if_net = ui->ui_flags; is->hy_state = STARTUP; /* don't allow state transitions yet */ ifp->if_init = hyinit; 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 = hyinfo[unit]; register struct hy_softc *is; if (unit >= NHY || ui == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban) return; printf(" hy%d", unit); 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]; int s; if (if_ubainit(&is->hy_ifuba, ui->ui_ubanum, sizeof (struct hy_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; } /* * Issue wait for message and start the state machine */ s = splimp(); is->hy_state = IDLE; is->hy_flags = RQ_STATUS | RQ_STATISTICS | RQ_MARKUP; is->hy_retry = 0; hyact(ui); 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; } 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: Power Off bit set, trying to reset\n", unit); addr->hyd_csr |= S_POWEROFF; DELAY(100); if (addr->hyd_csr & S_POWEROFF) { if_down(&is->hy_if); is->hy_state = STARTUP; printf( "hy%d: Power Off Error, network shutdown\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 ilen %d olen %d lastwcr %d retry %d\n", is->hy_flags, is->hy_ilen, 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++; if (is->hy_state == RECVSENT || is->hy_state == RECVDATASENT) is->hy_if.if_ierrors++; 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; } /* * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. */ hyoutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { register struct hym_hdr *hym; register struct mbuf *m; #ifdef HYROUTE register struct hyroute *r = &hy_route[ifp->if_unit]; #endif short dtype; /* packet type */ int dhost; /* destination adapter address */ int dlen; int mplen = 0; /* message proper length */ short loopback = 0; /* hardware loopback requested */ int error = 0; int s; #ifdef DEBUG if (hy_nodebug & 8) hy_debug_flag = 1; #endif dlen = 0; for (m = m0; m; m = m->m_next) dlen += m->m_len; m = m0; switch(dst->sa_family) { #ifdef INET case AF_INET: { register struct ip *ip = mtod(m, struct ip *); register struct sockaddr_in *sin = (struct sockaddr_in *)dst; register long hostaddr = in_lnaof(sin->sin_addr); dhost = hostaddr & 0xffff; dtype = HYLINK_IP; #ifdef DEBUG printD("hy%d: output to host %x, dhost %x\n", ifp->if_unit, sin->sin_addr.s_addr, dhost); #endif /* * Debugging loopback support: * upper byte of 24 bit host number interpreted as follows * 0x00 --> no loopback * 0x01 --> hardware loop through remote adapter * other --> software loop through remote ip layer */ if (hostaddr & 0xff0000) { struct in_addr temp; temp = ip->ip_dst; ip->ip_dst = ip->ip_src; ip->ip_src = temp; if ((hostaddr & 0xff0000) == 0x10000) loopback = H_LOOPBK; } /* * If entire packet won't fit in message proper, just * send hyperchannel hardware header and ip header in * message proper. If that won't fit either, just send * the maximum 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 - sizeof (struct hy_hdr)) { mplen = sizeof(struct hy_hdr) + (ip->ip_hl << 2); if (mplen > MPSIZE) mplen = MPSIZE; } break; } #endif default: printf("hy%d: can't handle af%d\n", ifp->if_unit, dst->sa_family); #ifdef DEBUG if (hy_nodebug & 4) hy_debug_flag = 1; #endif error = EAFNOSUPPORT; goto drop; } /* * 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. */ 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); } hym = mtod(m, struct hym_hdr *); hym->hym_mplen = mplen; hym->hym_hdr.hyh_type = dtype; hym->hym_hdr.hyh_off = 0; hym->hym_hdr.hyh_from = htons(ifp->if_host[0]); hym->hym_hdr.hyh_param = loopback; #ifdef HYROUTE if (r->hyr_lasttime.tv_sec != 0) { register struct hy_hash *rh; register int i; i = HYRHASH(dhost); rh = &r->hyr_hash[i]; i = 0; while (rh->hyr_key != dhost) { rh++; i++; if (rh > &r->hyr_hash[HYRSIZE]) rh = &r->hyr_hash[0]; if (rh->hyr_flags == 0 || i > HYRSIZE) goto notfound; } if (rh->hyr_flags & HYR_GATE) { loopback = 0; /* no hardware loopback on gateways */ i = rh->hyr_nextgate; if (i >= rh->hyr_egate) rh->hyr_nextgate = rh->hyr_pgate; else rh->hyr_nextgate++; rh = &r->hyr_hash[r->hyr_gateway[i]]; if ((rh->hyr_flags & HYR_DIR) == 0) goto notfound; } hym->hym_hdr.hyh_ctl = rh->hyr_ctl; hym->hym_hdr.hyh_access = rh->hyr_access; hym->hym_hdr.hyh_to = rh->hyr_dst; } else { hym->hym_hdr.hyh_ctl = H_XTRUNKS | H_RTRUNKS; hym->hym_hdr.hyh_access = 0; hym->hym_hdr.hyh_to = htons(dhost); } #else hym->hym_hdr.hyh_ctl = H_XTRUNKS | H_RTRUNKS; hym->hym_hdr.hyh_access = 0; hym->hym_hdr.hyh_to = htons(dhost); #endif headerexists: if (hym->hym_mplen) { hym->hym_hdr.hyh_ctl |= H_ASSOC; #ifdef DEBUG if (hy_nodebug & 16) hy_debug_flag = 1; #endif } else hym->hym_hdr.hyh_ctl &= ~H_ASSOC; #ifdef DEBUG printD("hy%d: output mplen=%x ctl=%x access=%x to=%x", ifp->if_unit, hym->hym_mplen, hym->hym_hdr.hyh_ctl, hym->hym_hdr.hyh_access, hym->hym_hdr.hyh_to); printD(" (adapter %x) from=%x param=%x type=%x off=%x\n", hym->hym_hdr.hyh_to_adapter, hym->hym_hdr.hyh_from, hym->hym_hdr.hyh_param, hym->hym_hdr.hyh_type, hym->hym_hdr.hyh_off); #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 = ENETUNREACH; /* XXX */ drop: m_freem(m); return (error); } 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); } 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_hdr.hyh_to_adapter == hym->hym_hdr.hyh_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 /* * Strip off the software part of * the hyperchannel header */ m->m_off += sizeof(struct hym_data); m->m_len -= sizeof(struct hym_data); is->hy_olen = if_wubaput(&is->hy_ifuba, m); 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); #endif hystart(ui, cmd, (mplen == 0) ? is->hy_olen : mplen, is->hy_ifuba.ifu_w.ifrw_info); if (mplen != 0) is->hy_flags |= RQ_XASSOC; } 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, HYF_MARKP0 | (PORTNUM(&is->hy_status) << 2), 0, 0); } else if (rq & RQ_MARKUP) { register struct ifnet *ifp = &is->hy_if; register struct sockaddr_in *sin = (struct sockaddr_in *)&ifp->if_addr; is->hy_flags &= ~RQ_MARKUP; is->hy_retry = 0; /* * Fill in the Internet address * 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]); ifp->if_host[0] = (is->hy_stat.hyc_uaddr << 8) | PORTNUM(&is->hy_status); sin->sin_family = AF_INET; sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]); ifp->if_flags |= IFF_UP; if_rtinit(ifp, RTF_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, &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; is->hy_stat.hyc_msgcnt = ntohl(p->hyc_msgcnt); is->hy_stat.hyc_dbcnt = ntohl(p->hyc_dbcnt); is->hy_stat.hyc_tbusy = ntohl(p->hyc_tbusy); is->hy_stat.hyc_hwret = ntohl(p->hyc_hwret); is->hy_stat.hyc_crcbad = ntohl(p->hyc_crcbad); is->hy_stat.hyc_mcret = ntohl(p->hyc_mcret); is->hy_stat.hyc_tdabort = ntohl(p->hyc_tdabort); is->hy_stat.hyc_atype[0] = p->hyc_atype[0]; is->hy_stat.hyc_atype[1] = p->hyc_atype[1]; is->hy_stat.hyc_atype[2] = p->hyc_atype[2]; is->hy_stat.hyc_uaddr = p->hyc_uaddr; #ifdef DEBUG printD( "hy%d: statistics - msgcnt %d dbcnt %d hwret %d tbusy %d crcbad %d\n", ui->ui_unit, is->hy_stat.hyc_msgcnt, is->hy_stat.hyc_dbcnt, is->hy_stat.hyc_tbusy, is->hy_stat.hyc_hwret, is->hy_stat.hyc_crcbad); printD(" mcret %d tdabort %d atype %x %x %x uaddr %x\n", is->hy_stat.hyc_mcret, is->hy_stat.hyc_tdabort, 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 hy_hdr *hyh; register unsigned len; if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_r.ifrw_bdp); hyh = (struct hy_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); #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 } #ifdef DEBUG printD("hy%d: recvd mp, len = %d, data = ", ui->ui_unit, len); if (hy_debug_flag) hyprintdata((char *)hyh, len); #endif if (hyh->hyh_ctl & H_ASSOC) { is->hy_state = RECVDATASENT; is->hy_ilen = len; is->hy_retry = 0; hystart(ui, HYF_INPUTDATA, HYMTU-len+sizeof (struct hy_hdr), is->hy_ifuba.ifu_r.ifrw_info + len); } else { hyrecvdata(ui, hyh, len); is->hy_state = IDLE; } break; } case RECVDATASENT: { register struct hy_hdr *hyh; register unsigned len; if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_r.ifrw_bdp); hyh = (struct hy_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 *)hyh + is->hy_ilen, len); #endif hyrecvdata(ui, hyh, len + is->hy_ilen); is->hy_state = IDLE; break; } case XMITSENT: if (is->hy_flags & RQ_XASSOC) { register unsigned 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 + 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; 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 return (0); } /* * Called from device interrupt when recieving data. * Examine packet to determine type. Decapsulate packet * based on type and pass to type specific higher-level * input routine. */ hyrecvdata(ui, hyh0, len) struct uba_device *ui; struct hy_hdr *hyh0; int len; { register struct hy_softc *is = &hy_softc[ui->ui_unit]; register struct hy_hdr *hyh = hyh0; struct mbuf *m; register struct ifqueue *inq; is->hy_if.if_ipackets++; #ifdef DEBUG printD("hy%d: recieved packet, len = %d (actual %d)\n", ui->ui_unit, len, len - (hyh->hyh_off + sizeof (struct hy_hdr))); #endif #ifdef HYLOG { struct { short hlen; struct hy_hdr hhdr; } hh; hh.hlen = len; hh.hhdr = *hyh; 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); if (m == NULL) return; switch (hyh->hyh_type) { #ifdef INET case HYLINK_IP: /* * Strip the variable portion of the hyperchannel header * (fixed portion stripped in if_rubaget). */ m->m_len -= hyh->hyh_off; m->m_off += hyh->hyh_off; schednetisr(NETISR_IP); inq = &ipintrq; break; #endif default: m_freem(m); 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 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 ilen %d olen %d lastwcr %d retry %d\n", is->hy_flags, is->hy_ilen, 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(); is->hy_if.if_timer = SCANINTERVAL; if (is->hy_ntime > 2 && is->hy_state != WAITING && is->hy_state != STARTUP && is->hy_state != IDLE) { printf("hy%d: watchdog timer expired\n", unit); hycancel(ui); } #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\n", unit); is->hy_state = IDLE; is->hy_flags |= (RQ_MARKUP | RQ_STATISTICS | RQ_ENDOP | RQ_STATUS); hyact(ui); } } #endif splx(s); } #ifdef HYLOG hylog(code, len, ptr) int code; int 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_DISABLED; hy_log.hyl_onerr = HYL_CATCH1; } if (hy_log.hyl_enable == HYL_DISABLED || hy_log.hyl_enable == HYL_CAUGHT1 || hy_log.hyl_enable == HYL_CAUGHTSTATUS || (hy_log.hyl_enable == HYL_CATCHSTATUS && code != HYL_STATUS)) goto out; p = hy_log.hyl_ptr; if (p + len + 2 >= hy_log.hyl_eptr) { bzero(p, hy_log.hyl_eptr - p); p = &hy_log.hyl_buf[0]; if (hy_log.hyl_enable == HYL_CATCH1) { hy_log.hyl_enable = hy_log.hyl_onerr = HYL_CAUGHT1; goto out; } if (hy_log.hyl_enable == HYL_CATCHSTATUS) { hy_log.hyl_enable = hy_log.hyl_onerr = HYL_CAUGHTSTATUS; goto out; } } *p++ = code; *p++ = len; bcopy(ptr, p, len); hy_log.hyl_ptr = p + len; out: splx(s); } #endif hyioctl(dev, cmd, data, flag) dev_t dev; int cmd; caddr_t data; int flag; { int s = splimp(), error = 0; if (minor(dev) >= NHY) { error = ENXIO; goto bad; } switch(cmd) { case HYSETROUTE: if (!suser()) { error = EPERM; goto bad; } hy_route[minor(dev)] = *(struct hyroute *)data; hy_route[minor(dev)].hyr_lasttime = time; break; case HYGETROUTE: *(struct hyroute *)data = hy_route[minor(dev)]; break; default: error = ENXIO; break; } bad: splx(s); return (error); } #endif