diff options
| author | ragge <ragge@NetBSD.org> | 2001-05-06 17:36:04 +0000 |
|---|---|---|
| committer | ragge <ragge@NetBSD.org> | 2001-05-06 17:36:04 +0000 |
| commit | 09a79adad293e600ee2e12b77029df260b8603b5 (patch) | |
| tree | f33c45888095ec6a121e28d37c75668ef6cb9c0e /sys/dev | |
| parent | b3a4957ee53d95a34b40e894685e42319cbf702f (diff) | |
Driver for the DMC-11/DMR-11 DDCMP interface, (untested) from 4.4BSD.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/qbus/if_dmc.c | 953 | ||||
| -rw-r--r-- | sys/dev/qbus/if_dmcreg.h | 138 |
2 files changed, 1091 insertions, 0 deletions
diff --git a/sys/dev/qbus/if_dmc.c b/sys/dev/qbus/if_dmc.c new file mode 100644 index 00000000000..518ffee53dc --- /dev/null +++ b/sys/dev/qbus/if_dmc.c @@ -0,0 +1,953 @@ +/* $NetBSD: if_dmc.c,v 1.1 2001/05/06 17:36:04 ragge Exp $ */ +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_dmc.c 7.10 (Berkeley) 12/16/90 + */ + +/* + * DMC11 device driver, internet version + * + * Bill Nesheim + * Cornell University + * + * Lou Salkind + * New York University + */ + +#undef DMCDEBUG /* for base table dump on fatal error */ + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/netisr.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_var.h> +#endif + +#include <machine/bus.h> + +#include <dev/qbus/ubareg.h> +#include <dev/qbus/ubavar.h> +#include <dev/qbus/if_uba.h> + +#include <dev/qbus/if_dmcreg.h> + + +/* + * output timeout value, sec.; should depend on line speed. + */ +static int dmc_timeout = 20; + +#define NRCV 7 +#define NXMT 3 +#define NCMDS (NRCV+NXMT+4) /* size of command queue */ + +#define DMC_WBYTE(csr, val) \ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, csr, val) +#define DMC_WWORD(csr, val) \ + bus_space_write_2(sc->sc_iot, sc->sc_ioh, csr, val) +#define DMC_RBYTE(csr) \ + bus_space_read_1(sc->sc_iot, sc->sc_ioh, csr) +#define DMC_RWORD(csr) \ + bus_space_read_2(sc->sc_iot, sc->sc_ioh, csr) + + +#ifdef DMCDEBUG +#define printd if(dmcdebug)printf +int dmcdebug = 0; +#endif + +/* error reporting intervals */ +#define DMC_RPNBFS 50 +#define DMC_RPDSC 1 +#define DMC_RPTMO 10 +#define DMC_RPDCK 10 + +struct dmc_command { + char qp_cmd; /* command */ + short qp_ubaddr; /* buffer address */ + short qp_cc; /* character count || XMEM */ + struct dmc_command *qp_next; /* next command on queue */ +}; + +struct dmcbufs { + int ubinfo; /* from uballoc */ + short cc; /* buffer size */ + short flags; /* access control */ +}; +#define DBUF_OURS 0 /* buffer is available */ +#define DBUF_DMCS 1 /* buffer claimed by somebody */ +#define DBUF_XMIT 4 /* transmit buffer */ +#define DBUF_RCV 8 /* receive buffer */ + + +/* + * DMC software status per interface. + * + * Each interface is referenced by a network interface structure, + * sc_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 set of 7 UBA interface structures + * for each, which + * contain 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 dmc_softc { + struct device sc_dev; /* Configuration common part */ + struct ifnet sc_if; /* network-visible interface */ + short sc_oused; /* output buffers currently in use */ + short sc_iused; /* input buffers given to DMC */ + short sc_flag; /* flags */ + struct ubinfo sc_ui; /* UBA mapping info for base table */ + int sc_errors[4]; /* non-fatal error counters */ + bus_space_tag_t sc_iot; + bus_addr_t sc_ioh; + bus_dma_tag_t sc_dmat; + struct evcnt sc_rintrcnt; /* Interrupt counting */ + struct evcnt sc_tintrcnt; /* Interrupt counting */ +#define sc_datck sc_errors[0] +#define sc_timeo sc_errors[1] +#define sc_nobuf sc_errors[2] +#define sc_disc sc_errors[3] + struct dmcbufs sc_rbufs[NRCV]; /* receive buffer info */ + struct dmcbufs sc_xbufs[NXMT]; /* transmit buffer info */ + struct ifubinfo sc_ifuba; /* UNIBUS resources */ + struct ifrw sc_ifr[NRCV]; /* UNIBUS receive buffer maps */ + struct ifxmt sc_ifw[NXMT]; /* UNIBUS receive buffer maps */ + /* command queue stuff */ + struct dmc_command sc_cmdbuf[NCMDS]; + struct dmc_command *sc_qhead; /* head of command queue */ + struct dmc_command *sc_qtail; /* tail of command queue */ + struct dmc_command *sc_qactive; /* command in progress */ + struct dmc_command *sc_qfreeh; /* head of list of free cmd buffers */ + struct dmc_command *sc_qfreet; /* tail of list of free cmd buffers */ + /* end command queue stuff */ + struct dmc_base { + short d_base[128]; /* DMC base table */ + } dmc_base; +}; + +static int dmcmatch(struct device *, struct cfdata *, void *); +static void dmcattach(struct device *, struct device *, void *); +static int dmcinit(struct ifnet *); +static void dmcrint(void *); +static void dmcxint(void *); +static void dmcdown(struct dmc_softc *sc); +static void dmcrestart(struct dmc_softc *); +static void dmcload(struct dmc_softc *, int, u_short, u_short); +static void dmcstart(struct ifnet *); +static void dmctimeout(struct ifnet *); +static int dmcioctl(struct ifnet *, u_long, caddr_t); +static int dmcoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +static void dmcreset(struct device *); + +struct cfattach dmc_ca = { + sizeof(struct dmc_softc), dmcmatch, dmcattach +}; + +/* flags */ +#define DMC_RUNNING 0x01 /* device initialized */ +#define DMC_BMAPPED 0x02 /* base table mapped */ +#define DMC_RESTART 0x04 /* software restart in progress */ +#define DMC_ONLINE 0x08 /* device running (had a RDYO) */ + + +/* queue manipulation macros */ +#define QUEUE_AT_HEAD(qp, head, tail) \ + (qp)->qp_next = (head); \ + (head) = (qp); \ + if ((tail) == (struct dmc_command *) 0) \ + (tail) = (head) + +#define QUEUE_AT_TAIL(qp, head, tail) \ + if ((tail)) \ + (tail)->qp_next = (qp); \ + else \ + (head) = (qp); \ + (qp)->qp_next = (struct dmc_command *) 0; \ + (tail) = (qp) + +#define DEQUEUE(head, tail) \ + (head) = (head)->qp_next;\ + if ((head) == (struct dmc_command *) 0)\ + (tail) = (head) + +int +dmcmatch(struct device *parent, struct cfdata *cf, void *aux) +{ + struct uba_attach_args *ua = aux; + struct dmc_softc ssc; + struct dmc_softc *sc = &ssc; + int i; + + sc->sc_iot = ua->ua_iot; + sc->sc_ioh = ua->ua_ioh; + + DMC_WBYTE(DMC_BSEL1, DMC_MCLR); + for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--) + ; + if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) { + printf("dmcprobe: can't start device\n" ); + return (0); + } + DMC_WBYTE(DMC_BSEL0, DMC_RQI|DMC_IEI); + /* let's be paranoid */ + DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) | DMC_RQI|DMC_IEI); + DELAY(1000000); + DMC_WBYTE(DMC_BSEL1, DMC_MCLR); + for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--) + ; + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +void +dmcattach(struct device *parent, struct device *self, void *aux) +{ + struct uba_attach_args *ua = aux; + struct dmc_softc *sc = (struct dmc_softc *)self; + + sc->sc_iot = ua->ua_iot; + sc->sc_ioh = ua->ua_ioh; + sc->sc_dmat = ua->ua_dmat; + + strcpy(sc->sc_if.if_xname, sc->sc_dev.dv_xname); + sc->sc_if.if_mtu = DMCMTU; + sc->sc_if.if_init = dmcinit; + sc->sc_if.if_output = dmcoutput; + sc->sc_if.if_ioctl = dmcioctl; + sc->sc_if.if_watchdog = dmctimeout; + sc->sc_if.if_flags = IFF_POINTOPOINT; + sc->sc_if.if_softc = sc; + + uba_intr_establish(ua->ua_icookie, ua->ua_cvec, dmcrint, sc, + &sc->sc_rintrcnt); + uba_intr_establish(ua->ua_icookie, ua->ua_cvec+4, dmcxint, sc, + &sc->sc_tintrcnt); + uba_reset_establish(dmcreset, &sc->sc_dev); + evcnt_attach_dynamic(&sc->sc_rintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt, + sc->sc_dev.dv_xname, "intr"); + evcnt_attach_dynamic(&sc->sc_tintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt, + sc->sc_dev.dv_xname, "intr"); + + if_attach(&sc->sc_if); +} + +/* + * Reset of interface after UNIBUS reset. + * If interface is on specified UBA, reset its state. + */ +void +dmcreset(struct device *dev) +{ + struct dmc_softc *sc = (struct dmc_softc *)dev; + + sc->sc_flag = 0; + sc->sc_if.if_flags &= ~IFF_RUNNING; + dmcinit(&sc->sc_if); +} + +/* + * Initialization of interface; reinitialize UNIBUS usage. + */ +int +dmcinit(struct ifnet *ifp) +{ + struct dmc_softc *sc = ifp->if_softc; + struct ifrw *ifrw; + struct ifxmt *ifxp; + struct dmcbufs *rp; + struct dmc_command *qp; + struct ifaddr *ifa; + struct cfdata *ui = sc->sc_dev.dv_cfdata; + int base; + int s; + + /* + * Check to see that an address has been set + * (both local and destination for an address family). + */ + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + if (ifa->ifa_addr->sa_family && ifa->ifa_dstaddr->sa_family) + break; + if (ifa == (struct ifaddr *) 0) + return 0; + + if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) { + printf("dmcinit: DMC not running\n"); + ifp->if_flags &= ~IFF_UP; + return 0; + } + /* map base table */ + if ((sc->sc_flag & DMC_BMAPPED) == 0) { + sc->sc_ui.ui_size = sizeof(struct dmc_base); + sc->sc_ui.ui_vaddr = (caddr_t)&sc->dmc_base; + uballoc((void *)sc->sc_dev.dv_parent, &sc->sc_ui, 0); + sc->sc_flag |= DMC_BMAPPED; + } + /* initialize UNIBUS resources */ + sc->sc_iused = sc->sc_oused = 0; + if ((ifp->if_flags & IFF_RUNNING) == 0) { + if (if_ubaminit(&sc->sc_ifuba, (void *)sc->sc_dev.dv_parent, + sizeof(struct dmc_header) + DMCMTU, + sc->sc_ifr, NRCV, sc->sc_ifw, NXMT) == 0) { + printf("%s: can't allocate uba resources\n", + sc->sc_dev.dv_xname); + ifp->if_flags &= ~IFF_UP; + return 0; + } + ifp->if_flags |= IFF_RUNNING; + } + sc->sc_flag &= ~DMC_ONLINE; + sc->sc_flag |= DMC_RUNNING; + /* + * Limit packets enqueued until we see if we're on the air. + */ + ifp->if_snd.ifq_maxlen = 3; + + /* initialize buffer pool */ + /* receives */ + ifrw = &sc->sc_ifr[0]; + for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { + rp->ubinfo = ifrw->ifrw_info; + rp->cc = DMCMTU + sizeof (struct dmc_header); + rp->flags = DBUF_OURS|DBUF_RCV; + ifrw++; + } + /* transmits */ + ifxp = &sc->sc_ifw[0]; + for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) { + rp->ubinfo = ifxp->ifw_info; + rp->cc = 0; + rp->flags = DBUF_OURS|DBUF_XMIT; + ifxp++; + } + + /* set up command queues */ + sc->sc_qfreeh = sc->sc_qfreet + = sc->sc_qhead = sc->sc_qtail = sc->sc_qactive = + (struct dmc_command *)0; + /* set up free command buffer list */ + for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) { + QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet); + } + + /* base in */ + base = sc->sc_ui.ui_baddr; + dmcload(sc, DMC_BASEI, (u_short)base, (base>>2) & DMC_XMEM); + /* specify half duplex operation, flags tell if primary */ + /* or secondary station */ + if (ui->cf_flags == 0) + /* use DDCMP mode in full duplex */ + dmcload(sc, DMC_CNTLI, 0, 0); + else if (ui->cf_flags == 1) + /* use MAINTENENCE mode */ + dmcload(sc, DMC_CNTLI, 0, DMC_MAINT ); + else if (ui->cf_flags == 2) + /* use DDCMP half duplex as primary station */ + dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX); + else if (ui->cf_flags == 3) + /* use DDCMP half duplex as secondary station */ + dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX | DMC_SEC); + + /* enable operation done interrupts */ + while ((DMC_RBYTE(DMC_BSEL2) & DMC_IEO) == 0) + DMC_WBYTE(DMC_BSEL2, DMC_RBYTE(DMC_BSEL2) | DMC_IEO); + s = splnet(); + /* queue first NRCV buffers for DMC to fill */ + for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { + rp->flags |= DBUF_DMCS; + dmcload(sc, DMC_READ, rp->ubinfo, + (((rp->ubinfo>>2)&DMC_XMEM) | rp->cc)); + sc->sc_iused++; + } + splx(s); + return 0; +} + +/* + * Start output on interface. Get another datagram + * to send from the interface queue and map it to + * the interface before starting output. + * + * Must be called at spl 5 + */ +void +dmcstart(struct ifnet *ifp) +{ + struct dmc_softc *sc = ifp->if_softc; + struct mbuf *m; + struct dmcbufs *rp; + int n; + + /* + * Dequeue up to NXMT requests and map them to the UNIBUS. + * If no more requests, or no dmc buffers available, just return. + */ + n = 0; + for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) { + /* find an available buffer */ + if ((rp->flags & DBUF_DMCS) == 0) { + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == 0) + return; + /* mark it dmcs */ + rp->flags |= (DBUF_DMCS); + /* + * Have request mapped to UNIBUS for transmission + * and start the output. + */ + rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m); + rp->cc &= DMC_CCOUNT; + if (++sc->sc_oused == 1) + sc->sc_if.if_timer = dmc_timeout; + dmcload(sc, DMC_WRITE, rp->ubinfo, + rp->cc | ((rp->ubinfo>>2)&DMC_XMEM)); + } + n++; + } +} + +/* + * Utility routine to load the DMC device registers. + */ +void +dmcload(struct dmc_softc *sc, int type, u_short w0, u_short w1) +{ + struct dmc_command *qp; + int sps; + + sps = splnet(); + + /* grab a command buffer from the free list */ + if ((qp = sc->sc_qfreeh) == (struct dmc_command *)0) + panic("dmc command queue overflow"); + DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet); + + /* fill in requested info */ + qp->qp_cmd = (type | DMC_RQI); + qp->qp_ubaddr = w0; + qp->qp_cc = w1; + + if (sc->sc_qactive) { /* command in progress */ + if (type == DMC_READ) { + QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail); + } else { + QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail); + } + } else { /* command port free */ + sc->sc_qactive = qp; + DMC_WBYTE(DMC_BSEL0, qp->qp_cmd); + dmcrint(sc); + } + splx(sps); +} + +/* + * DMC interface receiver interrupt. + * Ready to accept another command, + * pull one off the command queue. + */ +void +dmcrint(void *arg) +{ + struct dmc_softc *sc = arg; + struct dmc_command *qp; + int n; + + if ((qp = sc->sc_qactive) == (struct dmc_command *) 0) { + printf("%s: dmcrint no command\n", sc->sc_dev.dv_xname); + return; + } + while (DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) { + DMC_WWORD(DMC_SEL4, qp->qp_ubaddr); + DMC_WWORD(DMC_SEL6, qp->qp_cc); + DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & ~(DMC_IEI|DMC_RQI)); + /* free command buffer */ + QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet); + while (DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) { + /* + * Can't check for RDYO here 'cause + * this routine isn't reentrant! + */ + DELAY(5); + } + /* move on to next command */ + if ((sc->sc_qactive = sc->sc_qhead) == (struct dmc_command *)0) + break; /* all done */ + /* more commands to do, start the next one */ + qp = sc->sc_qactive; + DEQUEUE(sc->sc_qhead, sc->sc_qtail); + DMC_WBYTE(DMC_BSEL0, qp->qp_cmd); + n = RDYSCAN; + while (n-- > 0) + if ((DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) || + (DMC_RBYTE(DMC_BSEL2) & DMC_RDYO)) + break; + } + if (sc->sc_qactive) { + DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & (DMC_IEI|DMC_RQI)); + /* VMS does it twice !*$%@# */ + DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & (DMC_IEI|DMC_RQI)); + } + +} + +/* + * DMC interface transmitter interrupt. + * A transfer may have completed, check for errors. + * If it was a read, notify appropriate protocol. + * If it was a write, pull the next one off the queue. + */ +void +dmcxint(void *a) +{ + struct dmc_softc *sc = a; + + struct ifnet *ifp; + struct mbuf *m; + struct ifqueue *inq; + int arg, pkaddr, cmd, len, s; + struct ifrw *ifrw; + struct dmcbufs *rp; + struct ifxmt *ifxp; + struct dmc_header *dh; + char buf[64]; + + ifp = &sc->sc_if; + + while (DMC_RBYTE(DMC_BSEL2) & DMC_RDYO) { + + cmd = DMC_RBYTE(DMC_BSEL2) & 0xff; + arg = DMC_RWORD(DMC_SEL6) & 0xffff; + /* reconstruct UNIBUS address of buffer returned to us */ + pkaddr = ((arg&DMC_XMEM)<<2) | (DMC_RWORD(DMC_SEL4) & 0xffff); + /* release port */ + DMC_WBYTE(DMC_BSEL2, DMC_RBYTE(DMC_BSEL2) & ~DMC_RDYO); + switch (cmd & 07) { + + case DMC_OUR: + /* + * A read has completed. + * Pass packet to type specific + * higher-level input routine. + */ + ifp->if_ipackets++; + /* find location in dmcuba struct */ + ifrw= &sc->sc_ifr[0]; + for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { + if(rp->ubinfo == pkaddr) + break; + ifrw++; + } + if (rp >= &sc->sc_rbufs[NRCV]) + panic("dmc rcv"); + if ((rp->flags & DBUF_DMCS) == 0) + printf("%s: done unalloc rbuf\n", + sc->sc_dev.dv_xname); + + len = (arg & DMC_CCOUNT) - sizeof (struct dmc_header); + if (len < 0 || len > DMCMTU) { + ifp->if_ierrors++; +#ifdef DMCDEBUG + printd("%s: bad rcv pkt addr 0x%x len 0x%x\n", + sc->sc_dev.dv_xname, pkaddr, len); +#endif + goto setup; + } + /* + * Deal with trailer protocol: if type is trailer + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + dh = (struct dmc_header *)ifrw->ifrw_addr; + dh->dmc_type = ntohs((u_short)dh->dmc_type); + if (len == 0) + goto setup; + + /* + * Pull packet off interface. Off is nonzero if + * packet has trailing header; dmc_get will then + * force this header information to be at the front, + * but we still have to drop the type and length + * which are at the front of any trailer data. + */ + m = if_ubaget(&sc->sc_ifuba, ifrw, ifp, len); + if (m == 0) + goto setup; + /* Shave off dmc_header */ + m_adj(m, sizeof(struct dmc_header)); + switch (dh->dmc_type) { + +#ifdef INET + case DMC_IPTYPE: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; +#endif + default: + m_freem(m); + goto setup; + } + + s = splnet(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else + IF_ENQUEUE(inq, m); + splx(s); + + setup: + /* is this needed? */ + rp->ubinfo = ifrw->ifrw_info; + + dmcload(sc, DMC_READ, rp->ubinfo, + ((rp->ubinfo >> 2) & DMC_XMEM) | rp->cc); + break; + + case DMC_OUX: + /* + * A write has completed, start another + * transfer if there is more data to send. + */ + ifp->if_opackets++; + /* find associated dmcbuf structure */ + ifxp = &sc->sc_ifw[0]; + for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) { + if(rp->ubinfo == pkaddr) + break; + ifxp++; + } + if (rp >= &sc->sc_xbufs[NXMT]) { + printf("%s: bad packet address 0x%x\n", + sc->sc_dev.dv_xname, pkaddr); + break; + } + if ((rp->flags & DBUF_DMCS) == 0) + printf("%s: unallocated packet 0x%x\n", + sc->sc_dev.dv_xname, pkaddr); + /* mark buffer free */ + if_ubaend(&sc->sc_ifuba, ifxp); + rp->flags &= ~DBUF_DMCS; + if (--sc->sc_oused == 0) + sc->sc_if.if_timer = 0; + else + sc->sc_if.if_timer = dmc_timeout; + if ((sc->sc_flag & DMC_ONLINE) == 0) { + extern int ifqmaxlen; + + /* + * We're on the air. + * Open the queue to the usual value. + */ + sc->sc_flag |= DMC_ONLINE; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + } + break; + + case DMC_CNTLO: + arg &= DMC_CNTMASK; + if (arg & DMC_FATAL) { + if (arg != DMC_START) { + bitmask_snprintf(arg, CNTLO_BITS, + buf, sizeof(buf)); + log(LOG_ERR, + "%s: fatal error, flags=%s\n", + sc->sc_dev.dv_xname, buf); + } + dmcrestart(sc); + break; + } + /* ACCUMULATE STATISTICS */ + switch(arg) { + case DMC_NOBUFS: + ifp->if_ierrors++; + if ((sc->sc_nobuf++ % DMC_RPNBFS) == 0) + goto report; + break; + case DMC_DISCONN: + if ((sc->sc_disc++ % DMC_RPDSC) == 0) + goto report; + break; + case DMC_TIMEOUT: + if ((sc->sc_timeo++ % DMC_RPTMO) == 0) + goto report; + break; + case DMC_DATACK: + ifp->if_oerrors++; + if ((sc->sc_datck++ % DMC_RPDCK) == 0) + goto report; + break; + default: + goto report; + } + break; + report: +#ifdef DMCDEBUG + bitmask_snprintf(arg, CNTLO_BITS, buf, sizeof(buf)); + printd("%s: soft error, flags=%s\n", + sc->sc_dev.dv_xname, buf); +#endif + if ((sc->sc_flag & DMC_RESTART) == 0) { + /* + * kill off the dmc to get things + * going again by generating a + * procedure error + */ + sc->sc_flag |= DMC_RESTART; + arg = sc->sc_ui.ui_baddr; + dmcload(sc, DMC_BASEI, arg, (arg>>2)&DMC_XMEM); + } + break; + + default: + printf("%s: bad control %o\n", + sc->sc_dev.dv_xname, cmd); + break; + } + } + dmcstart(ifp); +} + +/* + * DMC output routine. + * Encapsulate a packet of type family for the dmc. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + */ +int +dmcoutput(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, + struct rtentry *rt) +{ + int type, error, s; + struct mbuf *m = m0; + struct dmc_header *dh; + + if ((ifp->if_flags & IFF_UP) == 0) { + error = ENETDOWN; + goto bad; + } + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + type = DMC_IPTYPE; + break; +#endif + + case AF_UNSPEC: + dh = (struct dmc_header *)dst->sa_data; + type = dh->dmc_type; + break; + + default: + printf("%s: can't handle af%d\n", ifp->if_xname, + dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + + /* + * Add local network header + * (there is space for a uba on a vax to step on) + */ + M_PREPEND(m, sizeof(struct dmc_header), M_DONTWAIT); + if (m == 0) { + error = ENOBUFS; + goto bad; + } + dh = mtod(m, struct dmc_header *); + dh->dmc_type = htons((u_short)type); + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + s = splnet(); + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + m_freem(m); + splx(s); + return (ENOBUFS); + } + IF_ENQUEUE(&ifp->if_snd, m); + dmcstart(ifp); + splx(s); + return (0); + +bad: + m_freem(m0); + return (error); +} + + +/* + * Process an ioctl request. + */ +/* ARGSUSED */ +int +dmcioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + int s = splnet(), error = 0; + register struct dmc_softc *sc = ifp->if_softc; + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if ((ifp->if_flags & IFF_RUNNING) == 0) + dmcinit(ifp); + break; + + case SIOCSIFDSTADDR: + if ((ifp->if_flags & IFF_RUNNING) == 0) + dmcinit(ifp); + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + sc->sc_flag & DMC_RUNNING) + dmcdown(sc); + else if (ifp->if_flags & IFF_UP && + (sc->sc_flag & DMC_RUNNING) == 0) + dmcrestart(sc); + break; + + default: + error = EINVAL; + } + splx(s); + return (error); +} + +/* + * Restart after a fatal error. + * Clear device and reinitialize. + */ +void +dmcrestart(struct dmc_softc *sc) +{ + int s, i; + +#ifdef DMCDEBUG + /* dump base table */ + printf("%s base table:\n", sc->sc_dev.dv_xname); + for (i = 0; i < sizeof (struct dmc_base); i++) + printf("%o\n" ,dmc_base[unit].d_base[i]); +#endif + + dmcdown(sc); + + /* + * Let the DMR finish the MCLR. At 1 Mbit, it should do so + * in about a max of 6.4 milliseconds with diagnostics enabled. + */ + for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--) + ; + /* Did the timer expire or did the DMR finish? */ + if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) { + log(LOG_ERR, "%s: M820 Test Failed\n", sc->sc_dev.dv_xname); + return; + } + + /* restart DMC */ + dmcinit(&sc->sc_if); + sc->sc_flag &= ~DMC_RESTART; + s = splnet(); + dmcstart(&sc->sc_if); + splx(s); + sc->sc_if.if_collisions++; /* why not? */ +} + +/* + * Reset a device and mark down. + * Flush output queue and drop queue limit. + */ +void +dmcdown(struct dmc_softc *sc) +{ + struct ifxmt *ifxp; + + DMC_WBYTE(DMC_BSEL1, DMC_MCLR); + sc->sc_flag &= ~(DMC_RUNNING | DMC_ONLINE); + + for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) { +#ifdef notyet + if (ifxp->ifw_xtofree) { + (void) m_freem(ifxp->ifw_xtofree); + ifxp->ifw_xtofree = 0; + } +#endif + } + IF_PURGE(&sc->sc_if.if_snd); +} + +/* + * Watchdog timeout to see that transmitted packets don't + * lose interrupts. The device has to be online (the first + * transmission may block until the other side comes up). + */ +void +dmctimeout(struct ifnet *ifp) +{ + struct dmc_softc *sc = ifp->if_softc; + char buf1[64], buf2[64]; + + if (sc->sc_flag & DMC_ONLINE) { + bitmask_snprintf(DMC_RBYTE(DMC_BSEL0) & 0xff, DMC0BITS, + buf1, sizeof(buf1)); + bitmask_snprintf(DMC_RBYTE(DMC_BSEL2) & 0xff, DMC2BITS, + buf2, sizeof(buf2)); + log(LOG_ERR, "%s: output timeout, bsel0=%s bsel2=%s\n", + sc->sc_dev.dv_xname, buf1, buf2); + dmcrestart(sc); + } +} diff --git a/sys/dev/qbus/if_dmcreg.h b/sys/dev/qbus/if_dmcreg.h new file mode 100644 index 00000000000..d96085740e2 --- /dev/null +++ b/sys/dev/qbus/if_dmcreg.h @@ -0,0 +1,138 @@ +/* $NetBSD: if_dmcreg.h,v 1.1 2001/05/06 17:36:04 ragge Exp $ */ +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_dmc.h 7.5 (Berkeley) 6/28/90 + */ + +/* + * DMC-11 Interface + */ + +#ifdef notdef +struct dmcdevice { + char bsel0; + char bsel1; + char bsel2; + char bsel3; + short sel4; + short sel6; +}; +#endif + +#define DMC_BSEL0 0 +#define DMC_BSEL1 1 +#define DMC_BSEL2 2 +#define DMC_BSEL3 3 +#define DMC_SEL4 4 +#define DMC_SEL6 6 + +/* + * dmc software packet encapsulation. This allows the dmc + * link to be multiplexed among several protocols. + * The first eight bytes of the dmc header are garbage, + * since on a vax the uba has been known to mung these + * bytes. The next two bytes encapsulate packet type. + */ +struct dmc_header { + char dmc_buf[8]; /* space for uba on vax */ + short dmc_type; /* encapsulate packet type */ +}; + +/* packet types */ +#define DMC_IPTYPE 1 +#define DMC_TRAILER 2 +#define DMC_NTRAILER 16 + +/* + * DMCMTU includes space for data (1024) + + * protocol header (256) + trailer descriptor (4). + * The software link encapsulation header (dmc_header) + * is handled separately. + #define DMCMTU 1284 + */ +#define DMCMTU 1500 + +#define RDYSCAN 16 /* loop delay for RDYI after RQI */ + +/* defines for bsel0 */ +#define DMC_BACCI 0 +#define DMC_CNTLI 1 +#define DMC_PERR 2 +#define DMC_BASEI 3 +#define DMC_WRITE 0 /* transmit block */ +#define DMC_READ 4 /* read block */ +#define DMC_RQI 0040 /* port request bit */ +#define DMC_IEI 0100 /* enable input interrupts */ +#define DMC_RDYI 0200 /* port ready */ +#define DMC0BITS "\10\10RDI\7IEI\6RQI" + +/* defines for bsel1 */ +#define DMC_MCLR 0100 /* DMC11 Master Clear */ +#define DMC_RUN 0200 /* clock running */ +#define DMC1BITS "\10\10RUN\7MCLR" + +/* defines for bsel2 */ +#define DMC_BACCO 0 +#define DMC_CNTLO 1 +#define DMC_OUX 0 /* transmit block */ +#define DMC_OUR 4 /* read block */ +#define DMC_IEO 0100 /* enable output interrupts */ +#define DMC_RDYO 0200 /* port available */ +#define DMC2BITS "\10\10RDO\7IEO" + +/* defines for CNTLI mode */ +#define DMC_HDPLX 02000 /* half duplex DDCMP operation */ +#define DMC_SEC 04000 /* half duplex secondary station */ +#define DMC_MAINT 00400 /* enter maintenance mode */ + +/* defines for BACCI/O and BASEI mode */ +#define DMC_XMEM 0140000 /* xmem bit position */ +#define DMC_CCOUNT 0037777 /* character count mask */ +#define DMC_RESUME 0002000 /* resume (BASEI only) */ + +/* defines for CNTLO */ +#define DMC_CNTMASK 01777 + +#define DMC_DATACK 01 +#define DMC_TIMEOUT 02 +#define DMC_NOBUFS 04 +#define DMC_MAINTREC 010 +#define DMC_LOSTDATA 020 +#define DMC_DISCONN 0100 +#define DMC_START 0200 +#define DMC_NEXMEM 0400 +#define DMC_ERROR 01000 + +#define DMC_FATAL (DMC_ERROR|DMC_NEXMEM|DMC_START|DMC_LOSTDATA|DMC_MAINTREC) +#define CNTLO_BITS \ + "\10\12ERROR\11NEXMEM\10START\7DISC\5LSTDATA\4MAINT\3NOBUF\2TIMEO\1DATACK" |
