from ACC
authorMike Karels <karels@ucbvax.Berkeley.EDU>
Wed, 18 Sep 1985 14:10:33 +0000 (06:10 -0800)
committerMike Karels <karels@ucbvax.Berkeley.EDU>
Wed, 18 Sep 1985 14:10:33 +0000 (06:10 -0800)
SCCS-vsn: sys/vax/if/if_hdh.c 6.1

usr/src/sys/vax/if/if_hdh.c [new file with mode: 0644]

diff --git a/usr/src/sys/vax/if/if_hdh.c b/usr/src/sys/vax/if/if_hdh.c
new file mode 100644 (file)
index 0000000..a7457ef
--- /dev/null
@@ -0,0 +1,687 @@
+
+
+
+/************************************************************************\
+
+     ________________________________________________________
+    /                                                        \
+   |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |
+   |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |
+   |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
+   |       AAAA AAAA      CCCC              CCCC              |
+   |      AAAA   AAAA     CCCC              CCCC              |
+   |     AAAA     AAAA    CCCC              CCCC              |
+   |    AAAA       AAAA   CCCC              CCCC              |
+   |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
+   |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |
+   | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |
+    \________________________________________________________/
+
+       Copyright (c) 1984 by Advanced Computer Communications
+       720 Santa Barbara Street, Santa Barbara, California  93101
+       (805) 963-9431
+
+       This software may be duplicated and used on systems
+       which are licensed to run U.C. Berkeley versions of
+       the UNIX operating system.  Any duplication of any
+       part of this software must include a copy of ACC's
+       copyright notice.
+
+
+File:
+               if_hdh.c
+
+Author:
+               Art Berggreen
+
+Project:
+               4.2BSD HDH
+
+Function:
+               Device specific driver for IF-11/HDH under 4.2BSD
+               networking code.
+
+Revision History:
+               31-Aug-1984: V1.0 - First Implementation. A.B.
+                6-Nov-1984: V1.1 - Supress extra "LINE DOWN" msgs. A.B.
+               13-Jan-1984: V1.2 - Add conditionals for TWG. A.B.
+
+\************************************************************************/
+
+
+
+
+/* $Header$ */
+
+#include "hdh.h"
+#ifdef NHDH > 0
+
+/*
+ *
+ * ACC IF-11/HDH interface
+ *
+ */
+
+#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 "../net/if.h"
+#include "../netimp/if_imp.h"
+
+#include "../vax/cpu.h"
+#include "../vax/mtpr.h"
+#include "../vaxif/if_hdhreg.h"
+#include "../vaxif/if_uba.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.h"
+
+int     hdhprobe(), hdhattach(), hdhrint(), hdhxint();
+struct  uba_device *hdhinfo[NHDH];
+u_short hdhstd[] = { 0 };
+struct  uba_driver hdhdriver =
+       { hdhprobe, 0, hdhattach, 0, hdhstd, "hdh", hdhinfo };
+
+#define        HDHUNIT(x)      minor(x)
+
+int    hdhinit(), hdhstart(), hdhreset();
+
+/*
+ * "Lower half" of IMP interface driver.
+ *
+ * Each IMP interface is handled by a common module which handles
+ * the IMP-host protocol and a hardware driver which manages the
+ * hardware specific details of talking with the IMP.
+ *
+ * The hardware portion of the IMP driver handles DMA and related
+ * management of UNIBUS resources.  The IMP protocol module interprets
+ * contents of these messages and "controls" the actions of the
+ * hardware module during IMP resets, but not, for instance, during
+ * UNIBUS resets.
+ *
+ * The two modules are coupled at "attach time", and ever after,
+ * through the imp interface structure.  Higher level protocols,
+ * e.g. IP, interact with the IMP driver, rather than the HDH.
+ */
+
+#define NHDHCH 2               /* no. of FDX channels for HDH */
+#define SUPR   0               /* supervisor channel */
+#define        DATA    1               /* data channel */
+#define HDHSUPR        0               /* supervisor read */
+#define HDHSUPW        1               /* supervisor write */
+#define HDHDATR        2               /* data read */
+#define HDHDATW        3               /* data write */
+
+#define HDH_UP         2       /* HDH protocol is up */
+#define HDH_STARTED    1       /* HDH has been initialized */
+
+#define HCBUSY 1               /* HDH HDX channel busy flag */
+
+/*
+/* The IF-11/HDH has four independent dath flow channels between the
+/* front-end and the host.  Two are used for reading and writing
+/* control messages and two are used for data flow.  Each IF-11/HDH
+/* has a device dependent data structure (hdh_softc) which contains
+/* an array of four channel dependent structures (hdh_chan) to maintain
+/* the context of each channel.  Channel structures can be linked into
+/* a queue of I/O requests pending for the hardware interface.
+/* UNIBUS mapping resources are allocated for each channel pair.
+*/
+
+struct hdh_chan {              /* HDH HDX channel structure */
+       struct hdh_chan *hc_next;       /* link for Start I/O queuing */
+       char            hc_chan;        /* HDX chan number */
+       char            hc_adx;         /* extended UNIBUS address bits */
+       short           hc_addr;        /* lower UNIBUS address bits */
+       short           hc_cnt;         /* byte count */
+       char            hc_func;        /* UMC I/O function */
+       char            hc_sbfc;        /* UMC I/O subfunction */
+       short           hc_flags;       /* status flags */
+};
+
+struct hdh_sioq {              /* Start I/O queue head structure */
+       struct hdh_chan *sioq_head;     /* pointer to queue head */
+       struct hdh_chan *sioq_tail;     /* pointer to queue tail */
+};
+
+struct hdh_softc {             /* HDH device dependent structure */
+       struct ifnet    *hdh_if;        /* pointer to IMP's ifnet struct */
+       struct impcb    *hdh_ic;        /* data structure shared with IMP */
+       struct ifuba    hdh_ifuba[NHDHCH]; /* UNIBUS resources */
+       struct hdh_chan hdh_chan[2*NHDHCH]; /* HDX HDH channels */
+       struct hdh_sioq hdh_sioq;       /* start i/o queue */
+       short           hdh_flags;      /* various status conditions */
+} hdh_softc[NHDH];
+
+
+/*
+ * Normally, code goes here to cause the device to interrupt to determine its
+ * interrupt vector.  However, since the UMC must be told its vector in order
+ * to interrupt, we allocate and return an unused vector and initialize the
+ * UMC.
+ */
+hdhprobe(reg)
+caddr_t reg;
+{
+       register int br, cvec;
+       struct hdhregs *addr = (struct hdhregs *)reg;
+#ifdef lint
+       br = 0; cvec = br; br = cvec;
+#endif
+
+       br = 0x15;                      /* priority 21 (5 on UNIBUS) */
+
+#ifdef HDHDEBUG
+       cvec = 0270;                    /* use constant for now ... */
+#else
+
+#ifdef VAXVMS                          /* if VMS */
+       cvec = 0270;                    /*   we can't allocate vectors */
+#else
+       cvec = (uba_hd[numuba].uh_lastiv -= 4);  /* available vector */
+#endif VAXVMS
+
+#endif HDHDEBUG
+
+       addr->ioini = (char) 0;         /* init UMC regs */
+       addr->staack = (char) 0;        /*   pass vector */
+       addr->ionmi = (char) 0;         /*     and kick UMC */
+       addr->iochn = (char) (cvec >> 2);
+       addr->csr = (short) HDH_RST;
+       addr->csr = (short) (HDH_IEN|HDH_DMA|HDH_WRT); /* set enables */
+       DELAY(5000);                    /* give the UMC some time */
+       return(1);
+}
+
+/*
+ * Call the IMP module to allow it to set up its internal
+ * state, then tie the two modules together by setting up
+ * the back pointers to common data structures.
+ */
+hdhattach(ui)
+       struct uba_device *ui;
+{
+       register struct hdh_softc *sc = &hdh_softc[ui->ui_unit];
+       register struct impcb *ip;
+       struct ifimpcb {
+               struct  ifnet ifimp_if;
+               struct  impcb ifimp_impcb;
+       } *ifimp;
+
+       if ((ifimp = (struct ifimpcb *)impattach(ui, hdhreset)) == 0)
+               panic("hdhattach");
+       sc->hdh_if = &ifimp->ifimp_if;
+       ip = &ifimp->ifimp_impcb;
+       sc->hdh_ic = ip;
+       ip->ic_init = hdhinit;
+       ip->ic_start = hdhstart;
+       sc->hdh_ifuba[ui->ui_unit].ifu_flags = UBA_CANTWAIT;
+}
+
+/*
+ * Reset interface after UNIBUS reset.
+ */
+hdhreset(unit, uban)
+int unit, uban;
+{
+       register struct uba_device *ui = hdhinfo[unit];
+       register struct hdh_softc *sc = &hdh_softc[unit];
+
+#ifdef HDHDEBUG
+       printf("HDH RESET\n");
+#endif HDHDEBUG
+
+       if ((unit >= NHDH) || (ui == 0) || (ui->ui_alive == 0)
+           || (ui->ui_ubanum != uban))
+               return;
+       printf(" hdh%d", unit);
+       (*sc->hdh_if->if_init)(unit);
+}
+
+/*
+ * Initialize the imp interface.
+ */
+
+static char init_blk[] = 
+    {
+       HDHINIT,                /* SYSINIT opcode                       */
+       HDHRQUP & 0xff,         /* control code (LSB)                   */
+       (HDHRQUP>>8) & 0xff,    /* control code (MSB)                   */
+       10,                     /* command extension len                */
+       0,                      /* loopback mode (off)                  */
+       3,                      /* our address (3=DTE)                  */
+       1,                      /* their address (1=DCE)                */
+       3,                      /* frame ack t1 timeout                 */
+       3,                      /* poll ack timeout                     */
+       30,                     /* adm wait timeout                     */
+       3,                      /* rej wait timeout                     */
+       10,                     /* max retries                          */
+       3,                      /* watchdog timeout                     */
+       0xaa                    /* baud rate (0xaa=38.4KB)              */
+                               /*   (output on RS-232 pin 24,          */
+                               /*    send/receive timing is always     */
+                               /*    taken from pins 15/17)            */
+    };
+
+hdhinit(unit)
+int unit;
+{      
+       register struct hdh_softc *sc;
+       register struct hdhregs *addr;
+       register struct uba_device *ui;
+       register struct umc_chan *up;
+       register struct mbuf *m, *n;
+       int i, s, ubano;
+
+#ifdef HDHDEBUG
+       printf("HDH INIT\n");
+#endif HDHDEBUG
+
+       if (unit >= NHDH || (ui = hdhinfo[unit]) == NULL
+           || ui->ui_alive == 0) {
+               printf("hdh%d: not alive\n", unit);
+               return(0);
+       }
+       addr = (struct hdhregs *)ui->ui_addr;
+       sc = &hdh_softc[unit];
+
+       if (sc->hdh_flags & HDH_STARTED) {
+               printf("hdh%d: re-init\n", unit);
+               return(1);
+       }
+
+       /*
+        * Alloc uba resources
+        */
+       for(i=0;i<NHDHCH;i++) {
+               if (if_ubainit(&sc->hdh_ifuba[i], ui->ui_ubanum, 0,
+                   (int)btoc(IMPMTU)) == 0) {
+                       printf("hdh%d: cannot get chan %d uba resources\n",
+                               unit, i);
+                       ui->ui_alive = 0;
+                       return(0);
+               }
+       }
+
+       sc->hdh_flags = HDH_STARTED;
+
+       /*
+        * hang a supervisor read (for line status)
+        */
+       hdh_iorq(unit, HDHSUPR, IMPMTU, HDHRDB);
+
+       /*
+        * hang a data read
+        */
+       hdh_iorq(unit, HDHDATR, IMPMTU, HDHRDB+HDHSTR);
+
+       /*
+        * bring up line to IMP
+        */
+
+       snd_supr(unit, init_blk, sizeof(init_blk));
+
+       return(1);
+}
+
+/*
+ * Start an output operation on an mbuf.
+ */
+hdhstart(dev)
+dev_t dev;
+{
+       int unit = HDHUNIT(dev);
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct mbuf *m;
+        int len;
+
+       /*
+        * If output isn't active, attempt to
+        * start sending a new packet.
+        */
+
+       if (sc->hdh_ic->ic_oactive) {
+               printf("hdh%d: start on active unit\n", unit);
+               return;
+       }
+
+       if ((sc->hdh_flags & HDH_UP) == 0) {
+               sc->hdh_ic->ic_oactive = 0;     /* Link not up, can't xmit */
+               return;
+       }
+
+       IF_DEQUEUE(&sc->hdh_if->if_snd, m);
+       if (m == 0) {
+               sc->hdh_ic->ic_oactive = 0;
+               return;
+       }
+
+       len = if_wubaput(&sc->hdh_ifuba[DATA], m);      /* copy data to mapped mem */
+       sc->hdh_ic->ic_oactive = 1;
+
+       hdh_iorq(unit, HDHDATW, len, HDHWRT+HDHEOS);
+}
+
+/*
+ * Start i/o operation on a UMC logical channel
+ */
+hdh_iorq(unit, lcn, len, func)
+int unit, lcn, len, func;
+{
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct hdh_chan *hc = &sc->hdh_chan[lcn];
+       register int info, s;
+
+       /*
+        * If channel is busy (shouldn't be), drop.
+        */
+       if  (hc->hc_flags & HCBUSY) {
+               printf("hdh%d: channel busy lcn=%d\n", unit, lcn);
+               return;
+       }
+
+       /* get appropriate UNIBUS mapping info */
+
+       if (lcn & 1)            /* read or write? */
+               info = sc->hdh_ifuba[lcn>>1].ifu_w.ifrw_info;
+       else
+               info = sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_info;
+
+       /* set channel info */
+
+       hc->hc_flags |= HCBUSY;
+       hc->hc_chan = lcn;
+       hc->hc_adx = (char)((info & 0x30000) >> 12);
+       hc->hc_addr = (unsigned short)(info & 0xffff);
+       hc->hc_cnt = len;
+       hc->hc_func = (char)func;
+       hc->hc_sbfc = 0;
+
+       s = splimp();
+       /*
+        * If UMC comm regs busy, queue start i/o for later.
+        */
+       if (sc->hdh_sioq.sioq_head) {
+               (sc->hdh_sioq.sioq_tail)->hc_next = hc;
+               sc->hdh_sioq.sioq_tail = hc;
+               hc->hc_next = 0;
+               splx(s);
+               return;
+       }
+
+       /* start i/o on channel now */
+
+       sc->hdh_sioq.sioq_head = hc;
+       sc->hdh_sioq.sioq_tail = hc;
+       hc->hc_next = 0;
+       start_chn(unit);
+       splx(s);
+}
+
+start_chn(unit)
+int unit;
+{
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct hdh_chan *hc = sc->hdh_sioq.sioq_head;
+       register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
+
+       /*
+        * Set up comm regs.
+        */
+       addr->iochn = hc->hc_chan;
+       addr->ioadx = hc->hc_adx;
+       addr->ioadl = hc->hc_addr;
+       addr->iocnt = hc->hc_cnt;
+       addr->iofcn = hc->hc_func;
+       addr->iosbf = hc->hc_sbfc;
+       addr->ioini = 1;
+
+       /* signal UMC if necessary */
+
+       if (!(addr->ionmi)) {
+               addr->ionmi = 1;
+               addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
+       }
+}
+
+/*
+ * IF-11/HDH interrupt handler
+ */
+hdhintr(unit)
+int unit;
+{
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct hdh_chan *hc;
+       register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
+       register struct mbuf *m;
+       int lcn, type, cc, cnt, s;
+
+       /*
+        * Check for hardware errors.
+        */
+       if (addr->csr & HDH_UER) {
+               printf("hdh%d: hard error csr=%b\n", unit, addr->csr, HDH_BITS);
+               addr->csr = 0;          /* disable i/f */
+               return;
+       }
+       /*
+        * Get logical channel info.
+        */
+       if ((lcn = addr->stachn) >= (NHDHCH*2)) {
+               printf("hdh%d: unknown channel lcn=%d\n", unit, lcn);
+               return;
+       }
+
+       hc = &sc->hdh_chan[lcn];
+
+       type = addr->statyp;
+       cc = addr->stacc;
+       cnt = hc->hc_cnt - addr->stacnt;
+
+       /* Figure out what kind of interrupt it was */
+
+       switch(type) {
+
+       case HDHSACK:           /* start i/o accepted */
+               if (hc != sc->hdh_sioq.sioq_head) {
+                       printf("hdh%d: STARTIO error lcn=%d hc=%x sq=%x\n",
+                               unit, lcn, hc, sc->hdh_sioq.sioq_head);
+                       return;
+               }
+
+               /* try to start any queued i/o request */
+
+               if (sc->hdh_sioq.sioq_head = sc->hdh_sioq.sioq_head->hc_next) {
+                       start_chn(unit);
+               }
+               break;
+
+       case HDHDONE:           /* i/o completion */
+               switch (cc) {
+
+               case HDHIOCABT:
+                       printf("hdh%d: I/O abort ", unit);
+                       goto daterr;
+
+               case HDHIOCERR:
+                       printf("hdh%d: program error ", unit);
+                       goto daterr;
+
+               case HDHIOCOVR:
+                       printf("hdh%d: overrun error ", unit);
+                       goto daterr;
+
+               case HDHIOCUBE:
+                       printf("hdh%d: NXM timeout or UB parity error ", unit);
+               
+               daterr:
+                       printf("lcn=%d func=%x\n", lcn, hc->hc_func);
+                       if (hc->hc_func & HDHRDB)
+                               sc->hdh_if->if_ierrors++;
+                       else
+                               sc->hdh_if->if_oerrors++;
+               }
+
+               hc->hc_flags &= ~HCBUSY;
+
+               /* was it supervisor or data traffic? */
+
+               if (lcn > HDHSUPW)
+                       hdh_data(unit, lcn, cc, cnt);
+               else
+                       hdh_supr(unit, lcn, cc, cnt);
+
+       }
+
+       /*
+        * Ack the interrupt
+        */
+       addr->staack = 1;
+       if (!(addr->ionmi)) {
+               addr->ionmi = 1;
+               addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
+       }       
+}
+
+/*
+ * data channel interrupt completion handler
+ */
+hdh_data(unit, lcn, cc, rcnt)
+int unit, lcn, cc, rcnt;
+{
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct hdh_chan *hc = &sc->hdh_chan[lcn];
+       register struct mbuf *m;
+
+
+       /* was it read or write? */
+
+       if (hc->hc_func & HDHRDB) {
+               if (cc == HDHIOCOK) {
+                       /*
+                        * Queue good packet for input 
+                        */
+                       sc->hdh_if->if_ipackets++;
+                       m = if_rubaget(&sc->hdh_ifuba[lcn>>1], rcnt, 0);
+                       impinput(unit, m);
+               }
+
+               /* hang a new data read */
+
+               hdh_iorq(unit, lcn, IMPMTU, HDHRDB+HDHSTR);
+
+       } else {
+               /*
+                * fire up next output
+                */
+               sc->hdh_if->if_opackets++;
+               sc->hdh_ic->ic_oactive = 0;
+               hdhstart(unit);         
+       }
+}
+
+/*
+ * supervisor channel interrupt completion handler
+ */
+hdh_supr(unit, lcn, cc, rcnt)
+int unit, lcn, cc, rcnt;
+{
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct hdh_chan *hc = &sc->hdh_chan[lcn];
+       register struct uba_device *ui;
+       short *p;
+       int i;
+       
+
+       /* was it read or write? */
+
+       if (hc->hc_func & HDHRDB) {     
+               if (cc == HDHIOCOK) {
+                       p = (short *)(sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_addr);
+
+                       /* figure out what kind of supervisor message */
+
+                       switch (*p) {
+
+                       case HDHIACK:
+                       case HDHLNACK:
+                               break;
+       
+                       case HDHLNUP:
+                               printf("hdh%d: LINE UP\n", unit);
+                               sc->hdh_flags |= HDH_UP;
+                               hdhstart(unit);
+                               break;
+       
+                       case HDHLNDN:
+                               if (sc->hdh_flags & HDH_UP)
+                                       printf("hdh%d: LINE DOWN\n", unit);
+                               sc->hdh_flags &= ~HDH_UP;
+                               break;
+       
+                       case HDHLOOP:
+                               break;
+       
+                       case HDHSQERR:
+                               printf("hdh%d: HOST SEQUENCE ERROR\n", unit);
+                               break;
+       
+                       case HDHSQRCV:
+                               printf("hdh%d: IMP SEQUENCE ERROR\n", unit);
+                               break;
+       
+                       case HDHDTERR:
+                               printf("hdh%d: HOST DATA ERROR\n", unit);
+                               break;
+       
+                       case HDHTIMO:
+                               printf("hdh%d: TIMEOUT\n", unit);
+                               break;
+       
+                       default:
+                               printf("hdh%d: supervisor error, code=%x\n",
+                                       unit, *p);
+                       }
+               }
+
+               /* hang a new supr read */
+
+               hdh_iorq(unit, HDHSUPR, IMPMTU, HDHRDB+HDHSTR);
+       } 
+}
+
+snd_supr(unit, msg, len)
+int unit, len;
+char *msg;
+{
+       register struct hdh_softc *sc = &hdh_softc[unit];
+       register struct hdh_chan *hc = &sc->hdh_chan[HDHSUPW];
+       register struct mbuf *m;
+       register char *p;
+       register int cnt;
+
+       if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) {
+               printf("hdh%d: cannot get supervisor cmnd buffer\n", unit);
+                       return(0);
+       }
+
+       cnt = len;
+       m->m_len = len;
+       p = mtod(m, char *);
+
+       while(cnt--) *p++ = *msg++;
+
+       cnt = if_wubaput(&sc->hdh_ifuba[SUPR], m);
+
+       hdh_iorq(unit, HDHSUPW, cnt, HDHWRT+HDHEOS);
+
+       return(1);
+}
+
+#endif NHDH