diff --git a/sys/dev/bi/files.bi b/sys/dev/bi/files.bi index ba8f454868ed..f7ea75c1c871 100644 --- a/sys/dev/bi/files.bi +++ b/sys/dev/bi/files.bi @@ -1,4 +1,4 @@ -# $NetBSD: files.bi,v 1.1 1999/08/04 19:13:05 ragge Exp $ +# $NetBSD: files.bi,v 1.2 2000/04/09 16:49:57 ragge Exp $ # # Config file and device description for machine-independent # code for devices Digital Equipment Corp. BI bus. @@ -16,7 +16,7 @@ file dev/bi/kdb.c kdb # DEBNA/DEBNT Ethernet Adapter device ni: ifnet, ether, arp attach ni at bi -file dev/bi/ni.c ni +file dev/bi/if_ni.c ni # DWBUA BI-Unibus adapter attach uba at bi with uba_bi diff --git a/sys/dev/bi/if_ni.c b/sys/dev/bi/if_ni.c new file mode 100644 index 000000000000..0793f824ae78 --- /dev/null +++ b/sys/dev/bi/if_ni.c @@ -0,0 +1,862 @@ +/* $NetBSD: if_ni.c,v 1.1 2000/04/09 16:49:57 ragge Exp $ */ +/* + * Copyright (c) 2000 Ludd, University of Lule}, Sweden. 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 at Ludd, University of + * Lule}, Sweden and its contributors. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Driver for DEBNA/DEBNT/DEBNK ethernet cards. + * Things that is still to do: + * Collect statistics. + */ + +#include "opt_inet.h" +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#if NBPFILTER > 0 +#include +#include +#endif + +#include +#ifdef __vax__ +#include +#include +#endif + +#include +#include + +#include "ioconf.h" +#include "locators.h" + +/* + * Tunable buffer parameters. Good idea to have them as power of 8; then + * they will fit into a logical VAX page. + */ +#define NMSGBUF 8 /* Message queue entries */ +#define NTXBUF 16 /* Transmit queue entries */ +#define NTXFRAGS 8 /* Number of transmit buffer fragments */ +#define NRXBUF 24 /* Receive queue entries */ +#define NBDESCS (NTXBUF * NTXFRAGS + NRXBUF) +#define NQUEUES 3 /* RX + TX + MSG */ +#define PKTHDR 18 /* Length of (control) packet header */ +#define RXADD 18 /* Additional length of receive datagram */ +#define TXADD (10+NTXFRAGS*8) /* "" transmit "" */ +#define MSGADD 134 /* "" message "" */ + +#include /* XXX include earlier */ + +/* + * Macros for (most cases of) insqti/remqhi. + * Retry NRETRIES times to do the operation, if it still fails assume + * a lost lock and panic. + */ +#define NRETRIES 100 +#define INSQTI(e, h) ({ \ + int ret, i; \ + for (i = 0; i < NRETRIES; i++) { \ + if ((ret = insqti(e, h)) != ILCK_FAILED) \ + break; \ + } \ + if (i == NRETRIES) \ + panic("ni: insqti failed at %d", __LINE__); \ + ret; \ +}) +#define REMQHI(h) ({ \ + int i;void *ret; \ + for (i = 0; i < NRETRIES; i++) { \ + if ((ret = remqhi(h)) != (void *)ILCK_FAILED) \ + break; \ + } \ + if (i == NRETRIES) \ + panic("ni: remqhi failed at %d", __LINE__); \ + ret; \ +}) + + +#define nipqb (&sc->sc_gvppqb->nc_pqb) +#define gvp sc->sc_gvppqb +#define fqb sc->sc_fqb +#define bbd sc->sc_bbd + +struct ni_softc { + struct device sc_dev; /* Configuration common part */ + struct ethercom sc_ec; /* Ethernet common part */ +#define sc_if sc_ec.ec_if /* network-visible interface */ + bus_space_tag_t sc_iot; + bus_addr_t sc_ioh; + bus_dma_tag_t sc_dmat; + struct ni_gvppqb *sc_gvppqb; /* Port queue block */ + struct ni_gvppqb *sc_pgvppqb; /* Phys address of PQB */ + struct ni_fqb *sc_fqb; /* Free Queue block */ + struct ni_bbd *sc_bbd; /* Buffer descriptors */ + u_int8_t sc_enaddr[ETHER_ADDR_LEN]; +}; + +static int nimatch __P((struct device *, struct cfdata *, void *)); +static void niattach __P((struct device *, struct device *, void *)); +static void niinit __P((struct ni_softc *)); +static void nistart __P((struct ifnet *)); +static void niintr __P((void *)); +static int niioctl __P((struct ifnet *, u_long, caddr_t)); +static int ni_add_rxbuf(struct ni_softc *, struct ni_dg *, int); +static void ni_setup __P((struct ni_softc *)); +static void nitimeout __P((struct ifnet *)); +static void ni_getpgs(struct ni_softc *sc, int size, caddr_t *v, paddr_t *p); + +volatile int endwait; /* Used during autoconfig */ + +struct cfattach ni_ca = { + sizeof(struct ni_softc), nimatch, niattach +}; + +#define NI_WREG(csr, val) \ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, csr, val) +#define NI_RREG(csr) \ + bus_space_read_4(sc->sc_iot, sc->sc_ioh, csr) + +#define WAITREG(csr,val) while (NI_RREG(csr) & val); +/* + * Check for present device. + */ +int +nimatch(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct bi_attach_args *ba = aux; + u_short type; + + type = bus_space_read_2(ba->ba_iot, ba->ba_ioh, BIREG_DTYPE); + if (type != BIDT_DEBNA && type != BIDT_DEBNT && type != BIDT_DEBNK) + return 0; + + if (cf->cf_loc[BICF_NODE] != BICF_NODE_DEFAULT && + cf->cf_loc[BICF_NODE] != ba->ba_nodenr) + return 0; + + return 1; +} + +/* + * Allocate a bunch of descriptor-safe memory. + * We need to get the structures from the beginning of its own pages. + */ +static void +ni_getpgs(struct ni_softc *sc, int size, caddr_t *v, paddr_t *p) +{ + bus_dma_segment_t seg; + int nsegs, error; + + if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &seg, 1, + &nsegs, BUS_DMA_NOWAIT)) != 0) + panic(" unable to allocate memory: error %d", error); + + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, nsegs, size, v, + BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) + panic(" unable to map memory: error %d", error); + + if (p) + *p = seg.ds_addr; +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +void +niattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct bi_attach_args *ba = aux; + struct ni_softc *sc = (struct ni_softc *)self; + struct ifnet *ifp = (struct ifnet *)&sc->sc_if; + struct ni_msg *msg; + struct ni_ptdb *ptdb; + caddr_t va; + int i, j, res; + u_short type; + + type = bus_space_read_2(ba->ba_iot, ba->ba_ioh, BIREG_DTYPE); + printf(": DEBN%c\n", type == BIDT_DEBNA ? 'A' : type == BIDT_DEBNT ? + 'T' : 'K'); + sc->sc_iot = ba->ba_iot; + sc->sc_ioh = ba->ba_ioh; + sc->sc_dmat = ba->ba_dmat; + + bi_intr_establish(ba->ba_icookie, ba->ba_ivec, niintr, sc); + + ni_getpgs(sc, sizeof(struct ni_gvppqb), (caddr_t *)&sc->sc_gvppqb, + (paddr_t *)&sc->sc_pgvppqb); + ni_getpgs(sc, sizeof(struct ni_fqb), (caddr_t *)&sc->sc_fqb, 0); + ni_getpgs(sc, NBDESCS * sizeof(struct ni_bbd), + (caddr_t *)&sc->sc_bbd, 0); + /* + * Zero the newly allocated memory. + */ + bzero(sc->sc_gvppqb, sizeof(struct ni_gvppqb)); + + nipqb->np_veclvl = (ba->ba_ivec << 2) + 2; + nipqb->np_node = ba->ba_intcpu; + nipqb->np_vpqb = (u_int32_t)gvp; +#ifdef __vax__ + nipqb->np_spt = nipqb->np_gpt = mfpr(PR_SBR); + nipqb->np_sptlen = nipqb->np_gptlen = mfpr(PR_SLR); +#else +#error Must fix support for non-vax. +#endif + nipqb->np_bvplvl = 1; + nipqb->np_vfqb = (u_int32_t)fqb; + nipqb->np_vbdt = (u_int32_t)bbd; + nipqb->np_nbdr = NBDESCS; + + /* Free queue block */ + nipqb->np_freeq = NQUEUES; + fqb->nf_mlen = PKTHDR+MSGADD; + fqb->nf_dlen = PKTHDR+TXADD; + fqb->nf_rlen = PKTHDR+RXADD; + + strcpy(ifp->if_xname, sc->sc_dev.dv_xname); + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = nistart; + ifp->if_ioctl = niioctl; + ifp->if_watchdog = nitimeout; + + /* + * Start init sequence. + */ + /* Check state */ + for (i = 0; i < 100; i++) { + if ((NI_RREG(NI_PSR) & PSR_STATE) == PSR_UNDEF) + break; + DELAY(100000); + } + if (i == 100) { + printf(": not undefined state\n"); + return; + } + + /* Clear owner bits */ + NI_WREG(NI_PSR, NI_RREG(NI_PSR) & ~PSR_OWN); + NI_WREG(NI_PCR, NI_RREG(NI_PCR) & ~PCR_OWN); + + /* kick off init */ + NI_WREG(NI_PCR, (u_int32_t)sc->sc_pgvppqb | PCR_INIT | PCR_OWN); + while (NI_RREG(NI_PCR) & PCR_OWN) + DELAY(100000); + + /* Check state */ + for (i = 0; i < 100; i++) { + if ((NI_RREG(NI_PSR) & PSR_INITED)) + break; + DELAY(100000); + } + if (i == 100) { + printf(": failed initialize\n"); + return; + } + NI_WREG(NI_PSR, NI_RREG(NI_PSR) & ~PSR_OWN); + + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_OWN|PCR_ENABLE); + WAITREG(NI_PCR, PCR_OWN); + WAITREG(NI_PSR, PSR_OWN); + + /* Check state */ + for (i = 0; i < 100; i++) { + if ((NI_RREG(NI_PSR) & PSR_STATE) == PSR_ENABLED) + break; + DELAY(10000); + } + if (i == 100) { + printf(": failed enable\n"); + return; + } + NI_WREG(NI_PSR, NI_RREG(NI_PSR) & ~PSR_OWN); + + /* + * The message queue packets must be located on the beginning + * of a page. A VAX page is 512 bytes, but it clusters 8 pages. + * This knowledge is used here when allocating pages. + * !!! How should this be done on MIPS and Alpha??? !!! + */ +#if NBPG < 4096 +#error pagesize too small +#endif + /* Set up message free queue */ + ni_getpgs(sc, NMSGBUF * 512, &va, 0); + bzero(va, NMSGBUF * 512); + for (i = 0; i < NMSGBUF; i++) { + struct ni_msg *msg; + + msg = (void *)(va + i * 512); + + res = INSQTI(msg, &fqb->nf_mforw); + } + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_MFREEQ|PCR_OWN); + WAITREG(NI_PCR, PCR_OWN); + + /* Set up xmit queue */ + ni_getpgs(sc, NTXBUF * 512, &va, 0); + for (i = 0; i < NTXBUF; i++) { + struct ni_dg *data; + + data = (void *)(va + i * 512); + data->nd_status = 0; + data->nd_len = TXADD; + data->nd_ptdbidx = 1; + data->nd_opcode = BVP_DGRAM; + for (j = 0; j < NTXFRAGS; j++) { + data->bufs[j]._offset = 0; + data->bufs[j]._key = 1; + bbd[i * NTXFRAGS + j].nb_key = 1; + bbd[i * NTXFRAGS + j].nb_status = 0; + data->bufs[j]._index = i * NTXFRAGS + j; + } + res = INSQTI(data, &fqb->nf_dforw); + } + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_DFREEQ|PCR_OWN); + WAITREG(NI_PCR, PCR_OWN); + + /* recv buffers */ + ni_getpgs(sc, NRXBUF * 512, &va, 0); + for (i = 0; i < NRXBUF; i++) { + struct ni_dg *data; + int idx; + + data = (void *)(va + i * 512); + data->nd_len = RXADD; + data->nd_opcode = BVP_DGRAMRX; + data->nd_ptdbidx = 2; + data->bufs[0]._key = 1; + + idx = NTXBUF * NTXFRAGS + i; + if (ni_add_rxbuf(sc, data, idx)) + panic("niattach: ni_add_rxbuf: out of mbufs"); + + res = INSQTI(data, &fqb->nf_rforw); + } + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_RFREEQ|PCR_OWN); + WAITREG(NI_PCR, PCR_OWN); + + /* Set initial parameters */ + msg = REMQHI(&fqb->nf_mforw); + + msg->nm_opcode = BVP_MSG; + msg->nm_status = 0; + msg->nm_len = sizeof(struct ni_param) + 6; + msg->nm_opcode2 = NI_WPARAM; + ((struct ni_param *)&msg->nm_text[0])->np_flags = NP_PAD; + + endwait = 0; + res = INSQTI(msg, &gvp->nc_forw0); + + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_CMDQNE|PCR_CMDQ0|PCR_OWN); + WAITREG(NI_PCR, PCR_OWN); + while (endwait == 0) + ; + + /* Clear counters */ + msg = REMQHI(&fqb->nf_mforw); + msg->nm_opcode = BVP_MSG; + msg->nm_status = 0; + msg->nm_len = sizeof(struct ni_param) + 6; + msg->nm_opcode2 = NI_RCCNTR; + + res = INSQTI(msg, &gvp->nc_forw0); + + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_CMDQNE|PCR_CMDQ0|PCR_OWN); + WAITREG(NI_PCR, PCR_OWN); + + /* Enable transmit logic */ + msg = REMQHI(&fqb->nf_mforw); + + msg->nm_opcode = BVP_MSG; + msg->nm_status = 0; + msg->nm_len = 18; + msg->nm_opcode2 = NI_STPTDB; + ptdb = (struct ni_ptdb *)&msg->nm_text[0]; + bzero(ptdb, sizeof(struct ni_ptdb)); + ptdb->np_index = 1; + ptdb->np_fque = 1; + + res = INSQTI(msg, &gvp->nc_forw0); + + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_CMDQNE|PCR_CMDQ0|PCR_OWN); + WAITREG(NI_PCR, PCR_OWN); + + /* Wait for everything to finish */ + WAITREG(NI_PSR, PSR_OWN); + + printf("%s: hardware address %s\n", sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_enaddr)); + + /* + * Attach the interface. + */ + if_attach(ifp); + ether_ifattach(ifp, sc->sc_enaddr); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +} + +/* + * Initialization of interface. + */ +void +niinit(sc) + struct ni_softc *sc; +{ + struct ifnet *ifp = (struct ifnet *)&sc->sc_if; + + /* + * Set flags (so ni_setup() do the right thing). + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Send setup messages so that the rx/tx locic starts. + */ + ni_setup(sc); + +} + +/* + * Start output on interface. + */ +void +nistart(ifp) + struct ifnet *ifp; +{ + struct ni_softc *sc = ifp->if_softc; + struct ni_dg *data; + struct ni_bbd *bdp; + struct mbuf *m, *m0; + int i, cnt, res, mlen; + + if (ifp->if_flags & IFF_OACTIVE) + return; +#ifdef DEBUG + if (ifp->if_flags & IFF_DEBUG) + printf("%s: nistart\n", sc->sc_dev.dv_xname); +#endif + + while (fqb->nf_dforw) { + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == 0) + break; + + data = REMQHI(&fqb->nf_dforw); + if ((int)data == Q_EMPTY) { + IF_PREPEND(&sc->sc_if.if_snd, m); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * Count number of mbufs in chain. + * Always do DMA directly from mbufs, therefore the transmit + * ring is really big. + */ + for (m0 = m, cnt = 0; m0; m0 = m0->m_next) + if (m0->m_len) + cnt++; + if (cnt > NTXFRAGS) + panic("nistart"); /* XXX */ + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + bdp = &bbd[(data->bufs[0]._index & 0x7fff)]; + for (m0 = m, i = 0, mlen = 0; m0; m0 = m0->m_next) { + if (m0->m_len == 0) + continue; + bdp->nb_status = (mtod(m0, u_int32_t) & NIBD_OFFSET) | + NIBD_VALID; + bdp->nb_pte = (u_int32_t)kvtopte(mtod(m0, void *)); + bdp->nb_len = m0->m_len; + data->bufs[i]._offset = 0; + data->bufs[i]._len = bdp->nb_len; + data->bufs[i]._index |= NIDG_CHAIN; + mlen += bdp->nb_len; + bdp++; + i++; + } + data->nd_opcode = BVP_DGRAM; + data->nd_pad3 = 1; + data->nd_ptdbidx = 1; + data->nd_len = 10 + i * 8; + data->bufs[i - 1]._index &= ~NIDG_CHAIN; + if (mlen < 64) + data->bufs[i - 1]._len = bdp[-1].nb_len += (64 - mlen); + data->nd_cmdref = (u_int32_t)m; +#ifdef DEBUG + if (ifp->if_flags & IFF_DEBUG) + printf("%s: sending %d bytes (%d segments)\n", + sc->sc_dev.dv_xname, mlen, i); +#endif + + res = INSQTI(data, &gvp->nc_forw0); + if (res == Q_EMPTY) { + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_CMDQNE|PCR_CMDQ0|PCR_OWN); + } + } +} + +void +niintr(void *arg) +{ + struct ether_header *eh; + struct ni_softc *sc = arg; + struct ni_dg *data; + struct ni_msg *msg; + struct ifnet *ifp = &sc->sc_if; + struct ni_bbd *bd; + struct mbuf *m; + int idx, res; + + /* Got any response packets? */ + while ((NI_RREG(NI_PSR) & PSR_RSQ) && (data = REMQHI(&gvp->nc_forwr))) { + + switch (data->nd_opcode) { + case BVP_DGRAMRX: /* Receive datagram */ + idx = data->bufs[0]._index; + bd = &bbd[idx]; + m = (void *)data->nd_cmdref; + m->m_pkthdr.len = m->m_len = data->bufs[0]._len; + m->m_pkthdr.rcvif = ifp; + if (ni_add_rxbuf(sc, data, idx)) { + bd->nb_len = (m->m_ext.ext_size - 2); + bd->nb_pte = + (long)kvtopte(m->m_ext.ext_buf); + bd->nb_status = 2 | NIBD_VALID; + bd->nb_key = 1; + } + data->nd_len = RXADD; + data->nd_status = 0; + res = INSQTI(data, &fqb->nf_rforw); + if (res == Q_EMPTY) { + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_RFREEQ|PCR_OWN); + } + if (m == (void *)data->nd_cmdref) + break; /* Out of mbufs */ + +#if NBPFILTER > 0 + eh = mtod(m, struct ether_header *); + if (ifp->if_bpf) { + bpf_mtap(ifp->if_bpf, m); + if ((ifp->if_flags & IFF_PROMISC) != 0 + && bcmp(LLADDR(ifp->if_sadl), + eh->ether_dhost, + ETHER_ADDR_LEN) != 0 && + ((eh->ether_dhost[0] & 1) == 0)) { + m_freem(m); + continue; + } + } +#endif + (*ifp->if_input)(ifp, m); + break; + + case BVP_DGRAM: + m = (struct mbuf *)data->nd_cmdref; + ifp->if_flags &= ~IFF_OACTIVE; + m_freem(m); + res = INSQTI(data, &fqb->nf_dforw); + if (res == Q_EMPTY) { + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_DFREEQ|PCR_OWN); + } + break; + + case BVP_MSGRX: + msg = (struct ni_msg *)data; + switch (msg->nm_opcode2) { + case NI_WPARAM: + bcopy(((struct ni_param *)&msg->nm_text[0])->np_dpa, sc->sc_enaddr, ETHER_ADDR_LEN); + endwait = 1; + break; + + case NI_RCCNTR: + case NI_CLPTDB: + case NI_STPTDB: + break; + + default: + printf("Unkn resp %d\n", + msg->nm_opcode2); + break; + } + res = INSQTI(data, &fqb->nf_mforw); + if (res == Q_EMPTY) { + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_MFREEQ|PCR_OWN); + } + break; + + default: + printf("Unknown opcode %d\n", data->nd_opcode); + res = INSQTI(data, &fqb->nf_mforw); + if (res == Q_EMPTY) { + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_FREEQNE|PCR_MFREEQ|PCR_OWN); + } + } + } + + /* Try to kick on the start routine again */ + nistart(ifp); + + NI_WREG(NI_PSR, NI_RREG(NI_PSR) & ~(PSR_OWN|PSR_RSQ)); +} + +/* + * Process an ioctl request. + */ +int +niioctl(ifp, cmd, data) + register struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ni_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s = splnet(), error = 0; + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + niinit(sc); + arp_ifinit(ifp, ifa); + break; +#endif + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, + * stop it. + */ + ifp->if_flags &= ~IFF_RUNNING; + ni_setup(sc); + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface it marked up and it is stopped, then + * start it. + */ + niinit(sc); + } else if ((ifp->if_flags & IFF_UP) != 0) { + /* + * Send a new setup packet to match any new changes. + * (Like IFF_PROMISC etc) + */ + ni_setup(sc); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Update our multicast list. + */ + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ec): + ether_delmulti(ifr, &sc->sc_ec); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + ni_setup(sc); + error = 0; + } + break; + + default: + error = EINVAL; + + } + splx(s); + return (error); +} + +/* + * Add a receive buffer to the indicated descriptor. + */ +int +ni_add_rxbuf(struct ni_softc *sc, struct ni_dg *data, int idx) +{ + struct ni_bbd *bd = &bbd[idx]; + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return (ENOBUFS); + } + + m->m_data += 2; + bd->nb_len = (m->m_ext.ext_size - 2); + bd->nb_pte = (long)kvtopte(m->m_ext.ext_buf); + bd->nb_status = 2 | NIBD_VALID; + bd->nb_key = 1; + + data->bufs[0]._offset = 0; + data->bufs[0]._len = bd->nb_len; + data->bufs[0]._index = idx; + data->nd_cmdref = (long)m; + + return (0); +} + +/* + * Create setup packet and put in queue for sending. + */ +void +ni_setup(struct ni_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + struct ni_msg *msg; + struct ni_ptdb *ptdb; + struct ether_multi *enm; + struct ether_multistep step; + int i, res; + + msg = REMQHI(&fqb->nf_mforw); + if ((int)msg == Q_EMPTY) + return; /* What to do? */ + + ptdb = (struct ni_ptdb *)&msg->nm_text[0]; + bzero(ptdb, sizeof(struct ni_ptdb)); + + msg->nm_opcode = BVP_MSG; + msg->nm_len = 18; + ptdb->np_index = 2; /* definition type index */ + ptdb->np_fque = 2; /* Free queue */ + if (ifp->if_flags & IFF_RUNNING) { + msg->nm_opcode2 = NI_STPTDB; + ptdb->np_type = ETHERTYPE_IP; + ptdb->np_flags = PTDB_UNKN|PTDB_BDC; + if (ifp->if_flags & IFF_PROMISC) + ptdb->np_flags |= PTDB_PROMISC; + memset(ptdb->np_mcast[0], 0xff, ETHER_ADDR_LEN); /* Broadcast */ + ptdb->np_adrlen = 1; + msg->nm_len += 8; + ifp->if_flags &= ~IFF_ALLMULTI; + if ((ifp->if_flags & IFF_PROMISC) == 0) { + ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); + i = 1; + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6)) { + ifp->if_flags |= IFF_ALLMULTI; + ptdb->np_flags |= PTDB_AMC; + break; + } + msg->nm_len += 8; + ptdb->np_adrlen++; + bcopy(enm->enm_addrlo, ptdb->np_mcast[i++], + ETHER_ADDR_LEN); + ETHER_NEXT_MULTI(step, enm); + } + } + } else + msg->nm_opcode2 = NI_CLPTDB; + + res = INSQTI(msg, &gvp->nc_forw0); + if (res == Q_EMPTY) { + WAITREG(NI_PCR, PCR_OWN); + NI_WREG(NI_PCR, PCR_CMDQNE|PCR_CMDQ0|PCR_OWN); + } +} + +/* + * Check for dead transmit logic. Not uncommon. + */ +void +nitimeout(ifp) + struct ifnet *ifp; +{ +#if 0 + struct ni_softc *sc = ifp->if_softc; + + if (sc->sc_inq == 0) + return; + + printf("%s: xmit logic died, resetting...\n", sc->sc_dev.dv_xname); + /* + * Do a reset of interface, to get it going again. + * Will it work by just restart the transmit logic? + */ + niinit(sc); +#endif +} diff --git a/sys/dev/bi/if_nireg.h b/sys/dev/bi/if_nireg.h new file mode 100644 index 000000000000..a31917ac8538 --- /dev/null +++ b/sys/dev/bi/if_nireg.h @@ -0,0 +1,303 @@ +/* $NetBSD: if_nireg.h,v 1.1 2000/04/09 16:49:58 ragge Exp $ */ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)nireg.h 7.3 (Berkeley) 6/28/90 + */ + +/* + * Registers for the DEBNA and DEBNK Ethernet interfaces + * (DEC calls these Network Interfaces, hence nireg.h) + */ + +/* + * this seems to be intended to be more general, but I have no details, + * so it goes here for now + * + * BI Vax Port (BVP) stuff first: + */ +#ifdef notdef +struct bvpregs { + u_long p_pcr; /* port control register */ + u_long p_psr; /* port status register */ + u_long p_per; /* port error register */ + u_long p_pdr; /* port data register */ +}; + +/* + * BI node space registers + */ +struct ni_regs { + struct biiregs ni_bi; /* BIIC registers, except GPRs */ + struct bvpregs ni_tkp; /* tk50 port control via BIIC GPRs */ + u_long ni_xxx[64]; /* unused */ + u_long ni_rxcd; /* receive console data */ + struct bvpregs ni_nip; /* NI port control via BCI3 GPRs */ + u_long ni_pudr; /* power-up diagnostic register */ +}; +#endif + +#define NI_PCR 0x204 +#define NI_PSR 0x208 +#define NI_PER 0x20c +#define NI_PDR 0x210 +#define NI_PUDR 0x204 + +/* bits in PCR */ +#define PCR_OWN 0x80 +#define PCR_MFREEQ 0x000 +#define PCR_DFREEQ 0x100 +#define PCR_RFREEQ 0x200 +#define PCR_IFREEQ 0x300 +#define PCR_CMDQ0 PCR_MFREEQ +#define PCR_CMDQ1 PCR_DFREEQ +#define PCR_CMDQ2 PCR_RFREEQ +#define PCR_CMDQ3 PCR_IFREEQ +#define PCR_FREEQNE 7 +#define PCR_CMDQNE 6 +#define PCR_ENABLE 2 +#define PCR_INIT 1 + +/* bits in PSR */ +#define PSR_OWN 0x80000000 +#define PSR_STATE 0x00070000 +#define PSR_STOPPED 0x00060000 +#define PSR_ENABLED 0x00040000 +#define PSR_INITED 0x00020000 +#define PSR_UNDEF 0x00010000 +#define PSR_RSQ 0x00000080 + +/* + * The DEBNx uses a very wierd (set of) structure(s) to communicate + * with something as simple as an ethernet controller. This is not + * very different to the way communication is done over CI with disks. + */ + +/* Message packet */ +struct ni_msg { + u_int32_t nm_forw; + u_int32_t nm_back; + u_int32_t nm_pad1; + u_int8_t nm_pad2; + u_int8_t nm_status; + u_int8_t nm_opcode; + u_int8_t nm_pad3; + u_int16_t nm_len; + u_int8_t nm_opcode2; + u_int8_t nm_status2; + u_int32_t nm_pad4; + u_int8_t nm_text[128]; +}; + +/* Datagram packet */ +struct ni_dg { + u_int32_t nd_forw; + u_int32_t nd_back; + u_int32_t nd_pad1; + u_int8_t nd_pad2; + u_int8_t nd_status; + u_int8_t nd_opcode; + u_int8_t nd_pad3; + u_int16_t nd_len; + u_int16_t nd_status2; + u_int32_t nd_cmdref; + u_int32_t nd_ptdbidx; + struct { + u_int16_t _offset; + u_int16_t _len; + u_int16_t _index; + u_int16_t _key; + } bufs[NTXFRAGS]; +}; + +#define NIDG_CHAIN 0x8000 + +/* NI parameter block */ +struct ni_param { + u_int8_t np_dpa[8]; + u_int8_t np_apa[8]; + u_int8_t np_lsa[8]; + u_int8_t np_bvc[8]; + u_int16_t np_curaddr; + u_int16_t np_maxaddr; + u_int16_t np_curptt; + u_int16_t np_maxptt; + u_int16_t np_curfq; + u_int16_t np_maxfq; + u_int32_t np_sid; + u_int32_t np_mop; + u_int32_t np_flags; + u_int32_t np_rcto; + u_int32_t np_xmto; +}; + +#define NP_ECT 0x01 +#define NP_PAD 0x02 +#define NP_BOO 0x04 +#define NP_CAR 0x08 +#define NP_ILP 0x10 +#define NP_ELP 0x20 +#define NP_DCRC 0x40 +#define NP_THRU 0x80 + +/* Protocol type definition block */ +struct ni_ptdb { + u_int16_t np_type; /* Protocol type */ + u_int8_t np_fque; /* Free queue */ + u_int8_t np_flags; /* See below */ + u_int32_t np_index; /* protocol type index */ + u_int16_t np_adrlen; /* # of multicast addresses */ + u_int16_t np_802; /* for IEEE 802 packets */ + u_int8_t np_mcast[16][8];/* Multicast (direct match) array */ +}; + +#define PTDB_PROMISC 0x08 +#define PTDB_802 0x10 +#define PTDB_BDC 0x20 +#define PTDB_UNKN 0x40 +#define PTDB_AMC 0x80 + +/* Buffer descriptor */ +struct ni_bbd { + u_int16_t nb_status; /* Offset, valid etc */ + u_int16_t nb_key; + u_int32_t nb_len; /* Buffer length */ + u_int32_t nb_pte; /* start (vax) PTE for this buffer */ + u_int32_t nb_pad; +}; +#define NIBD_OFFSET 0x1ff +#define NIBD_VALID 0x8000 + + +/* Free Queue Block */ +struct ni_fqb { + u_int32_t nf_mlen; + u_int32_t nf_mpad; + u_int32_t nf_mforw; + u_int32_t nf_mback; + u_int32_t nf_dlen; + u_int32_t nf_dpad; + u_int32_t nf_dforw; + u_int32_t nf_dback; + u_int32_t nf_rlen; + u_int32_t nf_rpad; + u_int32_t nf_rforw; + u_int32_t nf_rback; + u_int32_t nf_ilen; + u_int32_t nf_ipad; + u_int32_t nf_iforw; + u_int32_t nf_iback; +}; + +/* DEBNx specific part of Generic VAX Port */ +struct ni_pqb { + u_int16_t np_veclvl; /* Interrupt vector + level */ + u_int16_t np_node; /* Where to interrupt */ + u_int32_t np_freeq; + u_int32_t np_vfqb; /* Free queue block pointer */ + u_int32_t np_pad1[39]; + u_int32_t np_bvplvl; + u_int32_t np_vpqb; /* Virtual address of Generic PQB */ + u_int32_t np_vbdt; /* Virtual address of descriptors */ + u_int32_t np_nbdr; /* Number of descriptors */ + u_int32_t np_spt; /* System Page Table */ + u_int32_t np_sptlen; /* System Page Table length */ + u_int32_t np_gpt; /* Global Page Table */ + u_int32_t np_gptlen; /* Global Page Table length */ + u_int32_t np_mask; + u_int32_t np_pad2[67]; +}; + +/* "Generic VAX Port Control Block" whatever it means */ +struct ni_gvppqb { + u_int32_t nc_forw0; + u_int32_t nc_back0; + u_int32_t nc_forw1; + u_int32_t nc_back1; + u_int32_t nc_forw2; + u_int32_t nc_back2; + u_int32_t nc_forw3; + u_int32_t nc_back3; + u_int32_t nc_forwr; + u_int32_t nc_backr; + struct ni_pqb nc_pqb; /* DEBNx specific part of struct */ +}; + + +/* BVP opcodes, should be somewhere else */ +#define BVP_DGRAM 1 +#define BVP_MSG 2 +#define BVP_DGRAMI 3 +#define BVP_DGRAMRX 33 +#define BVP_MSGRX 34 +#define BVP_DGRAMIRX 35 + +/* NI-specific sub-opcodes */ +#define NI_WSYSID 1 +#define NI_RSYSID 2 +#define NI_WPARAM 3 +#define NI_RPARAM 4 +#define NI_RCCNTR 5 +#define NI_RDCNTR 6 +#define NI_STPTDB 7 +#define NI_CLPTDB 8 + +/* bits in ni_pudr */ +#define PUDR_TAPE 0x40000000 /* tk50 & assoc logic ok */ +#define PUDR_PATCH 0x20000000 /* patch logic ok */ +#define PUDR_VRAM 0x10000000 /* DEBNx onboard RAM ok */ +#define PUDR_VROM1 0x08000000 /* uVax ROM 1 ok */ /* ? */ +#define PUDR_VROM2 0x04000000 /* uVax ROM 2 ok */ +#define PUDR_VROM3 0x02000000 /* uVax ROM 3 ok */ +#define PUDR_VROM4 0x01000000 /* uVax ROM 4 ok */ +#define PUDR_UVAX 0x00800000 /* uVax passes self test */ +#define PUDR_BI 0x00400000 /* BIIC and BCI3 chips ok */ +#define PUDR_TMR 0x00200000 /* interval timer ok */ +#define PUDR_IRQ 0x00100000 /* no IRQ lines stuck */ +#define PUDR_NI 0x00080000 /* Ethernet ctlr ok */ +#define PUDR_TK50 0x00040000 /* tk50 present */ +#define PUDR_PRES 0x00001000 /* tk50 present (again?!) */ +#define PUDR_UVINT 0x00000800 /* uVax-to-80186 intr logic ok */ +#define PUDR_BUSHD 0x00000400 /* no bus hold errors */ +#define PUDR_II32 0x00000200 /* II32 transceivers ok */ +#define PUDR_MPSC 0x00000100 /* MPSC logic ok */ +#define PUDR_GAP 0x00000080 /* gap-detect logic ok */ +#define PUDR_MISC 0x00000040 /* misc. registers ok */ +#define PUDR_UNEXP 0x00000020 /* unexpected interrupt trapped */ +#define PUDR_80186 0x00000010 /* 80186 ok */ +#define PUDR_PATCH2 0x00000008 /* patch logic ok (again) */ +#define PUDR_8RAM 0x00000004 /* 80186 RAM ok */ +#define PUDR_8ROM2 0x00000002 /* 80186 ROM1 ok */ +#define PUDR_8ROM1 0x00000001 /* 80186 ROM2 ok */