diff options
| author | thorpej <thorpej@NetBSD.org> | 1999-02-12 00:36:48 +0000 |
|---|---|---|
| committer | thorpej <thorpej@NetBSD.org> | 1999-02-12 00:36:48 +0000 |
| commit | 7b516b31bd99a710f6550ff00dfff3a4ef563bf8 (patch) | |
| tree | e5b4bafa3907613d06a56ac173abf860b5edea35 /sys/dev | |
| parent | 999252997eebba36c94c292fc83e97ce355dba32 (diff) | |
Major overhaul of the Rhine/Rhine-II driver, structuring it a little more
like the SMC83C100 EPIC/100 driver:
* Rather than using pointers to the head and tail of the transmit and
receive rings, use wrapping indexes into arrays. This is a little more
obvious when reading the code.
* More cleanly separate the hardware descriptor from the software descriptor.
* bus_dma it everywhere.
* Implement interrupt pacing and avoid a potential race in the transmit
loop.
Now this looks more or less like the Rhine driver I was working on when
this driver was committed :-) Update copyright notice to reflect that.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/pci/if_vr.c | 1007 |
1 files changed, 576 insertions, 431 deletions
diff --git a/sys/dev/pci/if_vr.c b/sys/dev/pci/if_vr.c index 40d1b5ce324..abd28f1ed27 100644 --- a/sys/dev/pci/if_vr.c +++ b/sys/dev/pci/if_vr.c @@ -1,4 +1,41 @@ -/* $NetBSD: if_vr.c,v 1.17 1999/02/05 22:09:46 thorpej Exp $ */ +/* $NetBSD: if_vr.c,v 1.18 1999/02/12 00:36:48 thorpej Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ /* * Copyright (c) 1997, 1998 @@ -77,6 +114,8 @@ #include <sys/socket.h> #include <sys/device.h> +#include <vm/vm.h> /* for PAGE_SIZE */ + #include <net/if.h> #include <net/if_arp.h> #include <net/if_dl.h> @@ -93,8 +132,6 @@ #include <net/bpf.h> #endif -#include <vm/vm.h> /* for vtophys */ - #include <machine/bus.h> #include <machine/intr.h> @@ -107,12 +144,6 @@ #include <dev/pci/if_vrreg.h> -#if defined(__NetBSD__) && defined(__alpha__) -/* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vaddr_t)(va)) -#endif - #define VR_USEIOSPACE #define ETHER_CRC_LEN 4 /* XXX Should be in a common header. */ @@ -126,38 +157,49 @@ static struct vr_type { const char *vr_name; } vr_devs[] = { { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT3043, - "VIA VT3043 Rhine I 10/100BaseTX" }, + "VIA VT3043 (Rhine) 10/100 Ethernet" }, { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT86C100A, - "VIA VT86C100A Rhine II 10/100BaseTX" }, + "VIA VT86C100A (Rhine-II) 10/100 Ethernet" }, { 0, 0, NULL } }; -struct vr_list_data { - struct vr_desc vr_rx_list[VR_RX_LIST_CNT]; - struct vr_desc vr_tx_list[VR_TX_LIST_CNT]; -}; +/* + * Transmit descriptor list size. + */ +#define VR_NTXDESC 64 +#define VR_NTXDESC_MASK (VR_NTXDESC - 1) +#define VR_NEXTTX(x) (((x) + 1) & VR_NTXDESC_MASK) -struct vr_chain { - struct vr_desc *vr_ptr; - struct mbuf *vr_mbuf; - struct vr_chain *vr_nextdesc; -}; +/* + * Receive descriptor list size. + */ +#define VR_NRXDESC 64 +#define VR_NRXDESC_MASK (VR_NRXDESC - 1) +#define VR_NEXTRX(x) (((x) + 1) & VR_NRXDESC_MASK) -struct vr_chain_onefrag { - struct vr_desc *vr_ptr; - struct mbuf *vr_mbuf; - struct vr_chain_onefrag *vr_nextdesc; +/* + * Control data structres that are DMA'd to the Rhine chip. We allocate + * them in a single clump that maps to a single DMA segment to make several + * things easier. + * + * Note that since we always copy outgoing packets to aligned transmit + * buffers, we can reduce the transmit descriptors to one per packet. + */ +struct vr_control_data { + struct vr_desc vr_txdescs[VR_NTXDESC]; + struct vr_desc vr_rxdescs[VR_NRXDESC]; }; -struct vr_chain_data { - struct vr_chain_onefrag vr_rx_chain[VR_RX_LIST_CNT]; - struct vr_chain vr_tx_chain[VR_TX_LIST_CNT]; +#define VR_CDOFF(x) offsetof(struct vr_control_data, x) +#define VR_CDTXOFF(x) VR_CDOFF(vr_txdescs[(x)]) +#define VR_CDRXOFF(x) VR_CDOFF(vr_rxdescs[(x)]) - struct vr_chain_onefrag *vr_rx_head; - - struct vr_chain *vr_tx_head; - struct vr_chain *vr_tx_tail; - struct vr_chain *vr_tx_free; +/* + * Software state of transmit and receive descriptors. + */ +struct vr_descsoft { + struct mbuf *ds_mbuf; /* head of mbuf chain */ + bus_dmamap_t ds_dmamap; /* our DMA map */ }; struct vr_softc { @@ -166,43 +208,91 @@ struct vr_softc { void *vr_ats; /* shutdown hook */ bus_space_tag_t vr_bst; /* bus space tag */ bus_space_handle_t vr_bsh; /* bus space handle */ + bus_dma_tag_t vr_dmat; /* bus DMA tag */ pci_chipset_tag_t vr_pc; /* PCI chipset info */ struct ethercom vr_ec; /* Ethernet common info */ u_int8_t vr_enaddr[ETHER_ADDR_LEN]; struct mii_data vr_mii; /* MII/media info */ - caddr_t vr_ldata_ptr; - struct vr_list_data *vr_ldata; - struct vr_chain_data vr_cdata; + + bus_dmamap_t vr_cddmamap; /* control data DMA map */ +#define vr_cddma vr_cddmamap->dm_segs[0].ds_addr + + /* + * Software state for transmit and receive descriptors. + */ + struct vr_descsoft vr_txsoft[VR_NTXDESC]; + struct vr_descsoft vr_rxsoft[VR_NRXDESC]; + + /* + * Control data structures. + */ + struct vr_control_data *vr_control_data; + + int vr_txpending; /* number of TX requests pending */ + int vr_txdirty; /* first dirty TX descriptor */ + int vr_txlast; /* last used TX descriptor */ + + int vr_rxptr; /* next ready RX descriptor */ }; +#define VR_CDTXADDR(sc, x) ((sc)->vr_cddma + VR_CDTXOFF((x))) +#define VR_CDRXADDR(sc, x) ((sc)->vr_cddma + VR_CDRXOFF((x))) + +#define VR_CDTX(sc, x) (&(sc)->vr_control_data->vr_txdescs[(x)]) +#define VR_CDRX(sc, x) (&(sc)->vr_control_data->vr_rxdescs[(x)]) + +#define VR_DSTX(sc, x) (&(sc)->vr_txsoft[(x)]) +#define VR_DSRX(sc, x) (&(sc)->vr_rxsoft[(x)]) + +#define VR_CDTXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->vr_dmat, (sc)->vr_cddmamap, \ + VR_CDTXOFF((x)), sizeof(struct vr_desc), (ops)) + +#define VR_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->vr_dmat, (sc)->vr_cddmamap, \ + VR_CDRXOFF((x)), sizeof(struct vr_desc), (ops)) + +/* + * Note we rely on MCLBYTES being a power of two below. + */ +#define VR_INIT_RXDESC(sc, i) \ +do { \ + struct vr_desc *__d = VR_CDRX((sc), (i)); \ + struct vr_descsoft *__ds = VR_DSRX((sc), (i)); \ + \ + __d->vr_next = VR_CDRXADDR((sc), VR_NEXTRX((i))); \ + __d->vr_status = VR_RXSTAT_FIRSTFRAG | VR_RXSTAT_LASTFRAG | \ + VR_RXSTAT_OWN; \ + __d->vr_data = __ds->ds_dmamap->dm_segs[0].ds_addr; \ + __d->vr_ctl = VR_RXCTL_CHAIN | VR_RXCTL_RX_INTR | \ + ((MCLBYTES - 1) & VR_RXCTL_BUFLEN); \ + VR_CDRXSYNC((sc), (i), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); \ +} while (0) + /* * register space access macros */ -#define CSR_WRITE_4(sc, reg, val) \ +#define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4(sc->vr_bst, sc->vr_bsh, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ +#define CSR_WRITE_2(sc, reg, val) \ bus_space_write_2(sc->vr_bst, sc->vr_bsh, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ +#define CSR_WRITE_1(sc, reg, val) \ bus_space_write_1(sc->vr_bst, sc->vr_bsh, reg, val) -#define CSR_READ_4(sc, reg) \ +#define CSR_READ_4(sc, reg) \ bus_space_read_4(sc->vr_bst, sc->vr_bsh, reg) -#define CSR_READ_2(sc, reg) \ +#define CSR_READ_2(sc, reg) \ bus_space_read_2(sc->vr_bst, sc->vr_bsh, reg) -#define CSR_READ_1(sc, reg) \ +#define CSR_READ_1(sc, reg) \ bus_space_read_1(sc->vr_bst, sc->vr_bsh, reg) #define VR_TIMEOUT 1000 -static int vr_newbuf __P((struct vr_softc *, - struct vr_chain_onefrag *)); -static int vr_encap __P((struct vr_softc *, struct vr_chain *, - struct mbuf *)); +static int vr_add_rxbuf __P((struct vr_softc *, int)); static void vr_rxeof __P((struct vr_softc *)); static void vr_rxeoc __P((struct vr_softc *)); static void vr_txeof __P((struct vr_softc *)); -static void vr_txeoc __P((struct vr_softc *)); static int vr_intr __P((void *)); static void vr_start __P((struct ifnet *)); static int vr_ioctl __P((struct ifnet *, u_long, caddr_t)); @@ -223,8 +313,6 @@ static void vr_mii_statchg __P((struct device *)); static u_int8_t vr_calchash __P((u_int8_t *)); static void vr_setmulti __P((struct vr_softc *)); static void vr_reset __P((struct vr_softc *)); -static int vr_list_rx_init __P((struct vr_softc *)); -static int vr_list_tx_init __P((struct vr_softc *)); #define VR_SETBIT(sc, reg, x) \ CSR_WRITE_1(sc, reg, \ @@ -275,8 +363,6 @@ vr_mii_sync(sc) SIO_CLR(VR_MIICMD_CLK); DELAY(1); } - - return; } /* @@ -430,24 +516,20 @@ vr_mii_statchg(self) struct device *self; { struct vr_softc *sc = (struct vr_softc *)self; - int restart = 0; /* * In order to fiddle with the 'full-duplex' bit in the netconfig * register, we first have to put the transmit and/or receive logic * in the idle state. */ - if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { - restart = 1; - VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); - } + VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); if (sc->vr_mii.mii_media_active & IFM_FDX) VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); else VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); - if (restart) + if (sc->vr_ec.ec_if.if_flags & IFF_RUNNING) VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); /* XXX Update ifp->if_baudrate */ @@ -537,8 +619,6 @@ vr_setmulti(sc) CSR_WRITE_4(sc, VR_MAR0, hashes[0]); CSR_WRITE_4(sc, VR_MAR1, hashes[1]); CSR_WRITE_1(sc, VR_RXCFG, rxfilt); - - return; } static void @@ -560,77 +640,6 @@ vr_reset(sc) /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); - - return; -} - -/* - * Initialize the transmit descriptors. - */ -static int -vr_list_tx_init(sc) - struct vr_softc *sc; -{ - struct vr_chain_data *cd; - struct vr_list_data *ld; - int i; - - cd = &sc->vr_cdata; - ld = sc->vr_ldata; - for (i = 0; i < VR_TX_LIST_CNT; i++) { - cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; - if (i == (VR_TX_LIST_CNT - 1)) - cd->vr_tx_chain[i].vr_nextdesc = - &cd->vr_tx_chain[0]; - else - cd->vr_tx_chain[i].vr_nextdesc = - &cd->vr_tx_chain[i + 1]; - } - - cd->vr_tx_free = &cd->vr_tx_chain[0]; - cd->vr_tx_tail = cd->vr_tx_head = NULL; - - return (0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int -vr_list_rx_init(sc) - struct vr_softc *sc; -{ - struct vr_chain_data *cd; - struct vr_list_data *ld; - int i; - - cd = &sc->vr_cdata; - ld = sc->vr_ldata; - - for (i = 0; i < VR_RX_LIST_CNT; i++) { - cd->vr_rx_chain[i].vr_ptr = - (struct vr_desc *)&ld->vr_rx_list[i]; - if (vr_newbuf(sc, &cd->vr_rx_chain[i]) == ENOBUFS) - return (ENOBUFS); - if (i == (VR_RX_LIST_CNT - 1)) { - cd->vr_rx_chain[i].vr_nextdesc = - &cd->vr_rx_chain[0]; - ld->vr_rx_list[i].vr_next = - vtophys(&ld->vr_rx_list[0]); - } else { - cd->vr_rx_chain[i].vr_nextdesc = - &cd->vr_rx_chain[i + 1]; - ld->vr_rx_list[i].vr_next = - vtophys(&ld->vr_rx_list[i + 1]); - } - } - - cd->vr_rx_head = &cd->vr_rx_chain[0]; - - return (0); } /* @@ -641,31 +650,41 @@ vr_list_rx_init(sc) * overflow the field and make a mess. */ static int -vr_newbuf(sc, c) +vr_add_rxbuf(sc, i) struct vr_softc *sc; - struct vr_chain_onefrag *c; + int i; { - struct mbuf *m_new = NULL; + struct vr_descsoft *ds = VR_DSRX(sc, i); + struct mbuf *m_new; + int error; MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("%s: no memory for rx list -- packet dropped!\n", - sc->vr_dev.dv_xname); + if (m_new == NULL) return (ENOBUFS); - } MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("%s: no memory for rx list -- packet dropped!\n", - sc->vr_dev.dv_xname); + if ((m_new->m_flags & M_EXT) == 0) { m_freem(m_new); return (ENOBUFS); } - c->vr_mbuf = m_new; - c->vr_ptr->vr_status = VR_RXSTAT; - c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); - c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; + if (ds->ds_mbuf != NULL) + bus_dmamap_unload(sc->vr_dmat, ds->ds_dmamap); + + ds->ds_mbuf = m_new; + + error = bus_dmamap_load(sc->vr_dmat, ds->ds_dmamap, + m_new->m_ext.ext_buf, m_new->m_ext.ext_size, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s: unable to load rx DMA map %d, error = %d\n", + sc->vr_dev.dv_xname, i, error); + panic("vr_add_rxbuf"); /* XXX */ + } + + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); + + VR_INIT_RXDESC(sc, i); return (0); } @@ -681,16 +700,27 @@ vr_rxeof(sc) struct ether_header *eh; struct mbuf *m; struct ifnet *ifp; - struct vr_chain_onefrag *cur_rx; - int total_len = 0; + struct vr_desc *d; + struct vr_descsoft *ds; + int i, total_len; u_int32_t rxstat; ifp = &sc->vr_ec.ec_if; - while (!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & - VR_RXSTAT_OWN)) { - cur_rx = sc->vr_cdata.vr_rx_head; - sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; + for (i = sc->vr_rxptr;; i = VR_NEXTRX(i)) { + d = VR_CDRX(sc, i); + ds = VR_DSRX(sc, i); + + VR_CDRXSYNC(sc, i, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + rxstat = d->vr_status; + + if (rxstat & VR_RXSTAT_OWN) { + /* + * We have processed all of the receive buffers. + */ + break; + } /* * If an error occurs, update stats, clear the @@ -699,41 +729,48 @@ vr_rxeof(sc) * comes up in the ring. */ if (rxstat & VR_RXSTAT_RXERR) { + const char *errstr; + ifp->if_ierrors++; - printf("%s: rx error: ", sc->vr_dev.dv_xname); switch (rxstat & 0x000000FF) { case VR_RXSTAT_CRCERR: - printf("crc error\n"); + errstr = "crc error"; break; case VR_RXSTAT_FRAMEALIGNERR: - printf("frame alignment error\n"); + errstr = "frame alignment error"; break; case VR_RXSTAT_FIFOOFLOW: - printf("FIFO overflow\n"); + errstr = "FIFO overflow"; break; case VR_RXSTAT_GIANT: - printf("received giant packet\n"); + errstr = "received giant packet"; break; case VR_RXSTAT_RUNT: - printf("received runt packet\n"); + errstr = "received runt packet"; break; case VR_RXSTAT_BUSERR: - printf("system bus error\n"); + errstr = "system bus error"; break; case VR_RXSTAT_BUFFERR: - printf("rx buffer error\n"); + errstr = "rx buffer error"; break; default: - printf("unknown rx error\n"); + errstr = "unknown rx error"; break; } - cur_rx->vr_ptr->vr_status = VR_RXSTAT; - cur_rx->vr_ptr->vr_ctl = VR_RXCTL|VR_RXLEN; + printf("%s: receive error: %s\n", sc->vr_dev.dv_xname, + errstr); + + VR_INIT_RXDESC(sc, i); + continue; } + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + /* No errors; receive the packet. */ - total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); + total_len = VR_RXBYTES(d->vr_status); /* * XXX The VIA Rhine chip includes the CRC with every @@ -752,11 +789,12 @@ vr_rxeof(sc) * result in a lost packet, but there's little else we * can do in this situation. */ - m = cur_rx->vr_mbuf; - if (vr_newbuf(sc, cur_rx) == ENOBUFS) { + m = ds->ds_mbuf; + if (vr_add_rxbuf(sc, i) == ENOBUFS) { ifp->if_ierrors++; - cur_rx->vr_ptr->vr_status = VR_RXSTAT; - cur_rx->vr_ptr->vr_ctl = VR_RXCTL|VR_RXLEN; + VR_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); continue; } #else @@ -770,13 +808,14 @@ vr_rxeof(sc) if (m == NULL) { dropit: ifp->if_ierrors++; - cur_rx->vr_ptr->vr_status = VR_RXSTAT; - cur_rx->vr_ptr->vr_ctl = VR_RXCTL|VR_RXLEN; + VR_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); continue; } if (total_len > (MHLEN - 2)) { MCLGET(m, M_DONTWAIT); - if (m == NULL) + if ((m->m_flags & M_EXT) == 0) goto dropit; } m->m_data += 2; @@ -785,12 +824,13 @@ vr_rxeof(sc) * Note that we use clusters for incoming frames, so the * buffer is virtually contiguous. */ - memcpy(mtod(m, caddr_t), mtod(cur_rx->vr_mbuf, caddr_t), + memcpy(mtod(m, caddr_t), mtod(ds->ds_mbuf, caddr_t), total_len); /* Allow the recieve descriptor to continue using its mbuf. */ - cur_rx->vr_ptr->vr_status = VR_RXSTAT; - cur_rx->vr_ptr->vr_ctl = VR_RXCTL|VR_RXLEN; + VR_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); #endif /* __NO_STRICT_ALIGNMENT */ ifp->if_ipackets++; @@ -806,19 +846,21 @@ vr_rxeof(sc) */ if (ifp->if_bpf) { bpf_mtap(ifp->if_bpf, m); - if (ifp->if_flags & IFF_PROMISC && - (memcmp(eh->ether_dhost, sc->vr_enaddr, - ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0)) { + if ((ifp->if_flags & IFF_PROMISC) != 0 && + (rxstat & (VR_RXSTAT_RX_PHYS | VR_RXSTAT_RX_BROAD | + VR_RXSTAT_RX_MULTI)) == 0) { m_freem(m); continue; } } #endif /* Remove header from mbuf and pass it on. */ - m_adj(m, sizeof (struct ether_header)); + m_adj(m, sizeof(struct ether_header)); ether_input(ifp, eh, m); } + + /* Update the receive pointer. */ + sc->vr_rxptr = i; } void @@ -828,7 +870,7 @@ vr_rxeoc(sc) vr_rxeof(sc); VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); - CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + CSR_WRITE_4(sc, VR_RXADDR, VR_CDRXADDR(sc, sc->vr_rxptr)); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); } @@ -837,37 +879,39 @@ vr_rxeoc(sc) * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ - static void vr_txeof(sc) struct vr_softc *sc; { - struct vr_chain *cur_tx; - struct ifnet *ifp; - register struct mbuf *n; - - ifp = &sc->vr_ec.ec_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; + struct ifnet *ifp = &sc->vr_ec.ec_if; + struct vr_desc *d; + struct vr_descsoft *ds; + u_int32_t txstat; + int i; - /* Sanity check. */ - if (sc->vr_cdata.vr_tx_head == NULL) - return; + ifp->if_flags &= ~IFF_OACTIVE; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ - while (sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) { - u_int32_t txstat; + for (i = sc->vr_txdirty; sc->vr_txpending != 0; + i = VR_NEXTTX(i), sc->vr_txpending--) { + d = VR_CDTX(sc, i); + ds = VR_DSTX(sc, i); - cur_tx = sc->vr_cdata.vr_tx_head; - txstat = cur_tx->vr_ptr->vr_status; + VR_CDTXSYNC(sc, i, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + txstat = d->vr_status; if (txstat & VR_TXSTAT_OWN) break; + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, + 0, ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->vr_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + if (txstat & VR_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & VR_TXSTAT_DEFER) @@ -876,39 +920,19 @@ vr_txeof(sc) ifp->if_collisions++; } - ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; - + ifp->if_collisions += (txstat & VR_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; - MFREE(cur_tx->vr_mbuf, n); - cur_tx->vr_mbuf = NULL; - - if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) { - sc->vr_cdata.vr_tx_head = NULL; - sc->vr_cdata.vr_tx_tail = NULL; - break; - } - - sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc; } -} -/* - * TX 'end of channel' interrupt handler. - */ -static void -vr_txeoc(sc) - struct vr_softc *sc; -{ - struct ifnet *ifp; + /* Update the dirty transmit buffer pointer. */ + sc->vr_txdirty = i; - ifp = &sc->vr_ec.ec_if; - - ifp->if_timer = 0; - - if (sc->vr_cdata.vr_tx_head == NULL) { - ifp->if_flags &= ~IFF_OACTIVE; - sc->vr_cdata.vr_tx_tail = NULL; - } + /* + * Cancel the watchdog timer if there are no pending + * transmissions. + */ + if (sc->vr_txpending == 0) + ifp->if_timer = 0; } static int @@ -918,12 +942,12 @@ vr_intr(arg) struct vr_softc *sc; struct ifnet *ifp; u_int16_t status; - int handled = 0; + int handled = 0, dotx = 0; sc = arg; ifp = &sc->vr_ec.ec_if; - /* Supress unwanted interrupts. */ + /* Suppress unwanted interrupts. */ if ((ifp->if_flags & IFF_UP) == 0) { vr_stop(sc); return (0); @@ -945,29 +969,36 @@ vr_intr(arg) if (status & VR_ISR_RX_OK) vr_rxeof(sc); - if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || - (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) || - (status & VR_ISR_RX_DROPPED)) { - vr_rxeof(sc); + if (status & + (VR_ISR_RX_ERR | VR_ISR_RX_NOBUF | VR_ISR_RX_OFLOW | + VR_ISR_RX_DROPPED)) vr_rxeoc(sc); - } if (status & VR_ISR_TX_OK) { + dotx = 1; vr_txeof(sc); - vr_txeoc(sc); } - if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)) { + if (status & (VR_ISR_TX_UNDERRUN | VR_ISR_TX_ABRT)) { + if (status & VR_ISR_TX_UNDERRUN) + printf("%s: transmit underrun\n", + sc->vr_dev.dv_xname); + if (status & VR_ISR_TX_ABRT) + printf("%s: transmit aborted\n", + sc->vr_dev.dv_xname); ifp->if_oerrors++; + dotx = 1; vr_txeof(sc); - if (sc->vr_cdata.vr_tx_head != NULL) { + if (sc->vr_txpending) { VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); } } if (status & VR_ISR_BUSERR) { - vr_reset(sc); + printf("%s: PCI bus error\n", sc->vr_dev.dv_xname); + /* vr_init() calls vr_start() */ + dotx = 0; vr_init(sc); } } @@ -975,85 +1006,13 @@ vr_intr(arg) /* Re-enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); - if (ifp->if_snd.ifq_head != NULL) { + if (dotx) vr_start(ifp); - } return (handled); } /* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int -vr_encap(sc, c, m_head) - struct vr_softc *sc; - struct vr_chain *c; - struct mbuf *m_head; -{ - int frag = 0; - struct vr_desc *f = NULL; - int total_len; - struct mbuf *m; - - m = m_head; - total_len = 0; - - /* - * The VIA Rhine wants packet buffers to be longword - * aligned, but very often our mbufs aren't. Rather than - * waste time trying to decide when to copy and when not - * to copy, just do it all the time. - */ - if (m != NULL) { - struct mbuf *m_new = NULL; - - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("%s: no memory for tx list", - sc->vr_dev.dv_xname); - return (1); - } - if (m_head->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - printf("%s: no memory for tx list", - sc->vr_dev.dv_xname); - return (1); - } - } - m_copydata(m_head, 0, m_head->m_pkthdr.len, - mtod(m_new, caddr_t)); - m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; - m_freem(m_head); - m_head = m_new; - /* - * The Rhine chip doesn't auto-pad, so we have to make - * sure to pad short frames out to the minimum frame length - * ourselves. - */ - if (m_head->m_len < VR_MIN_FRAMELEN) { - m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len; - m_new->m_len = m_new->m_pkthdr.len; - } - f = c->vr_ptr; - f->vr_data = vtophys(mtod(m_new, caddr_t)); - f->vr_ctl = total_len = m_new->m_len; - f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; - f->vr_status = 0; - frag = 1; - } - - c->vr_mbuf = m_head; - c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; - c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr); - - return (0); -} - -/* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are @@ -1063,37 +1022,86 @@ static void vr_start(ifp) struct ifnet *ifp; { - struct vr_softc *sc; - struct mbuf *m_head = NULL; - struct vr_chain *cur_tx = NULL, *start_tx; - - sc = ifp->if_softc; + struct vr_softc *sc = ifp->if_softc; + struct mbuf *m0, *m; + struct vr_desc *d; + struct vr_descsoft *ds; + int error, firsttx, nexttx, opending; /* - * Check for an available queue slot. If there are none, - * punt. + * Remember the previous txpending and the first transmit + * descriptor we use. */ - if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) { - ifp->if_flags |= IFF_OACTIVE; - return; - } + opending = sc->vr_txpending; + firsttx = VR_NEXTTX(sc->vr_txlast); - start_tx = sc->vr_cdata.vr_tx_free; - - while (sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (sc->vr_txpending < VR_NTXDESC) { + /* + * Grab a packet off the queue. + */ + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) break; - /* Pick a descriptor off the free list. */ - cur_tx = sc->vr_cdata.vr_tx_free; - sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc; + /* + * Get the next available transmit descriptor. + */ + nexttx = VR_NEXTTX(sc->vr_txlast); + d = VR_CDTX(sc, nexttx); + ds = VR_DSTX(sc, nexttx); + + /* + * Load the DMA map. If this fails, the packet didn't + * fit in one DMA segment, and we need to copy. Note, + * the packet must also be aligned. + */ + if ((mtod(m0, bus_addr_t) & 3) != 0 || + bus_dmamap_load_mbuf(sc->vr_dmat, ds->ds_dmamap, m0, + BUS_DMA_NOWAIT) != 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: unable to allocate Tx mbuf\n", + sc->vr_dev.dv_xname); + IF_PREPEND(&ifp->if_snd, m0); + break; + } + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + printf("%s: unable to allocate Tx " + "cluster\n", sc->vr_dev.dv_xname); + m_freem(m); + IF_PREPEND(&ifp->if_snd, m0); + break; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len; + m_freem(m0); + m0 = m; + error = bus_dmamap_load_mbuf(sc->vr_dmat, + ds->ds_dmamap, m0, BUS_DMA_NOWAIT); + if (error) { + printf("%s: unable to load Tx buffer, " + "error = %d\n", sc->vr_dev.dv_xname, error); + IF_PREPEND(&ifp->if_snd, m0); + break; + } + } - /* Pack the data into the descriptor. */ - vr_encap(sc, cur_tx, m_head); + /* Sync the DMA map. */ + bus_dmamap_sync(sc->vr_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE); - if (cur_tx != start_tx) - VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; + /* + * Store a pointer to the packet so we can free it later. + */ + ds->ds_mbuf = m0; #if NBPFILTER > 0 /* @@ -1101,27 +1109,72 @@ vr_start(ifp) * to him. */ if (ifp->if_bpf) - bpf_mtap(ifp->if_bpf, cur_tx->vr_mbuf); + bpf_mtap(ifp->if_bpf, m0); #endif - VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; - VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_TX_GO); + + /* + * Fill in the transmit descriptor. The Rhine + * doesn't auto-pad, so we have to do this ourselves. + */ + d->vr_data = ds->ds_dmamap->dm_segs[0].ds_addr; + d->vr_ctl = m0->m_pkthdr.len < VR_MIN_FRAMELEN ? + VR_MIN_FRAMELEN : m0->m_pkthdr.len; + d->vr_ctl |= + VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG|VR_TXCTL_LASTFRAG; + + /* + * If this is the first descriptor we're enqueuing, + * don't give it to the Rhine yet. That could cause + * a race condition. We'll do it below. + */ + if (nexttx == firsttx) + d->vr_status = 0; + else + d->vr_status = VR_TXSTAT_OWN; + + VR_CDTXSYNC(sc, nexttx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Advance the tx pointer. */ + sc->vr_txpending++; + sc->vr_txlast = nexttx; } - /* - * If there are no frames queued, bail. - */ - if (cur_tx == NULL) - return; + if (sc->vr_txpending == VR_NTXDESC) { + /* No more slots left; notify upper layer. */ + ifp->if_flags |= IFF_OACTIVE; + } - sc->vr_cdata.vr_tx_tail = cur_tx; + if (sc->vr_txpending != opending) { + /* + * We enqueued packets. If the transmitter was idle, + * reset the txdirty pointer. + */ + if (opending == 0) + sc->vr_txdirty = firsttx; - if (sc->vr_cdata.vr_tx_head == NULL) - sc->vr_cdata.vr_tx_head = start_tx; + /* + * Cause a transmit interrupt to happen on the + * last packet we enqueued. + */ + VR_CDTX(sc, sc->vr_txlast)->vr_ctl |= VR_TXCTL_FINT; + VR_CDTXSYNC(sc, sc->vr_txlast, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; + /* + * The entire packet chain is set up. Give the + * first descriptor to the Rhine now. + */ + VR_CDTX(sc, firsttx)->vr_status = VR_TXSTAT_OWN; + VR_CDTXSYNC(sc, firsttx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Start the transmitter. */ + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_TX_GO); + + /* Set the watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } } /* @@ -1133,11 +1186,13 @@ vr_init(xsc) { struct vr_softc *sc = xsc; struct ifnet *ifp = &sc->vr_ec.ec_if; + struct vr_desc *d; + int i; - /* - * Cancel pending I/O and free all RX/TX buffers. - */ + /* Cancel pending I/O. */ vr_stop(sc); + + /* Reset the Rhine to a known state. */ vr_reset(sc); VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); @@ -1146,18 +1201,28 @@ vr_init(xsc) VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); - /* Init circular RX list. */ - if (vr_list_rx_init(sc) == ENOBUFS) { - printf("%s: initialization failed: no " - "memory for rx buffers\n", sc->vr_dev.dv_xname); - vr_stop(sc); - return; + /* + * Initialize the transmit desciptor ring. txlast is initialized + * to the end of the list so that it will wrap around to the first + * descriptor when the first packet is transmitted. + */ + for (i = 0; i < VR_NTXDESC; i++) { + d = VR_CDTX(sc, i); + memset(d, 0, sizeof(struct vr_desc)); + d->vr_next = VR_CDTXADDR(sc, VR_NEXTTX(i)); + VR_CDTXSYNC(sc, i, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); } + sc->vr_txpending = 0; + sc->vr_txdirty = 0; + sc->vr_txlast = VR_NTXDESC - 1; /* - * Init tx descriptors. + * Initialize the receive descriptor ring. The buffers are + * already allocated. */ - vr_list_tx_init(sc); + for (i = 0; i < VR_NRXDESC; i++) + VR_INIT_RXDESC(sc, i); + sc->vr_rxptr = 0; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) @@ -1171,29 +1236,22 @@ vr_init(xsc) else VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); - /* - * Program the multicast filter, if necessary. - */ + /* Program the multicast filter, if necessary. */ vr_setmulti(sc); - /* - * Load the address of the RX list. - */ - CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + /* Give the transmit and recieve rings to the Rhine. */ + CSR_WRITE_4(sc, VR_RXADDR, VR_CDRXADDR(sc, sc->vr_rxptr)); + CSR_WRITE_4(sc, VR_TXADDR, VR_CDTXADDR(sc, VR_NEXTTX(sc->vr_txlast))); + + /* Set current media. */ + mii_mediachg(&sc->vr_mii); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| VR_CMD_TX_ON|VR_CMD_RX_ON| VR_CMD_RX_GO); - /* Set current media. */ - mii_mediachg(&sc->vr_mii); - - CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); - - /* - * Enable interrupts. - */ + /* Enable interrupts. */ CSR_WRITE_2(sc, VR_ISR, 0xFFFF); CSR_WRITE_2(sc, VR_IMR, VR_INTRS); @@ -1202,6 +1260,9 @@ vr_init(xsc) /* Start one second timer. */ timeout(vr_tick, sc, hz); + + /* Attempt to start output on the interface. */ + vr_start(ifp); } /* @@ -1277,14 +1338,29 @@ vr_ioctl(ifp, command, data) break; case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + vr_stop(sc); + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + vr_init(sc); + } else if ((ifp->if_flags & IFF_UP) != 0) { + /* + * Reset the interface to pick up changes in any other + * flags that affect the hardware state. + */ vr_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - vr_stop(sc); } - error = 0; break; + case SIOCADDMULTI: case SIOCDELMULTI: if (command == SIOCADDMULTI) @@ -1293,21 +1369,26 @@ vr_ioctl(ifp, command, data) error = ether_delmulti(ifr, &sc->vr_ec); if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ vr_setmulti(sc); error = 0; } break; + case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->vr_mii.mii_media, command); break; + default: error = EINVAL; break; } splx(s); - return (error); } @@ -1315,21 +1396,12 @@ static void vr_watchdog(ifp) struct ifnet *ifp; { - struct vr_softc *sc; - - sc = ifp->if_softc; + struct vr_softc *sc = ifp->if_softc; + printf("%s: device timeout\n", sc->vr_dev.dv_xname); ifp->if_oerrors++; - printf("%s: watchdog timeout\n", sc->vr_dev.dv_xname); - vr_stop(sc); - vr_reset(sc); vr_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - vr_start(ifp); - - return; } /* @@ -1351,12 +1423,13 @@ vr_tick(arg) /* * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. + * transmit lists. */ static void vr_stop(sc) struct vr_softc *sc; { + struct vr_descsoft *ds; struct ifnet *ifp; int i; @@ -1373,31 +1446,22 @@ vr_stop(sc) CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); /* - * Free data in the RX lists. + * Release any queued transmit buffers. */ - for (i = 0; i < VR_RX_LIST_CNT; i++) { - if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { - m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); - sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; + for (i = 0; i < VR_NTXDESC; i++) { + ds = VR_DSTX(sc, i); + if (ds->ds_mbuf != NULL) { + bus_dmamap_unload(sc->vr_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; } } - bzero((char *)&sc->vr_ldata->vr_rx_list, - sizeof (sc->vr_ldata->vr_rx_list)); /* - * Free the TX list buffers. + * Mark the interface down and cancel the watchdog timer. */ - for (i = 0; i < VR_TX_LIST_CNT; i++) { - if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { - m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); - sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; - } - } - - bzero((char *)&sc->vr_ldata->vr_tx_list, - sizeof (sc->vr_ldata->vr_tx_list)); - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; } static struct vr_type *vr_lookup __P((struct pci_attach_args *)); @@ -1462,13 +1526,12 @@ vr_attach(parent, self, aux) { struct vr_softc *sc = (struct vr_softc *) self; struct pci_attach_args *pa = (struct pci_attach_args *) aux; + bus_dma_segment_t seg; struct vr_type *vrt; - int i; u_int32_t command; struct ifnet *ifp; - unsigned int round; - caddr_t roundptr; u_char eaddr[ETHER_ADDR_LEN]; + int i, rseg, error; #define PCI_CONF_WRITE(r, v) pci_conf_write(pa->pa_pc, pa->pa_tag, (r), (v)) #define PCI_CONF_READ(r) pci_conf_read(pa->pa_pc, pa->pa_tag, (r)) @@ -1576,10 +1639,6 @@ vr_attach(parent, self, aux) printf("%s: interrupting at %s\n", sc->vr_dev.dv_xname, intrstr); } - sc->vr_ats = shutdownhook_establish(vr_shutdown, sc); - if (sc->vr_ats == NULL) - printf("%s: warning: couldn't establish shutdown hook\n", - sc->vr_dev.dv_xname); /* Reset the adapter. */ vr_reset(sc); @@ -1604,27 +1663,81 @@ vr_attach(parent, self, aux) bcopy(eaddr, sc->vr_enaddr, ETHER_ADDR_LEN); - sc->vr_ldata_ptr = malloc(sizeof (struct vr_list_data) + 8, - M_DEVBUF, M_NOWAIT); - if (sc->vr_ldata_ptr == NULL) { - free(sc, M_DEVBUF); - printf("%s: no memory for list buffers!\n", - sc->vr_dev.dv_xname); - return; + sc->vr_dmat = pa->pa_dmat; + + /* + * Allocate the control data structures, and create and load + * the DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->vr_dmat, + sizeof(struct vr_control_data), PAGE_SIZE, 0, &seg, 1, &rseg, + 0)) != 0) { + printf("%s: unable to allocate control data, error = %d\n", + sc->vr_dev.dv_xname, error); + goto fail_0; } - sc->vr_ldata = (struct vr_list_data *)sc->vr_ldata_ptr; - round = (unsigned long)sc->vr_ldata_ptr & 0xF; - roundptr = sc->vr_ldata_ptr; - for (i = 0; i < 8; i++) { - if (round % 8) { - round++; - roundptr++; - } else - break; + if ((error = bus_dmamem_map(sc->vr_dmat, &seg, rseg, + sizeof(struct vr_control_data), (caddr_t *)&sc->vr_control_data, + BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map control data, error = %d\n", + sc->vr_dev.dv_xname, error); + goto fail_1; + } + + if ((error = bus_dmamap_create(sc->vr_dmat, + sizeof(struct vr_control_data), 1, + sizeof(struct vr_control_data), 0, 0, + &sc->vr_cddmamap)) != 0) { + printf("%s: unable to create control data DMA map, " + "error = %d\n", sc->vr_dev.dv_xname, error); + goto fail_2; + } + + if ((error = bus_dmamap_load(sc->vr_dmat, sc->vr_cddmamap, + sc->vr_control_data, sizeof(struct vr_control_data), NULL, + 0)) != 0) { + printf("%s: unable to load control data DMA map, error = %d\n", + sc->vr_dev.dv_xname, error); + goto fail_3; + } + + /* + * Create the transmit buffer DMA maps. + */ + for (i = 0; i < VR_NTXDESC; i++) { + if ((error = bus_dmamap_create(sc->vr_dmat, MCLBYTES, + 1, MCLBYTES, 0, 0, + &VR_DSTX(sc, i)->ds_dmamap)) != 0) { + printf("%s: unable to create tx DMA map %d, " + "error = %d\n", sc->vr_dev.dv_xname, i, error); + goto fail_4; + } + } + + /* + * Create the receive buffer DMA maps. + */ + for (i = 0; i < VR_NRXDESC; i++) { + if ((error = bus_dmamap_create(sc->vr_dmat, MCLBYTES, 1, + MCLBYTES, 0, 0, + &VR_DSRX(sc, i)->ds_dmamap)) != 0) { + printf("%s: unable to create rx DMA map %d, " + "error = %d\n", sc->vr_dev.dv_xname, i, error); + goto fail_5; + } + } + + /* + * Pre-allocate the receive buffers. + */ + for (i = 0; i < VR_NRXDESC; i++) { + if ((error = vr_add_rxbuf(sc, i)) != 0) { + printf("%s: unable to allocate or map rx buffer %d, " + "error = %d\n", sc->vr_dev.dv_xname, i, error); + goto fail_6; + } } - sc->vr_ldata = (struct vr_list_data *)roundptr; - bzero(sc->vr_ldata, sizeof (struct vr_list_data)); ifp = &sc->vr_ec.ec_if; ifp->if_softc = sc; @@ -1667,4 +1780,36 @@ vr_attach(parent, self, aux) if (sc->vr_ats == NULL) printf("%s: warning: couldn't establish shutdown hook\n", sc->vr_dev.dv_xname); + return; + + fail_6: + for (i = 0; i < VR_NRXDESC; i++) { + if (sc->vr_rxsoft[i].ds_mbuf != NULL) { + bus_dmamap_unload(sc->vr_dmat, + sc->vr_rxsoft[i].ds_dmamap); + (void) m_freem(sc->vr_rxsoft[i].ds_mbuf); + } + } + fail_5: + for (i = 0; i < VR_NRXDESC; i++) { + if (sc->vr_rxsoft[i].ds_dmamap != NULL) + bus_dmamap_destroy(sc->vr_dmat, + sc->vr_rxsoft[i].ds_dmamap); + } + fail_4: + for (i = 0; i < VR_NTXDESC; i++) { + if (sc->vr_txsoft[i].ds_dmamap != NULL) + bus_dmamap_destroy(sc->vr_dmat, + sc->vr_txsoft[i].ds_dmamap); + } + bus_dmamap_unload(sc->vr_dmat, sc->vr_cddmamap); + fail_3: + bus_dmamap_destroy(sc->vr_dmat, sc->vr_cddmamap); + fail_2: + bus_dmamem_unmap(sc->vr_dmat, (caddr_t)sc->vr_control_data, + sizeof(struct vr_control_data)); + fail_1: + bus_dmamem_free(sc->vr_dmat, &seg, rseg); + fail_0: + return; } |
