summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2006-02-25 23:49:05 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2006-02-25 23:49:05 +0000
commit69badbaf6342f3bf8dfa11bc272590bb1ec2e357 (patch)
tree162f0cb9467e94402ca3e6ba4f048fce50147e43 /sys/dev
parenta586f735a16368f79a821eaf9e23a68182a93c7b (diff)
Add vic(4), a driver for the VMware vmxnet network interface. This is
an alternative approach to the much slower pcn(4) emulation of VMware. The driver is not yet working and work in progress. ok brad@ deraadt@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/if_vic.c1104
-rw-r--r--sys/dev/pci/if_vicreg.h195
-rw-r--r--sys/dev/pci/if_vicvar.h107
3 files changed, 1406 insertions, 0 deletions
diff --git a/sys/dev/pci/if_vic.c b/sys/dev/pci/if_vic.c
new file mode 100644
index 00000000000..3425bdaa477
--- /dev/null
+++ b/sys/dev/pci/if_vic.c
@@ -0,0 +1,1104 @@
+/* $OpenBSD: if_vic.c,v 1.1 2006/02/25 23:49:04 reyk Exp $ */
+
+/*
+ * Copyright (c) 2006 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for the VMware Virtual NIC ("vmxnet")
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/timeout.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/pci/if_vicreg.h>
+#include <dev/pci/if_vicvar.h>
+
+int vic_pci_match(struct device *, void *, void *);
+void vic_pci_attach(struct device *, struct device *, void *);
+void vic_pci_shutdown(void *);
+
+int vic_attach(struct vic_softc *);
+void vic_link_state(struct vic_softc *);
+void vic_shutdown(void *);
+int vic_intr(void *);
+void vic_rx_proc(struct vic_softc *);
+void vic_tx_proc(struct vic_softc *);
+void vic_iff(struct vic_softc *, u_int);
+void vic_getlladdr(struct vic_softc *);
+void vic_setlladdr(struct vic_softc *);
+int vic_media_change(struct ifnet *);
+void vic_media_status(struct ifnet *, struct ifmediareq *);
+void vic_start(struct ifnet *);
+int vic_tx_start(struct vic_softc *, struct mbuf *);
+void vic_watchdog(struct ifnet *);
+int vic_ioctl(struct ifnet *, u_long, caddr_t);
+void vic_init(struct ifnet *);
+void vic_stop(struct ifnet *);
+void vic_printb(unsigned short, char *);
+void vic_timer(void *);
+void vic_poll(void *); /* XXX poll */
+
+struct mbuf *vic_alloc_mbuf(struct vic_softc *, bus_dmamap_t);
+int vic_alloc_data(struct vic_softc *);
+void vic_reset_data(struct vic_softc *);
+void vic_free_data(struct vic_softc *);
+
+struct vic_pci_softc {
+ struct vic_softc sc_sc;
+ pci_chipset_tag_t sc_pc;
+ pcitag_t sc_pcitag;
+ pci_intr_handle_t sc_iht;
+ void *sc_ih;
+};
+
+struct cfdriver vic_cd = {
+ 0, "vic", DV_IFNET
+};
+
+struct cfattach vic_ca = {
+ sizeof(struct vic_pci_softc),
+ vic_pci_match,
+ vic_pci_attach
+};
+
+const struct pci_matchid vic_pci_devices[] = {
+ { PCI_VENDOR_VMWARE, PCI_PRODUCT_VMWARE_NET }
+};
+
+extern int ifqmaxlen;
+
+int
+vic_pci_match(struct device *parent, void *match, void *aux)
+{
+ return (pci_matchbyid((struct pci_attach_args *)aux,
+ vic_pci_devices, sizeof(vic_pci_devices) /
+ sizeof(vic_pci_devices[0])));
+}
+
+void
+vic_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct vic_pci_softc *psc = (struct vic_pci_softc *)self;
+ struct pci_attach_args *pa = aux;
+ struct vic_softc *sc = &psc->sc_sc;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ pcitag_t pt = pa->pa_tag;
+ const char *intrstr = NULL;
+ void *hook;
+ pcireg_t res;
+
+ psc->sc_pc = pc;
+ psc->sc_pcitag = pt;
+ sc->sc_psc = psc;
+
+ /* Validate PCI configuration */
+ res = pci_conf_read(pc, pt, PCI_COMMAND_STATUS_REG);
+ if ((res & PCI_COMMAND_MEM_ENABLE) == 0) {
+ printf(": memory mapping is not available\n");
+ return;
+ }
+
+ /* Enable memory mapping */
+ if (pci_mapreg_map(pa, VIC_BAR0, PCI_MAPREG_TYPE_IO, 0,
+ &sc->sc_st, &sc->sc_sh, NULL, NULL, 0)) {
+ printf(": memory mapping of register space failed\n");
+ return;
+ }
+
+ /* Map and enable the interrupt line */
+ if (pci_intr_map(pa, &psc->sc_iht)) {
+ printf(": interrupt mapping failed\n");
+ return;
+ }
+ intrstr = pci_intr_string(pc, psc->sc_iht);
+ if ((psc->sc_ih = pci_intr_establish(pc,
+ psc->sc_iht, IPL_NET, vic_intr, sc, sc->sc_dev.dv_xname)) == NULL) {
+ printf(": failed to establish the interrupt\n");
+ return;
+ }
+
+ printf(": %s\n", intrstr);
+
+ sc->sc_dmat = pa->pa_dmat;
+
+ if ((hook = shutdownhook_establish(vic_pci_shutdown, psc)) == NULL) {
+ printf(": failed to establish the shutdown hook\n");
+ return;
+ }
+
+ /* Finally, attach the device */
+ if (vic_attach(sc) == 0)
+ return;
+
+ shutdownhook_disestablish(hook);
+
+ return;
+}
+
+void
+vic_pci_shutdown(void *self)
+{
+ struct vic_pci_softc *psc = (struct vic_pci_softc *)self;
+ vic_shutdown(&psc->sc_sc);
+}
+
+int
+vic_attach(struct vic_softc *sc)
+{
+ struct ifnet *ifp;
+
+ sc->sc_ver_major = VIC_READ(VIC_VERSION_MAJOR);
+ sc->sc_ver_minor = VIC_READ(VIC_VERSION_MINOR);
+
+ printf("%s: vmxnet %X", sc->sc_dev.dv_xname,
+ sc->sc_ver_major & ~VIC_VERSION_MAJOR_M);
+
+ /* Check for a supported version */
+ if (((sc->sc_ver_major & VIC_VERSION_MAJOR_M) !=
+ (VIC_MAGIC & VIC_VERSION_MAJOR_M)) ||
+ VIC_MAGIC > sc->sc_ver_major ||
+ VIC_MAGIC < sc->sc_ver_minor) {
+ printf("unsupported device\n");
+ return (1);
+ }
+
+ VIC_WRITE(VIC_CMD, VIC_CMD_NUM_Rx_BUF);
+ sc->sc_nrxbuf = VIC_READ(VIC_CMD);
+ if (sc->sc_nrxbuf > VIC_NBUF_MAX || sc->sc_nrxbuf == 0)
+ sc->sc_nrxbuf = VIC_NBUF;
+ VIC_WRITE(VIC_CMD, VIC_CMD_NUM_Tx_BUF);
+ sc->sc_ntxbuf = VIC_READ(VIC_CMD);
+ if (sc->sc_ntxbuf > VIC_NBUF_MAX || sc->sc_ntxbuf == 0)
+ sc->sc_ntxbuf = VIC_NBUF;
+
+ VIC_WRITE(VIC_CMD, VIC_CMD_FEATURE);
+ sc->sc_feature = VIC_READ(VIC_CMD);
+
+ VIC_WRITE(VIC_CMD, VIC_CMD_HWCAP);
+ sc->sc_cap = VIC_READ(VIC_CMD);
+
+ if (sc->sc_cap) {
+ printf(", ");
+ vic_printb(sc->sc_cap, VIC_CMD_HWCAP_BITS);
+ }
+
+ vic_getlladdr(sc);
+
+ printf(", address: %s\n", ether_sprintf(sc->sc_lladdr));
+
+ /* Initialise pseudo media types */
+ ifmedia_init(&sc->sc_media, 0, vic_media_change,
+ vic_media_status);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
+
+ ifp = &sc->sc_ac.ac_if;
+ ifp->if_carp = NULL;
+ ifp->if_type = IFT_ETHER;
+ ifp->if_softc = sc;
+ ifp->if_start = vic_start;
+ ifp->if_watchdog = vic_watchdog;
+ ifp->if_ioctl = vic_ioctl;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+
+ ifp->if_capabilities = 0;
+#if 0
+ /* XXX interface capabilities */
+ if (sc->sc_cap & VIC_CMD_HWCAP_VLAN)
+ ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
+ if (sc->sc_cap & VIC_CMD_HWCAP_CSUM)
+ ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 |
+ IFCAP_CSUM_UDPv4;
+#endif
+
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, sizeof(ifp->if_xname));
+ bcopy(sc->sc_lladdr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
+
+ /* Allocate Rx and Tx queues */
+ if (vic_alloc_data(sc) != 0) {
+ printf("%s: could not allocate queues\n", ifp->if_xname);
+ return (1);
+ }
+
+ /* Attach the device structures */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ /* Initialize timeout for link state update. */
+ timeout_set(&sc->sc_timer, vic_timer, sc);
+
+ /* XXX poll */
+ timeout_set(&sc->sc_poll, vic_poll, sc);
+
+ return (0);
+}
+
+void
+vic_link_state(struct vic_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ u_int32_t status;
+ int link_state = LINK_STATE_DOWN;
+
+ status = VIC_READ(VIC_STATUS);
+ if (status & VIC_STATUS_CONNECTED)
+ link_state = LINK_STATE_UP;
+ if (ifp->if_link_state != link_state) {
+ ifp->if_link_state = link_state;
+ if_link_state_change(ifp);
+ }
+}
+
+void
+vic_shutdown(void *arg)
+{
+ struct vic_softc *sc = (struct vic_softc *)arg;
+ vic_stop(&sc->sc_ac.ac_if);
+}
+
+int
+vic_intr(void *arg)
+{
+ struct vic_softc *sc = (struct vic_softc *)arg;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+
+ VIC_WRITE(VIC_CMD, VIC_CMD_INTR_ACK);
+
+#ifdef VIC_DEBUG
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: %s\n", ifp->if_xname, __func__);
+#endif
+
+ vic_rx_proc(sc);
+ vic_tx_proc(sc);
+
+ return (1);
+}
+
+void
+vic_rx_proc(struct vic_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct vic_rxdesc *desc;
+ struct vic_rxbuf *rxb;
+ struct mbuf *m;
+ int len, idx;
+
+ for (;;) {
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map, 0,
+ sizeof(struct vic_data),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ idx = letoh32(sc->sc_data->vd_rx_nextidx);
+ if (idx >= sc->sc_data->vd_rx_length) {
+ ifp->if_ierrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: receive index error\n",
+ ifp->if_xname);
+ break;
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map,
+ VIC_OFF_RXDESC(idx), sizeof(struct vic_rxdesc),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ desc = &sc->sc_rxq[idx];
+
+ if (desc->rx_owner != VIC_OWNER_DRIVER)
+ break;
+
+ len = letoh32(desc->rx_length);
+ ifp->if_ibytes += len;
+ ifp->if_ipackets++;
+
+ if (len < ETHER_MIN_LEN) {
+ ifp->if_iqdrops++;
+ goto nextp;
+ }
+
+ if ((rxb = (struct vic_rxbuf *)desc->rx_priv) == NULL ||
+ rxb->rxb_m == NULL) {
+ ifp->if_ierrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: receive buffer error\n",
+ ifp->if_xname);
+ break;
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, rxb->rxb_map, 0,
+ rxb->rxb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->sc_dmat, rxb->rxb_map);
+ m = rxb->rxb_m;
+ rxb->rxb_m = NULL;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /* Get new mbuf for the Rx queue */
+ if ((rxb->rxb_m = vic_alloc_mbuf(sc, rxb->rxb_map)) == NULL) {
+ ifp->if_ierrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: receive buffer failed\n",
+ ifp->if_xname);
+ break;
+ }
+ desc->rx_physaddr = rxb->rxb_map->dm_segs->ds_addr;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+
+ ether_input_mbuf(ifp, m);
+
+ nextp:
+ desc->rx_owner = VIC_OWNER_NIC;
+ VIC_INC(sc->sc_data->vd_rx_nextidx, sc->sc_data->vd_rx_length);
+ }
+}
+
+void
+vic_tx_proc(struct vic_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct vic_txdesc *desc;
+ struct vic_txbuf *txb;
+ int idx;
+
+ for (; sc->sc_txpending;) {
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map, 0,
+ sizeof(struct vic_data),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ idx = letoh32(sc->sc_data->vd_tx_curidx);
+ if (idx >= sc->sc_data->vd_tx_length) {
+ ifp->if_oerrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: transmit index error\n",
+ ifp->if_xname);
+ break;
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map,
+ VIC_OFF_TXDESC(idx), sizeof(struct vic_rxdesc),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ desc = &sc->sc_txq[idx];
+
+ if (desc->tx_owner != VIC_OWNER_DRIVER)
+ break;
+
+ if ((txb = (struct vic_txbuf *)desc->tx_priv) == NULL ||
+ txb->txb_m == NULL) {
+ ifp->if_oerrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: transmit buffer error\n",
+ ifp->if_xname);
+ break;
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, txb->txb_map, 0,
+ txb->txb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->sc_dmat, txb->txb_map);
+ if (txb->txb_m != NULL) {
+ m_freem(txb->txb_m);
+ txb->txb_m = NULL;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+
+ sc->sc_txpending--;
+ sc->sc_data->vd_tx_stopped = 0;
+
+ VIC_INC(sc->sc_data->vd_tx_curidx, sc->sc_data->vd_tx_length);
+ }
+
+ sc->sc_txtimeout = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (!IFQ_IS_EMPTY(&ifp->if_snd))
+ vic_start(ifp);
+}
+
+void
+vic_iff(struct vic_softc *sc, u_int flags)
+{
+ /* XXX ALLMULTI */
+ memset(&sc->sc_data->vd_mcastfil, 0xff,
+ sizeof(sc->sc_data->vd_mcastfil));
+ sc->sc_data->vd_iff = flags;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map, 0,
+ sizeof(struct vic_data), BUS_DMASYNC_POSTWRITE);
+
+ VIC_WRITE(VIC_CMD, VIC_CMD_MCASTFIL);
+ VIC_WRITE(VIC_CMD, VIC_CMD_IFF);
+}
+
+void
+vic_getlladdr(struct vic_softc *sc)
+{
+ u_int32_t reg;
+ int i;
+
+ /* Get MAC address */
+ reg = sc->sc_cap & VIC_CMD_HWCAP_VPROM ? VIC_VPROM : VIC_LLADDR;
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->sc_lladdr[i] = VIC_READ8(reg + i);
+
+ /* Update the MAC address register */
+ if (reg == VIC_VPROM)
+ vic_setlladdr(sc);
+}
+
+void
+vic_setlladdr(struct vic_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ VIC_WRITE8(VIC_LLADDR + i, sc->sc_lladdr[i]);
+}
+
+int
+vic_media_change(struct ifnet *ifp)
+{
+ /* Ignore */
+ return (0);
+}
+
+void
+vic_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct vic_softc *sc = (struct vic_softc *)ifp->if_softc;
+
+ imr->ifm_active = IFM_ETHER | IFM_AUTO;
+ imr->ifm_status = IFM_AVALID;
+
+ vic_link_state(sc);
+
+ if (ifp->if_link_state == LINK_STATE_UP &&
+ ifp->if_flags & IFF_UP)
+ imr->ifm_status |= IFM_ACTIVE;
+}
+
+void
+vic_start(struct ifnet *ifp)
+{
+ struct vic_softc *sc = (struct vic_softc *)ifp->if_softc;
+ struct mbuf *m;
+ int error;
+
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
+
+ for (;; error = 0) {
+ IFQ_POLL(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+
+ if ((error = vic_tx_start(sc, m)) != 0)
+ ifp->if_oerrors++;
+ }
+}
+
+int
+vic_tx_start(struct vic_softc *sc, struct mbuf *m)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct vic_txbuf *txb;
+ struct vic_txdesc *desc;
+ struct mbuf *m0 = NULL;
+ int idx;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map, 0,
+ sizeof(struct vic_data),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ idx = letoh32(sc->sc_data->vd_tx_nextidx);
+ if (idx >= sc->sc_data->vd_tx_length) {
+ ifp->if_oerrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: transmit index error\n",
+ ifp->if_xname);
+ return (EINVAL);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map,
+ VIC_OFF_TXDESC(idx), sizeof(struct vic_txdesc),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ desc = &sc->sc_txq[idx];
+ txb = (struct vic_txbuf *)desc->tx_priv;
+
+ if (VIC_TXURN(sc)) {
+ ifp->if_flags |= IFF_OACTIVE;
+ return (ENOBUFS);
+ } else if (txb == NULL) {
+ ifp->if_oerrors++;
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: transmit buffer error\n",
+ ifp->if_xname);
+ return (ENOBUFS);
+ } else if (txb->txb_m != NULL) {
+ sc->sc_data->vd_tx_stopped = 1;
+ ifp->if_oerrors++;
+ return (ENOMEM);
+ }
+
+ if (bus_dmamap_load_mbuf(sc->sc_dmat, txb->txb_map,
+ m, BUS_DMA_NOWAIT) != 0) {
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == NULL)
+ return (ENOBUFS);
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(m0, M_DONTWAIT);
+ if (!(m0->m_flags & M_EXT)) {
+ m_freem(m0);
+ return (E2BIG);
+ }
+ }
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t));
+ m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len;
+ if (bus_dmamap_load_mbuf(sc->sc_dmat, txb->txb_map,
+ m0, BUS_DMA_NOWAIT) != 0) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ }
+
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m0 != NULL) {
+ m_freem(m);
+ m = m0;
+ m0 = NULL;
+ }
+
+ desc->tx_flags = VIC_TX_FLAGS_KEEP;
+ desc->tx_sa.sa_length = 1;
+ desc->tx_sa.sa_sg[0].sg_length = htole16(m->m_len);
+ desc->tx_sa.sa_sg[0].sg_addr_low = txb->txb_map->dm_segs->ds_addr;
+ desc->tx_owner = VIC_OWNER_NIC;
+
+ if (VIC_TXURN_WARN(sc)) {
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: running out of tx descriptors\n",
+ ifp->if_xname);
+ desc->tx_flags |= VIC_TX_FLAGS_TXURN;
+ }
+
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_len;
+
+ sc->sc_txpending++;
+ sc->sc_txtimeout = VIC_TX_TIMEOUT;
+ ifp->if_timer = 1;
+
+ VIC_INC(sc->sc_data->vd_tx_nextidx, sc->sc_data->vd_tx_length);
+
+ return (0);
+}
+
+void
+vic_watchdog(struct ifnet *ifp)
+{
+ struct vic_softc *sc = (struct vic_softc *)ifp->if_softc;
+
+ ifp->if_timer = 0;
+
+ if (sc->sc_txpending && sc->sc_txtimeout > 0) {
+ if (--sc->sc_txtimeout == 0) {
+ printf("%s: device timeout\n", ifp->if_xname);
+ ifp->if_flags &= ~IFF_RUNNING;
+ vic_init(ifp);
+ ifp->if_oerrors++;
+ return;
+ }
+ ifp->if_timer = 1;
+ }
+
+ if (!IFQ_IS_EMPTY(&ifp->if_snd))
+ vic_start(ifp);
+}
+
+int
+vic_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct vic_softc *sc = (struct vic_softc *)ifp->if_softc;
+ struct ifaddr *ifa;
+ struct ifreq *ifr;
+ int s, error = 0;
+
+ s = splnet();
+
+ if ((error = ether_ioctl(ifp, &sc->sc_ac, cmd, data)) > 0) {
+ splx(s);
+ return (error);
+ }
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifa = (struct ifaddr *)data;
+ ifp->if_flags |= IFF_UP;
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ arp_ifinit(&sc->sc_ac, ifa);
+#endif
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ vic_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ vic_stop(ifp);
+ }
+ break;
+
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ETHERMTU_JUMBO)
+ error = EINVAL;
+ else if (ifp->if_mtu != ifr->ifr_mtu)
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ error = (cmd == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->sc_ac) :
+ ether_delmulti(ifr, &sc->sc_ac);
+
+ if (error == ENETRESET)
+ error = 0;
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+ (IFF_UP | IFF_RUNNING))
+ vic_init(ifp);
+ error = 0;
+ }
+
+ splx(s);
+
+ return (error);
+}
+
+void
+vic_init(struct ifnet *ifp)
+{
+ struct vic_softc *sc = (struct vic_softc *)ifp->if_softc;
+ int s;
+
+ s = splnet();
+
+ timeout_add(&sc->sc_timer, hz * VIC_TIMER_DELAY);
+
+ vic_reset_data(sc);
+
+#ifdef notyet
+ VIC_WRITE(VIC_CMD, VIC_CMD_INTR_ENABLE);
+#endif
+
+ VIC_WRITE(VIC_DATA_ADDR, sc->sc_physaddr);
+ VIC_WRITE(VIC_DATA_LENGTH, htole32(sc->sc_size));
+ if (ifp->if_flags & IFF_PROMISC)
+ vic_iff(sc, VIC_CMD_IFF_PROMISC);
+ else
+ vic_iff(sc, VIC_CMD_IFF_BROADCAST | VIC_CMD_IFF_MULTICAST);
+
+#ifdef VIC_DEBUG
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: physaddr 0x%08x length 0x%08x\n",
+ ifp->if_xname, sc->sc_physaddr, sc->sc_size);
+#endif
+
+ sc->sc_data->vd_tx_stopped = sc->sc_data->vd_tx_queued = 0;
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /* XXX poll */
+ if (ifp->if_flags & IFF_LINK0) {
+ sc->sc_polling = 1;
+ timeout_add(&sc->sc_poll, VIC_TIMER_MS(100));
+ } else
+ sc->sc_polling = 0;
+
+ splx(s);
+}
+
+void
+vic_stop(struct ifnet *ifp)
+{
+ struct vic_softc *sc = (struct vic_softc *)ifp->if_softc;
+ int s;
+
+ s = splnet();
+
+ sc->sc_txtimeout = 0;
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+#ifdef notyet
+ VIC_WRITE(VIC_CMD, VIC_CMD_INTR_DISABLE);
+#endif
+
+ VIC_WRITE(VIC_DATA_ADDR, 0);
+ vic_iff(sc, 0);
+
+ sc->sc_data->vd_tx_stopped = 1;
+ timeout_del(&sc->sc_timer);
+
+ /* XXX poll */
+ if (sc->sc_polling) {
+ sc->sc_polling = 0;
+ timeout_del(&sc->sc_poll);
+ }
+
+ splx(s);
+}
+
+void
+vic_printb(unsigned short v, char *bits)
+{
+ int i, any = 0;
+ char c;
+
+ /*
+ * Used to print capability bits on startup
+ */
+ bits++;
+ if (bits) {
+ while ((i = *bits++)) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ printf(" ");
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ printf("%c", c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ }
+}
+
+struct mbuf *
+vic_alloc_mbuf(struct vic_softc *sc, bus_dmamap_t map)
+{
+ struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("%s: could not allocate mbuf\n",
+ sc->sc_dev.dv_xname);
+ return (NULL);
+ }
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ printf("%s: could not allocate mbuf cluster\n",
+ sc->sc_dev.dv_xname);
+ return (NULL);
+ }
+ if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) {
+ printf("%s: could not load mbuf DMA map",
+ sc->sc_dev.dv_xname);
+ return (NULL);
+ }
+
+ return (m);
+}
+
+int
+vic_alloc_data(struct vic_softc *sc)
+{
+ struct vic_rxbuf *rxb;
+ struct vic_txbuf *txb;
+ u_int32_t offset;
+ int i, error;
+
+ sc->sc_size = sizeof(struct vic_data) +
+ (sc->sc_nrxbuf + VIC_QUEUE2_SIZE) * sizeof(struct vic_rxdesc) +
+ sc->sc_ntxbuf * sizeof(struct vic_txdesc);
+
+ if ((error = bus_dmamap_create(sc->sc_dmat, sc->sc_size, 1,
+ sc->sc_size, 0, BUS_DMA_NOWAIT, &sc->sc_map)) != 0) {
+ printf("%s: could not create DMA material\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if ((error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_size, PAGE_SIZE, 0,
+ &sc->sc_seg, 1, &sc->sc_nsegs, BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: could not allocate DMA memory\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if ((error = bus_dmamem_map(sc->sc_dmat, &sc->sc_seg,
+ sc->sc_nsegs, sc->sc_size, (caddr_t *)&sc->sc_data,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
+ printf("%s: could not map DMA memory\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_map,
+ sc->sc_data, sc->sc_size, NULL, BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: could not load DMA map\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ bzero(sc->sc_data, sc->sc_size);
+ sc->sc_physaddr = sc->sc_map->dm_segs->ds_addr;
+ sc->sc_data->vd_magic = VIC_MAGIC;
+ sc->sc_data->vd_length = htole32(sc->sc_size);
+ offset = (u_int32_t)sc->sc_data + sizeof(struct vic_data);
+
+#ifdef VIC_DEBUG
+ printf("%s: (rxbuf %d * %d) (txbuf %d * %d) (size %d)\n",
+ sc->sc_dev.dv_xname,
+ sc->sc_nrxbuf, sizeof(struct vic_rxdesc),
+ sc->sc_ntxbuf, sizeof(struct vic_txdesc),
+ sc->sc_size);
+#endif
+
+ /* Setup the Rx queue */
+ sc->sc_rxq = (struct vic_rxdesc *)offset;
+ sc->sc_data->vd_rx_offset = htole32(offset);
+ sc->sc_data->vd_rx_length = htole32(sc->sc_nrxbuf);
+ offset += sc->sc_nrxbuf + sizeof(struct vic_rxdesc);
+ for (i = 0; i < sc->sc_nrxbuf; i++) {
+ rxb = &sc->sc_rxbuf[i];
+ if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
+ VIC_MAX_SCATTER, MCLBYTES, 0, BUS_DMA_NOWAIT,
+ &rxb->rxb_map)) != 0) {
+ printf("%s: could not create rx DMA map\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ /* Preallocate the Rx mbuf */
+ if ((rxb->rxb_m = vic_alloc_mbuf(sc, rxb->rxb_map)) == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+ sc->sc_rxq[i].rx_physaddr = rxb->rxb_map->dm_segs->ds_addr;
+ }
+
+ /* Setup Rx queue 2 (unused gap) */
+ sc->sc_rxq2 = (struct vic_rxdesc *)offset;
+ sc->sc_data->vd_rx_offset = htole32(offset);
+ sc->sc_data->vd_rx_length = htole32(1);
+ sc->sc_rxq2[0].rx_owner = VIC_OWNER_DRIVER;
+ offset += sizeof(struct vic_rxdesc);
+
+ /* Setup the Tx queue */
+ sc->sc_txq = (struct vic_txdesc *)offset;
+ sc->sc_data->vd_tx_offset = htole32(offset);
+ sc->sc_data->vd_tx_length = htole32(sc->sc_nrxbuf);
+ offset += sc->sc_ntxbuf + sizeof(struct vic_txdesc);
+ for (i = 0; i < sc->sc_ntxbuf; i++) {
+ txb = &sc->sc_txbuf[i];
+ if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
+ VIC_MAX_SCATTER, MCLBYTES, 0, BUS_DMA_NOWAIT,
+ &txb->txb_map)) != 0) {
+ printf("%s: could not create tx DMA map\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+ }
+
+ return (0);
+
+ fail:
+ vic_free_data(sc);
+
+ return (error);
+}
+
+void
+vic_reset_data(struct vic_softc *sc)
+{
+ struct vic_rxbuf *rxb;
+ struct vic_txbuf *txb;
+ int i;
+
+ for (i = 0; i < sc->sc_nrxbuf; i++) {
+ rxb = &sc->sc_rxbuf[i];
+
+ bzero(&sc->sc_rxq[i], sizeof(struct vic_rxdesc));
+ sc->sc_rxq[i].rx_physaddr =
+ rxb->rxb_map->dm_segs->ds_addr;
+ sc->sc_rxq[i].rx_buflength = htole32(MCLBYTES);
+ sc->sc_rxq[i].rx_owner = VIC_OWNER_NIC;
+ sc->sc_rxq[i].rx_priv = rxb;
+
+ bus_dmamap_sync(sc->sc_dmat, rxb->rxb_map, 0,
+ rxb->rxb_map->dm_mapsize, BUS_DMASYNC_PREWRITE);
+ }
+
+ for (i = 0; i < sc->sc_ntxbuf; i++) {
+ txb = &sc->sc_txbuf[i];
+
+ bzero(&sc->sc_txq[i], sizeof(struct vic_txdesc));
+ sc->sc_txq[i].tx_owner = VIC_OWNER_DRIVER;
+ sc->sc_txq[i].tx_priv = txb;
+
+ if (sc->sc_txbuf[i].txb_m != NULL) {
+ bus_dmamap_sync(sc->sc_dmat, txb->txb_map, 0,
+ txb->txb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, txb->txb_map);
+ m_freem(sc->sc_txbuf[i].txb_m);
+ sc->sc_txbuf[i].txb_m = NULL;
+ }
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map, 0,
+ sc->sc_map->dm_mapsize, BUS_DMASYNC_PREWRITE);
+}
+
+void
+vic_free_data(struct vic_softc *sc)
+{
+ bus_dmamap_t map;
+ int i;
+
+ if (sc->sc_data == NULL)
+ return;
+
+ /* Free Rx queue */
+ for (i = 0; i < sc->sc_nrxbuf; i++) {
+ if ((map = sc->sc_rxbuf[i].rxb_map) == NULL)
+ continue;
+ if (sc->sc_rxbuf[i].rxb_m != NULL) {
+ bus_dmamap_sync(sc->sc_dmat, map, 0,
+ map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, map);
+ m_freem(sc->sc_rxbuf[i].rxb_m);
+ }
+ bus_dmamap_destroy(sc->sc_dmat, map);
+ }
+
+ /* Free Tx queue */
+ for (i = 0; i < sc->sc_ntxbuf; i++) {
+ if ((map = sc->sc_txbuf[i].txb_map) == NULL)
+ continue;
+ if (sc->sc_txbuf[i].txb_m != NULL) {
+ bus_dmamap_sync(sc->sc_dmat, map, 0,
+ map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, map);
+ m_freem(sc->sc_txbuf[i].txb_m);
+ }
+ bus_dmamap_destroy(sc->sc_dmat, map);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_map, 0,
+ sc->sc_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_map);
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_data, sc->sc_size);
+ bus_dmamem_free(sc->sc_dmat, &sc->sc_seg, sc->sc_nsegs);
+}
+
+void
+vic_timer(void *arg)
+{
+ struct vic_softc *sc = (struct vic_softc *)arg;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+
+#ifdef VIC_DEBUG
+ if (ifp->if_flags & IFF_DEBUG) {
+ if (sc->sc_polling)
+ printf("%s: %s (polling #%u)\n",
+ ifp->if_xname, __func__, sc->sc_polling);
+ else
+ printf("%s: %s\n",
+ ifp->if_xname, __func__);
+ }
+#endif
+
+ /* Update link state (if changed) */
+ vic_link_state(sc);
+
+ /* Re-schedule another timeout. */
+ timeout_add(&sc->sc_timer, hz * VIC_TIMER_DELAY);
+}
+
+ /* XXX poll */
+void
+vic_poll(void *arg)
+{
+ struct vic_softc *sc = (struct vic_softc *)arg;
+ int s;
+
+ s = splnet();
+
+ vic_rx_proc(sc);
+ vic_tx_proc(sc);
+
+ VIC_INC_POS(sc->sc_polling, UINT_MAX);
+
+ timeout_add(&sc->sc_poll, VIC_TIMER_MS(100));
+
+ splx(s);
+}
diff --git a/sys/dev/pci/if_vicreg.h b/sys/dev/pci/if_vicreg.h
new file mode 100644
index 00000000000..809aea43ac1
--- /dev/null
+++ b/sys/dev/pci/if_vicreg.h
@@ -0,0 +1,195 @@
+/* $OpenBSD: if_vicreg.h,v 1.1 2006/02/25 23:49:04 reyk Exp $ */
+
+/*
+ * Copyright (c) 2006 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_IC_VICREG_H
+#define _DEV_IC_VICREG_H
+
+#define VIC_MAGIC 0xbabe864f
+
+/*
+ * Register address offsets
+ */
+
+#define VIC_DATA_ADDR 0x0000 /* Shared data address */
+#define VIC_DATA_LENGTH 0x0004 /* Shared data length */
+#define VIC_Tx_ADDR 0x0008 /* Tx pointer address */
+
+/* Command register */
+#define VIC_CMD 0x000c /* Command register */
+#define VIC_CMD_INTR_ACK 0x00000001 /* Acknowledge interrupt */
+#define VIC_CMD_MCASTFIL 0x00000002 /* Multicast address filter */
+#define VIC_CMD_MCASTFIL_LENGTH 2
+#define VIC_CMD_IFF 0x00000004 /* Interface flags */
+#define VIC_CMD_IFF_PROMISC 0x0001 /* Promiscous enabled */
+#define VIC_CMD_IFF_BROADCAST 0x0002 /* Broadcast enabled */
+#define VIC_CMD_IFF_MULTICAST 0x0004 /* Multicast enabled */
+#define VIC_CMD_RESERVED_8 0x00000008
+#define VIC_CMD_RESERVED_10 0x00000010
+#define VIC_CMD_INTR_DISABLE 0x00000020 /* Enable interrupts */
+#define VIC_CMD_INTR_ENABLE 0x00000040 /* Disable interrupts */
+#define VIC_CMD_RESERVED_80 0x00000080
+#define VIC_CMD_Tx_DONE 0x00000100 /* Tx done register */
+#define VIC_CMD_NUM_Rx_BUF 0x00000200 /* Number of Rx buffers */
+#define VIC_CMD_NUM_Tx_BUF 0x00000400 /* Number of Tx buffers */
+#define VIC_CMD_NUM_PINNED_BUF 0x00000800 /* Number of pinned buffers */
+#define VIC_CMD_HWCAP 0x00001000 /* Capability register */
+#define VIC_CMD_HWCAP_SG 0x0001 /* Scatter-gather transmits */
+#define VIC_CMD_HWCAP_CSUM_IPv4 0x0002 /* Hardware checksum of TCP/UDP */
+#define VIC_CMD_HWCAP_CSUM_ALL 0x0004 /* Hardware checksum support */
+#define VIC_CMD_HWCAP_CSUM \
+ (VIC_CMD_HWCAP_CSUM_IPv4 | VIC_CMD_HWCAP_CSUM_ALL)
+#define VIC_CMD_HWCAP_DMA_HIGH 0x0008 /* High DMA mapping possible */
+#define VIC_CMD_HWCAP_TOE 0x0010 /* TCP offload engine available */
+#define VIC_CMD_HWCAP_TSO 0x0020 /* TCP segmentation offload */
+#define VIC_CMD_HWCAP_TSO_SW 0x0040 /* Software TCP segmentation */
+#define VIC_CMD_HWCAP_VPROM 0x0080 /* Virtual PROM available */
+#define VIC_CMD_HWCAP_VLAN_Tx 0x0100 /* Hardware VLAN MTU Rx */
+#define VIC_CMD_HWCAP_VLAN_Rx 0x0200 /* Hardware VLAN MTU Tx */
+#define VIC_CMD_HWCAP_VLAN_SW 0x0400 /* Software VLAN MTU */
+#define VIC_CMD_HWCAP_VLAN \
+ (VIC_CMD_HWCAP_VLAN_Tx | VIC_CMD_HWCAP_VLAN_Rx | VIC_CMD_HWCAP_VLAN_SW)
+#define VIC_CMD_HWCAP_BITS \
+ "\20\01SG\02CSUM4\03CSUM\04HDMA\05TOE" \
+ "\06TSO\07TSOSW\10VPROM\13VLANTx\14VLANRx\15VLANSW"
+#define VIC_CMD_FEATURE 0x00002000 /* Additional feature register */
+#define VIC_CMD_FEATURE_0_Tx 0x0001
+#define VIC_CMD_FEATURE_TSO 0x0002
+
+#define VIC_LLADDR 0x0010 /* MAC address register */
+#define VIC_VERSION_MINOR 0x0018 /* Minor version register */
+#define VIC_VERSION_MAJOR 0x001c /* Major version register */
+#define VIC_VERSION_MAJOR_M 0xffff0000
+
+/* Status register */
+#define VIC_STATUS 0x0020
+#define VIC_STATUS_CONNECTED 0x00000001
+#define VIC_STATUS_ENABLED 0x00000002
+
+#define VIC_TOE_ADDR 0x0024 /* TCP offload address */
+
+/* Virtual PROM address */
+#define VIC_VPROM 0x0028
+#define VIC_VPROM_LENGTH 6
+
+/*
+ * Shared DMA data structures
+ */
+
+struct vic_sg {
+ u_int32_t sg_addr_low;
+ u_int16_t sg_addr_high;
+ u_int16_t sg_length;
+} __packed;
+
+#define VIC_SG_MAX 6
+#define VIC_SG_ADDR_MACH 0
+#define VIC_SG_ADDR_PHYS 1
+#define VIC_SG_ADDR_VIRT 3
+
+struct vic_sgarray {
+ u_int16_t sa_addr_type;
+ u_int16_t sa_length;
+ struct vic_sg sa_sg[VIC_SG_MAX];
+};
+
+struct vic_rxdesc {
+ u_int64_t rx_physaddr;
+ u_int32_t rx_buflength;
+ u_int32_t rx_length;
+ u_int16_t rx_owner;
+ u_int16_t rx_flags;
+ void *rx_priv;
+} __packed;
+
+#define VIC_RX_FLAGS_CSUMHW_OK 0x0001
+
+struct vic_txdesc {
+ u_int16_t tx_flags;
+ u_int16_t tx_owner;
+ void *tx_priv;
+ u_int32_t tx_tsomss;
+ struct vic_sgarray tx_sa;
+} __packed;
+
+#define VIC_TX_FLAGS_KEEP 0x0001
+#define VIC_TX_FLAGS_TXURN 0x0002
+#define VIC_TX_FLAGS_CSUMHW 0x0004
+#define VIC_TX_FLAGS_TSO 0x0008
+#define VIC_TX_FLAGS_PINNED 0x0010
+#define VIC_TX_FLAGS_QRETRY 0x1000
+
+struct vic_stats {
+ u_int32_t vs_tx_count;
+ u_int32_t vs_tx_packets;
+ u_int32_t vs_tx_0copy;
+ u_int32_t vs_tx_copy;
+ u_int32_t vs_tx_maxpending;
+ u_int32_t vs_tx_stopped;
+ u_int32_t vs_tx_overrun;
+ u_int32_t vs_intr;
+ u_int32_t vs_rx_packets;
+ u_int32_t vs_rx_underrun;
+} __packed;
+
+struct vic_data {
+ u_int32_t vd_magic;
+
+ u_int32_t vd_rx_length;
+ u_int32_t vd_rx_nextidx;
+ u_int32_t vd_rx_length2;
+ u_int32_t vd_rx_nextidx2;
+
+ u_int32_t vd_irq;
+ u_int32_t vd_iff;
+
+ u_int32_t vd_mcastfil[VIC_CMD_MCASTFIL_LENGTH];
+
+ u_int32_t vd_reserved1[1];
+
+ u_int32_t vd_tx_length;
+ u_int32_t vd_tx_curidx;
+ u_int32_t vd_tx_nextidx;
+ u_int32_t vd_tx_stopped;
+ u_int32_t vd_tx_triggerlvl;
+ u_int32_t vd_tx_queued;
+ u_int32_t vd_tx_minlength;
+
+ u_int32_t vd_reserved2[6];
+
+ u_int32_t vd_rx_saved_nextidx;
+ u_int32_t vd_rx_saved_nextidx2;
+ u_int32_t vd_tx_saved_nextidx;
+
+ u_int32_t vd_length;
+ u_int32_t vd_rx_offset;
+ u_int32_t vd_rx_offset2;
+ u_int32_t vd_tx_offset;
+ u_int32_t vd_debug;
+ u_int32_t vd_tx_physaddr;
+ u_int32_t vd_tx_physaddr_length;
+ u_int32_t vd_tx_maxlength;
+
+ struct vic_stats vd_stats;
+} __packed;
+
+#define VIC_OWNER_DRIVER 0
+#define VIC_OWNER_DRIVER_PEND 1
+#define VIC_OWNER_NIC 2
+#define VIC_OWNER_NIC_PEND 3
+
+#endif /* _DEV_IC_VICREG_H */
diff --git a/sys/dev/pci/if_vicvar.h b/sys/dev/pci/if_vicvar.h
new file mode 100644
index 00000000000..1458acccac2
--- /dev/null
+++ b/sys/dev/pci/if_vicvar.h
@@ -0,0 +1,107 @@
+/* $OpenBSD: if_vicvar.h,v 1.1 2006/02/25 23:49:04 reyk Exp $ */
+
+/*
+ * Copyright (c) 2006 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_IC_VICVAR_H
+#define _DEV_IC_VICVAR_H
+
+#define VIC_DEBUG 1
+
+#define VIC_BAR0 0x10 /* Base Address Register */
+
+#define VIC_NBUF 100
+#define VIC_NBUF_MAX 128
+#define VIC_MAX_SCATTER 1 /* 8? */
+#define VIC_QUEUE_SIZE VIC_NBUF_MAX
+#define VIC_QUEUE2_SIZE 1
+#define VIC_INC(_x, _y) (_x) = ((_x) + 1) % (_y)
+#define VIC_INC_POS(_x, _y) (_x) = (++(_x)) % (_y) ? (_x) : 1
+#define VIC_TX_TIMEOUT 5
+#define VIC_TIMER_DELAY 2
+#define VIC_TIMER_MS(_ms) (_ms * hz / 1000)
+
+#define VIC_TXURN_WARN(_sc) ((_sc)->sc_txpending >= ((_sc)->sc_ntxbuf - 5))
+#define VIC_TXURN(_sc) ((_sc)->sc_txpending >= (_sc)->sc_ntxbuf)
+#define VIC_OFF_RXDESC(_n) \
+ (sizeof(struct vic_data) + ((_n) * sizeof(struct vic_rxdesc)))
+#define VIC_OFF_TXDESC(_n) \
+ (sizeof(struct vic_data) + \
+ ((sc->sc_nrxbuf + VIC_QUEUE2_SIZE) * sizeof(struct vic_rxdesc)) + \
+ ((_n) * sizeof(struct vic_txdesc)))
+
+#define VIC_WRITE(_reg, _val) \
+ bus_space_write_4(sc->sc_st, sc->sc_sh, (_reg), (_val))
+#define VIC_READ(_reg) \
+ bus_space_read_4(sc->sc_st, sc->sc_sh, (_reg))
+#define VIC_WRITE8(_reg, _val) \
+ bus_space_write_1(sc->sc_st, sc->sc_sh, (_reg), (_val))
+#define VIC_READ8(_reg) \
+ bus_space_read_1(sc->sc_st, sc->sc_sh, (_reg))
+
+struct vic_rxbuf {
+ bus_dmamap_t rxb_map;
+ struct mbuf *rxb_m;
+};
+
+struct vic_txbuf {
+ bus_dmamap_t txb_map;
+ struct mbuf *txb_m;
+};
+
+struct vic_softc {
+ struct device sc_dev;
+
+ void *sc_psc;
+ struct arpcom sc_ac;
+ struct ifmedia sc_media;
+ struct timeout sc_timer;
+
+ struct timeout sc_poll; /* XXX poll */
+ u_int sc_polling;
+
+ int (*sc_enable)(struct vic_softc *);
+ void (*sc_disable)(struct vic_softc *);
+ void (*sc_power)(struct vic_softc *, int);
+
+ bus_space_tag_t sc_st;
+ bus_space_handle_t sc_sh;
+ bus_dma_tag_t sc_dmat;
+
+ u_int32_t sc_ver_major, sc_ver_minor;
+ u_int32_t sc_cap, sc_feature;
+
+ u_int8_t sc_lladdr[ETHER_ADDR_LEN];
+
+ u_int32_t sc_nrxbuf, sc_ntxbuf;
+
+ struct vic_rxdesc *sc_rxq, *sc_rxq2;
+ struct vic_rxbuf sc_rxbuf[VIC_QUEUE_SIZE];
+
+ struct vic_txdesc *sc_txq;
+ struct vic_txbuf sc_txbuf[VIC_QUEUE_SIZE];
+ int sc_txpending;
+ int sc_txtimeout;
+
+ struct vic_data *sc_data;
+ bus_addr_t sc_physaddr;
+ bus_dmamap_t sc_map;
+ bus_dma_segment_t sc_seg;
+ int sc_nsegs;
+ int sc_size;
+};
+
+#endif /* _DEV_IC_VICVAR_H */