+/* if_hy.c 4.1 83/02/20 */
+
+#include "hy.h"
+#if NHY > 0
+
+/*
+ * Network Systems Copropration Hyperchanel interface
+ *
+ * MUST BE UPDATED FOR 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/cpu.h"
+#include "../h/vmmac.h"
+#include "../h/dir.h"
+#include "../h/user.h"
+#include "../h/errno.h"
+
+#include "../net/if.h"
+#include "../net/route.h"
+
+#include "../netinet/in.h"
+#include "../netinet/in_systm.h"
+#include "../netinet/ip.h"
+#include "../netinet/ip_var.h"
+
+#include "../vaxif/if_hy.h"
+#include "../vaxif/if_uba.h"
+#include "../vaxif/if_hyroute.h"
+
+#include "../vax/mtpr.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.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 /* max error code is 0x17, 0x18 bin gets out of range */
+
+unsigned 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 */
+
+/*
+ * more flags
+ */
+
+#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);
+} /* hyprobe */
+
+/*
+ * 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_ubareset = hyreset;
+ ifp->if_watchdog = hywatch;
+ ifp->if_timer = SCANINTERVAL;
+ is->hy_ifuba.ifu_flags = UBA_CANTWAIT;
+#ifdef SUPBDP
+ is->hy_ifuba.ifu_flags |= UBA_NEEDBDP;
+#endif
+ if_attach(ifp);
+} /* hyattach */
+
+/*
+ * 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);
+} /* hyreset */
+
+/*
+ * 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);
+} /* hyinit */
+
+
+/*
+ * issue a command to the adapter
+ */
+hystart(ui, cmd, count, ubaddr)
+ struct uba_device *ui;
+ int cmd;
+ int count;
+ int 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); /* was byte count */
+ 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 {
+ unsigned char hcmd;
+ unsigned 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;
+} /* hystart */
+
+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 {
+ unsigned char hstate;
+ unsigned 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 (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) != 0) {
+ 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) != 0) {
+ 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) != 0) {
+ 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 (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 || RECV_DATA(addr))
+ hyact(ui);
+
+ } else if (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\tsaved: state %d count %d cmd 0x%x ptr 0x%x\n",
+ is->hy_flags, is->hy_ilen, is->hy_olen, is->hy_lastwcr, is->hy_retry,
+ 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++;
+ }
+
+ 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;
+
+} /* hyint */
+
+/*
+ * 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);
+ 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 != 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 (adapter %x) from=%x param=%x type=%x off=%x\n",
+ ifp->if_unit, hym->hym_mplen, hym->hym_hdr.hyh_ctl,
+ hym->hym_hdr.hyh_access, hym->hym_hdr.hyh_to,
+ 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; /* KLUDGE - should produce better error number */
+drop:
+ m_freem(m);
+ return(error);
+} /* hyoutput */
+
+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 (RECV_DATA(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 ubaddr=0x%x retry=%d\n",
+ ui->ui_unit, is->hy_savedcmd,
+ is->hy_savedcount, 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 != 0) {
+ 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;
+ cmd = (hym->hym_hdr.hyh_to_adapter == hym->hym_hdr.hyh_from_adapter) ?
+ HYF_XMITLOCMSG : 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);
+#ifdef SUPBDP
+ if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
+ UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_w.ifrw_bdp);
+#endif
+#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;
+
+#ifdef SUPBDP
+ if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) /* purge the BDP */
+ UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_r.ifrw_bdp);
+#endif
+ 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;
+
+#ifdef SUPBDP
+ if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP) /* purge the BDP */
+ UBAPURGE(is->hy_ifuba.ifu_uba, is->hy_ifuba.ifu_r.ifrw_bdp);
+#endif
+ 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; /* number of bytes sent in message proper */
+
+ 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 (RECV_DATA(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*/
+
+ } /* end of switch */
+
+ 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);
+} /* hyact */
+
+/*
+ * 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 == 0)
+ 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;
+ /*
+ * sent the packet up the chain to IP
+ */
+ 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);
+} /* hyrecvdata */
+
+/*
+ * 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) { /* free left over pages */
+ m_freem(is->hy_ifuba.ifu_xtofree);
+ is->hy_ifuba.ifu_xtofree = 0;
+ }
+} /* hyxmitdata */
+
+hycancel(ui)
+ register struct uba_device *ui;
+{
+ register struct hy_softc *is = &hy_softc[ui->ui_unit];
+
+ if (is->hy_ifuba.ifu_xtofree) { /* free left over pages */
+ 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\tsaved: state %d count %d ptr 0x%x cmd 0x%x\n",
+ is->hy_flags, is->hy_ilen, is->hy_olen, is->hy_lastwcr, is->hy_retry,
+ 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);
+} /* hycancel */
+
+#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, addr, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+{
+ register struct hyroute *r = &hy_route[minor(dev)];
+ register int s;
+
+ if (minor(dev) >= NHY) {
+ u.u_error = ENXIO;
+ return;
+ }
+ s = splimp();
+
+ switch(cmd) {
+
+ case HYSETROUTE:
+ if (suser()) {
+ if (copyin(addr, (caddr_t)r, sizeof(*r)))
+ u.u_error = EFAULT;
+ r->hyr_lasttime = time;
+ }
+ break;
+
+ case HYGETROUTE:
+ if (copyout((caddr_t)r, addr, sizeof(*r)))
+ u.u_error = EFAULT;
+ break;
+
+ default:
+ u.u_error = ENXIO;
+ break;
+ }
+ splx(s);
+}