Add driver for Attansic/Atheros L2 FastEthernet
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 13 Jul 2009 05:45:25 +0000 (13:45 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Tue, 14 Jul 2009 12:00:14 +0000 (20:00 +0800)
Submitted-by: Alexander Polakov <polachok@gmail.com> w/ modification
Obtained-from: FreeBSD

sys/dev/netif/ae/Makefile [new file with mode: 0644]
sys/dev/netif/ae/if_ae.c [new file with mode: 0644]
sys/dev/netif/ae/if_aereg.h [new file with mode: 0644]
sys/dev/netif/ae/if_aevar.h [new file with mode: 0644]

diff --git a/sys/dev/netif/ae/Makefile b/sys/dev/netif/ae/Makefile
new file mode 100644 (file)
index 0000000..937cd4c
--- /dev/null
@@ -0,0 +1,6 @@
+KMOD=  if_ae
+SRCS=  if_ae.c
+SRCS+= miibus_if.h device_if.h bus_if.h pci_if.h
+KMODDEPS= miibus
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/ae/if_ae.c b/sys/dev/netif/ae/if_ae.c
new file mode 100644 (file)
index 0000000..b2954ed
--- /dev/null
@@ -0,0 +1,1852 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * 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.
+ *
+ * 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 Attansic Technology Corp. L2 FastEthernet adapter.
+ *
+ * This driver is heavily based on age(4) Attansic L1 driver by Pyun YongHyeon.
+ *
+ * $FreeBSD: src/sys/dev/ae/if_ae.c,v 1.1.2.3.2.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/serialize.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/bpf.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ifq_var.h>
+#include <net/vlan/if_vlan_var.h>
+#include <net/vlan/if_vlan_ether.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcidevs.h>
+
+#include <dev/netif/mii_layer/miivar.h>
+
+#include <dev/netif/ae/if_aereg.h>
+#include <dev/netif/ae/if_aevar.h>
+
+/* "device miibus" required.  See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+/*
+ * Devices supported by this driver.
+ */
+static const struct ae_dev {
+       uint16_t        ae_vendorid;
+       uint16_t        ae_deviceid;
+       const char      *ae_name;
+} ae_devs[] = {
+        { VENDORID_ATTANSIC, DEVICEID_ATTANSIC_L2,
+            "Attansic Technology Corp, L2 Fast Ethernet" },
+       /* Required last entry */
+       { 0, 0, NULL }
+};
+
+
+static int     ae_probe(device_t);
+static int     ae_attach(device_t);
+static int     ae_detach(device_t);
+static int     ae_shutdown(device_t);
+static int     ae_suspend(device_t);
+static int     ae_resume(device_t);
+static int     ae_miibus_readreg(device_t, int, int);
+static int     ae_miibus_writereg(device_t, int, int, int);
+static void    ae_miibus_statchg(device_t);
+
+static int     ae_mediachange(struct ifnet *);
+static void    ae_mediastatus(struct ifnet *, struct ifmediareq *);
+static void    ae_init(void *);
+static void    ae_start(struct ifnet *);
+static int     ae_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
+static void    ae_watchdog(struct ifnet *);
+static void    ae_stop(struct ae_softc *);
+static void    ae_tick(void *);
+
+static void    ae_intr(void *);
+static void    ae_tx_intr(struct ae_softc *);
+static void    ae_rx_intr(struct ae_softc *);
+static int     ae_rxeof(struct ae_softc *, struct ae_rxd *);
+
+static int     ae_encap(struct ae_softc *, struct mbuf **);
+static void    ae_sysctl_node(struct ae_softc *);
+static void    ae_phy_reset(struct ae_softc *);
+static int     ae_reset(struct ae_softc *);
+static void    ae_pcie_init(struct ae_softc *);
+static void    ae_get_eaddr(struct ae_softc *);
+static void    ae_dma_free(struct ae_softc *);
+static int     ae_dma_alloc(struct ae_softc *);
+static void    ae_mac_config(struct ae_softc *);
+static void    ae_stop_rxmac(struct ae_softc *);
+static void    ae_stop_txmac(struct ae_softc *);
+static void    ae_rxfilter(struct ae_softc *);
+static void    ae_rxvlan(struct ae_softc *);
+static void    ae_update_stats_rx(uint16_t, struct ae_stats *);
+static void    ae_update_stats_tx(uint16_t, struct ae_stats *);
+static void    ae_powersave_disable(struct ae_softc *);
+static void    ae_powersave_enable(struct ae_softc *);
+
+static device_method_t ae_methods[] = {
+       /* Device interface. */
+       DEVMETHOD(device_probe,         ae_probe),
+       DEVMETHOD(device_attach,        ae_attach),
+       DEVMETHOD(device_detach,        ae_detach),
+       DEVMETHOD(device_shutdown,      ae_shutdown),
+       DEVMETHOD(device_suspend,       ae_suspend),
+       DEVMETHOD(device_resume,        ae_resume),
+       
+       /* Bus interface. */
+       DEVMETHOD(bus_print_child,      bus_generic_print_child),
+       DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+       
+       /* MII interface. */
+       DEVMETHOD(miibus_readreg,       ae_miibus_readreg),
+       DEVMETHOD(miibus_writereg,      ae_miibus_writereg),
+       DEVMETHOD(miibus_statchg,       ae_miibus_statchg),
+       { NULL, NULL }
+};
+
+static driver_t ae_driver = {
+       "ae",
+       ae_methods,
+       sizeof(struct ae_softc)
+};
+
+static devclass_t ae_devclass;
+DECLARE_DUMMY_MODULE(if_ae);
+MODULE_DEPEND(if_ae, miibus, 1, 1, 1);
+DRIVER_MODULE(if_ae, pci, ae_driver, ae_devclass, 0, 0);
+DRIVER_MODULE(miibus, ae, miibus_driver, miibus_devclass, 0, 0);
+
+/* Register access macros. */
+#define AE_WRITE_4(_sc, reg, val)      \
+       bus_space_write_4((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg), (val))
+#define AE_WRITE_2(_sc, reg, val)      \
+       bus_space_write_2((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg), (val))
+#define AE_WRITE_1(_sc, reg, val)      \
+       bus_space_write_1((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg), (val))
+#define AE_READ_4(_sc, reg)            \
+       bus_space_read_4((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg))
+#define AE_READ_2(_sc, reg)            \
+       bus_space_read_2((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg))
+#define AE_READ_1(_sc, reg)            \
+       bus_space_read_1((_sc)->ae_mem_bt, (_sc)->ae_mem_bh, (reg))
+
+#define AE_PHY_READ(sc, reg)           \
+       ae_miibus_readreg(sc->ae_dev, 0, reg)
+#define AE_PHY_WRITE(sc, reg, val)     \
+       ae_miibus_writereg(sc->ae_dev, 0, reg, val)
+#define AE_CHECK_EADDR_VALID(eaddr)    \
+       ((eaddr[0] == 0 && eaddr[1] == 0) || \
+        (eaddr[0] == 0xffffffff && eaddr[1] == 0xffff))
+#define AE_RXD_VLAN(vtag) \
+       (((vtag) >> 4) | (((vtag) & 0x07) << 13) | (((vtag) & 0x08) << 9))
+#define AE_TXD_VLAN(vtag) \
+       (((vtag) << 4) | (((vtag) >> 13) & 0x07) | (((vtag) >> 9) & 0x08))
+
+/*
+ * ae statistics.
+ */
+#define STATS_ENTRY(node, desc, field) \
+       { node, desc, offsetof(struct ae_stats, field) }
+struct {
+       const char      *node;
+       const char      *desc;
+       intptr_t        offset;
+} ae_stats_tx[] = {
+       STATS_ENTRY("bcast", "broadcast frames", tx_bcast),
+       STATS_ENTRY("mcast", "multicast frames", tx_mcast),
+       STATS_ENTRY("pause", "PAUSE frames", tx_pause),
+       STATS_ENTRY("control", "control frames", tx_ctrl),
+       STATS_ENTRY("defers", "deferrals occuried", tx_defer),
+       STATS_ENTRY("exc_defers", "excessive deferrals occuried", tx_excdefer),
+       STATS_ENTRY("singlecols", "single collisions occuried", tx_singlecol),
+       STATS_ENTRY("multicols", "multiple collisions occuried", tx_multicol),
+       STATS_ENTRY("latecols", "late collisions occuried", tx_latecol),
+       STATS_ENTRY("aborts", "transmit aborts due collisions", tx_abortcol),
+       STATS_ENTRY("underruns", "Tx FIFO underruns", tx_underrun)
+}, ae_stats_rx[] = {
+       STATS_ENTRY("bcast", "broadcast frames", rx_bcast),
+       STATS_ENTRY("mcast", "multicast frames", rx_mcast),
+       STATS_ENTRY("pause", "PAUSE frames", rx_pause),
+       STATS_ENTRY("control", "control frames", rx_ctrl),
+       STATS_ENTRY("crc_errors", "frames with CRC errors", rx_crcerr),
+       STATS_ENTRY("code_errors", "frames with invalid opcode", rx_codeerr),
+       STATS_ENTRY("runt", "runt frames", rx_runt),
+       STATS_ENTRY("frag", "fragmented frames", rx_frag),
+       STATS_ENTRY("align_errors", "frames with alignment errors", rx_align),
+       STATS_ENTRY("truncated", "frames truncated due to Rx FIFO inderrun",
+           rx_trunc)
+};
+#define AE_STATS_RX_LEN (sizeof(ae_stats_rx) / sizeof(*ae_stats_rx))
+#define AE_STATS_TX_LEN (sizeof(ae_stats_tx) / sizeof(*ae_stats_tx))
+
+static void
+ae_stop(struct ae_softc *sc)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       int i;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+       ifp->if_timer = 0;
+
+       sc->ae_flags &= ~AE_FLAG_LINK;
+       callout_stop(&sc->ae_tick_ch);
+
+       /*
+        * Clear and disable interrupts.
+        */
+       AE_WRITE_4(sc, AE_IMR_REG, 0);
+       AE_WRITE_4(sc, AE_ISR_REG, 0xffffffff);
+
+       /*
+        * Stop Rx/Tx MACs.
+        */
+       ae_stop_txmac(sc);
+       ae_stop_rxmac(sc);
+
+       /*
+        * Stop DMA engines.
+        */
+       AE_WRITE_1(sc, AE_DMAREAD_REG, ~AE_DMAREAD_EN);
+       AE_WRITE_1(sc, AE_DMAWRITE_REG, ~AE_DMAWRITE_EN);
+
+       /*
+        * Wait for everything to enter idle state.
+        */
+       for (i = 0; i < AE_IDLE_TIMEOUT; i++) {
+               if (AE_READ_4(sc, AE_IDLE_REG) == 0)
+                       break;
+               DELAY(100);
+       }
+       if (i == AE_IDLE_TIMEOUT)
+               if_printf(ifp, "could not enter idle state in stop.\n");
+}
+
+static void
+ae_stop_rxmac(struct ae_softc *sc)
+{
+       uint32_t val;
+       int i;
+
+       /*
+        * Stop Rx MAC engine.
+        */
+       val = AE_READ_4(sc, AE_MAC_REG);
+       if ((val & AE_MAC_RX_EN) != 0) {
+               val &= ~AE_MAC_RX_EN;
+               AE_WRITE_4(sc, AE_MAC_REG, val);
+       }
+
+       /*
+        * Stop Rx DMA engine.
+        */
+       if (AE_READ_1(sc, AE_DMAWRITE_REG) == AE_DMAWRITE_EN)
+               AE_WRITE_1(sc, AE_DMAWRITE_REG, 0);
+
+       /*
+        * Wait for IDLE state.
+        */
+       for (i = 0; i < AE_IDLE_TIMEOUT; i--) {
+               val = AE_READ_4(sc, AE_IDLE_REG);
+               if ((val & (AE_IDLE_RXMAC | AE_IDLE_DMAWRITE)) == 0)
+                       break;
+               DELAY(100);
+       }
+       if (i == AE_IDLE_TIMEOUT) {
+               if_printf(&sc->arpcom.ac_if,
+                         "timed out while stopping Rx MAC.\n");
+       }
+}
+
+static void
+ae_stop_txmac(struct ae_softc *sc)
+{
+       uint32_t val;
+       int i;
+
+       /*
+        * Stop Tx MAC engine.
+        */
+       val = AE_READ_4(sc, AE_MAC_REG);
+       if ((val & AE_MAC_TX_EN) != 0) {
+               val &= ~AE_MAC_TX_EN;
+               AE_WRITE_4(sc, AE_MAC_REG, val);
+       }
+
+       /*
+        * Stop Tx DMA engine.
+        */
+       if (AE_READ_1(sc, AE_DMAREAD_REG) == AE_DMAREAD_EN)
+               AE_WRITE_1(sc, AE_DMAREAD_REG, 0);
+
+       /*
+        * Wait for IDLE state.
+        */
+       for (i = 0; i < AE_IDLE_TIMEOUT; i--) {
+               val = AE_READ_4(sc, AE_IDLE_REG);
+               if ((val & (AE_IDLE_TXMAC | AE_IDLE_DMAREAD)) == 0)
+                       break;
+               DELAY(100);
+       }
+       if (i == AE_IDLE_TIMEOUT) {
+               if_printf(&sc->arpcom.ac_if,
+                         "timed out while stopping Tx MAC.\n");
+       }
+}
+
+/*
+ * Callback from MII layer when media changes.
+ */
+static void
+ae_miibus_statchg(device_t dev)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct mii_data *mii;
+       uint32_t val;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       if ((ifp->if_flags & IFF_RUNNING) == 0)
+               return;
+
+       mii = device_get_softc(sc->ae_miibus);
+       sc->ae_flags &= ~AE_FLAG_LINK;
+       if ((mii->mii_media_status & IFM_AVALID) != 0) {
+               switch (IFM_SUBTYPE(mii->mii_media_active)) {
+               case IFM_10_T:
+               case IFM_100_TX:
+                       sc->ae_flags |= AE_FLAG_LINK;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /* Stop Rx/Tx MACs. */
+       ae_stop_rxmac(sc);
+       ae_stop_txmac(sc);
+
+       /* Program MACs with resolved speed/duplex/flow-control. */
+       if ((sc->ae_flags & AE_FLAG_LINK) != 0) {
+               ae_mac_config(sc);
+
+               /*
+                * Restart DMA engines.
+                */
+               AE_WRITE_1(sc, AE_DMAREAD_REG, AE_DMAREAD_EN);
+               AE_WRITE_1(sc, AE_DMAWRITE_REG, AE_DMAWRITE_EN);
+
+               /*
+                * Enable Rx and Tx MACs.
+                */
+               val = AE_READ_4(sc, AE_MAC_REG);
+               val |= AE_MAC_TX_EN | AE_MAC_RX_EN;
+               AE_WRITE_4(sc, AE_MAC_REG, val);
+       }
+}
+
+static void
+ae_sysctl_node(struct ae_softc *sc)
+{
+       struct sysctl_ctx_list *ctx;
+       struct sysctl_oid *root, *stats, *stats_rx, *stats_tx;
+       struct ae_stats *ae_stats;
+       unsigned int i;
+
+       ae_stats = &sc->stats;
+       sysctl_ctx_init(&sc->ae_sysctl_ctx);
+       sc->ae_sysctl_tree = SYSCTL_ADD_NODE(&sc->ae_sysctl_ctx,
+                               SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
+                               device_get_nameunit(sc->ae_dev),
+                               CTLFLAG_RD, 0, "");
+       if (sc->ae_sysctl_tree == NULL) {
+               device_printf(sc->ae_dev, "can't add sysctl node\n");
+               return;
+       }
+       ctx = &sc->ae_sysctl_ctx;
+       root = sc->ae_sysctl_tree;
+
+       stats = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "stats",
+           CTLFLAG_RD, NULL, "ae statistics");
+       if (stats == NULL) {
+               device_printf(sc->ae_dev, "can't add stats sysctl node\n");
+               return;
+       }
+
+       /*
+        * Receiver statistcics.
+        */
+       stats_rx = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, "rx",
+           CTLFLAG_RD, NULL, "Rx MAC statistics");
+       if (stats_rx != NULL) {
+               for (i = 0; i < AE_STATS_RX_LEN; i++) {
+                       SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(stats_rx),
+                           OID_AUTO, ae_stats_rx[i].node, CTLFLAG_RD,
+                           (char *)ae_stats + ae_stats_rx[i].offset, 0,
+                           ae_stats_rx[i].desc);
+               }
+       }
+
+       /*
+        * Transmitter statistcics.
+        */
+       stats_tx = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, "tx",
+           CTLFLAG_RD, NULL, "Tx MAC statistics");
+       if (stats_tx != NULL) {
+               for (i = 0; i < AE_STATS_TX_LEN; i++) {
+                       SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(stats_tx),
+                           OID_AUTO, ae_stats_tx[i].node, CTLFLAG_RD,
+                           (char *)ae_stats + ae_stats_tx[i].offset, 0,
+                           ae_stats_tx[i].desc);
+               }
+       }
+}
+
+static int
+ae_miibus_readreg(device_t dev, int phy, int reg)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       uint32_t val;
+       int i;
+
+       /*
+        * Locking is done in upper layers.
+        */
+       if (phy != sc->ae_phyaddr)
+               return (0);
+       val = ((reg << AE_MDIO_REGADDR_SHIFT) & AE_MDIO_REGADDR_MASK) |
+           AE_MDIO_START | AE_MDIO_READ | AE_MDIO_SUP_PREAMBLE |
+           ((AE_MDIO_CLK_25_4 << AE_MDIO_CLK_SHIFT) & AE_MDIO_CLK_MASK);
+       AE_WRITE_4(sc, AE_MDIO_REG, val);
+
+       /*
+        * Wait for operation to complete.
+        */
+       for (i = 0; i < AE_MDIO_TIMEOUT; i++) {
+               DELAY(2);
+               val = AE_READ_4(sc, AE_MDIO_REG);
+               if ((val & (AE_MDIO_START | AE_MDIO_BUSY)) == 0)
+                       break;
+       }
+       if (i == AE_MDIO_TIMEOUT) {
+               device_printf(sc->ae_dev, "phy read timeout: %d.\n", reg);
+               return (0);
+       }
+       return ((val << AE_MDIO_DATA_SHIFT) & AE_MDIO_DATA_MASK);
+}
+
+static int
+ae_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       uint32_t aereg;
+       int i;
+
+       /*
+        * Locking is done in upper layers.
+        */
+       if (phy != sc->ae_phyaddr)
+               return (0);
+       aereg = ((reg << AE_MDIO_REGADDR_SHIFT) & AE_MDIO_REGADDR_MASK) |
+           AE_MDIO_START | AE_MDIO_SUP_PREAMBLE |
+           ((AE_MDIO_CLK_25_4 << AE_MDIO_CLK_SHIFT) & AE_MDIO_CLK_MASK) |
+           ((val << AE_MDIO_DATA_SHIFT) & AE_MDIO_DATA_MASK);
+       AE_WRITE_4(sc, AE_MDIO_REG, aereg);
+
+       /*
+        * Wait for operation to complete.
+        */
+       for (i = 0; i < AE_MDIO_TIMEOUT; i++) {
+               DELAY(2);
+               aereg = AE_READ_4(sc, AE_MDIO_REG);
+               if ((aereg & (AE_MDIO_START | AE_MDIO_BUSY)) == 0)
+                       break;
+       }
+       if (i == AE_MDIO_TIMEOUT)
+               device_printf(sc->ae_dev, "phy write timeout: %d.\n", reg);
+       return (0);
+}
+
+static int
+ae_probe(device_t dev)
+{
+       uint16_t vendor, devid;
+       const struct ae_dev *sp;
+
+       vendor = pci_get_vendor(dev);
+       devid = pci_get_device(dev);
+       for (sp = ae_devs; sp->ae_name != NULL; sp++) {
+               if (vendor == sp->ae_vendorid &&
+                   devid == sp->ae_deviceid) {
+                       device_set_desc(dev, sp->ae_name);
+                       return (0);
+               }
+       }
+       return (ENXIO);
+}
+
+static int
+ae_dma_alloc(struct ae_softc *sc)
+{
+       bus_addr_t busaddr;
+       int error;
+
+       /*
+        * Create parent DMA tag.
+        */
+       error = bus_dma_tag_create(NULL, 1, 0,
+                                  BUS_SPACE_MAXADDR_32BIT,
+                                  BUS_SPACE_MAXADDR,
+                                  NULL, NULL,
+                                  BUS_SPACE_MAXSIZE_32BIT,
+                                  0,
+                                  BUS_SPACE_MAXSIZE_32BIT,
+                                  0, &sc->dma_parent_tag);
+       if (error) {
+               device_printf(sc->ae_dev, "could not creare parent DMA tag.\n");
+               return (error);
+       }
+
+       /*
+        * Create DMA stuffs for TxD.
+        */
+       sc->txd_base = bus_dmamem_coherent_any(sc->dma_parent_tag, 4,
+                       AE_TXD_BUFSIZE_DEFAULT, BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                       &sc->dma_txd_tag, &sc->dma_txd_map,
+                       &sc->dma_txd_busaddr);
+       if (sc->txd_base == NULL) {
+               device_printf(sc->ae_dev, "could not creare TxD DMA stuffs.\n");
+               return ENOMEM;
+       }
+
+       /*
+        * Create DMA stuffs for TxS.
+        */
+       sc->txs_base = bus_dmamem_coherent_any(sc->dma_parent_tag, 4,
+                       AE_TXS_COUNT_DEFAULT * 4, BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                       &sc->dma_txs_tag, &sc->dma_txs_map,
+                       &sc->dma_txs_busaddr);
+       if (sc->txs_base == NULL) {
+               device_printf(sc->ae_dev, "could not creare TxS DMA stuffs.\n");
+               return ENOMEM;
+       }
+
+       /*
+        * Create DMA stuffs for RxD.
+        */
+       sc->rxd_base_dma = bus_dmamem_coherent_any(sc->dma_parent_tag, 128,
+                               AE_RXD_COUNT_DEFAULT * 1536 + 120,
+                               BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                               &sc->dma_rxd_tag, &sc->dma_rxd_map,
+                               &busaddr);
+       if (sc->rxd_base_dma == NULL) {
+               device_printf(sc->ae_dev, "could not creare RxD DMA stuffs.\n");
+               return ENOMEM;
+       }
+       sc->dma_rxd_busaddr = busaddr + 120;
+       sc->rxd_base = (struct ae_rxd *)(sc->rxd_base_dma + 120);
+
+       return (0);
+}
+
+static void
+ae_mac_config(struct ae_softc *sc)
+{
+       struct mii_data *mii;
+       uint32_t val;
+
+       mii = device_get_softc(sc->ae_miibus);
+       val = AE_READ_4(sc, AE_MAC_REG);
+       val &= ~AE_MAC_FULL_DUPLEX;
+       /* XXX disable AE_MAC_TX_FLOW_EN? */
+       if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+               val |= AE_MAC_FULL_DUPLEX;
+       AE_WRITE_4(sc, AE_MAC_REG, val);
+}
+
+static int
+ae_rxeof(struct ae_softc *sc, struct ae_rxd *rxd)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct mbuf *m;
+       unsigned int size;
+       uint16_t flags;
+
+       flags = le16toh(rxd->flags);
+#ifdef AE_DEBUG
+       if_printf(ifp, "Rx interrupt occuried.\n");
+#endif
+       size = le16toh(rxd->len) - ETHER_CRC_LEN;
+       if (size < (ETHER_MIN_LEN - ETHER_CRC_LEN -
+                   sizeof(struct ether_vlan_header))) {
+               if_printf(ifp, "Runt frame received.");
+               return (EIO);
+       }
+
+       m = m_devget(&rxd->data[0], size, ETHER_ALIGN, ifp, NULL);
+       if (m == NULL)
+               return (ENOBUFS);
+
+       if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) &&
+           (flags & AE_RXD_HAS_VLAN)) {
+               m->m_pkthdr.ether_vlantag = AE_RXD_VLAN(le16toh(rxd->vlan));
+               m->m_flags |= M_VLANTAG;
+       }
+       ifp->if_input(ifp, m);
+
+       return (0);
+}
+
+static void
+ae_rx_intr(struct ae_softc *sc)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct ae_rxd *rxd;
+       uint16_t flags;
+       int error;
+
+       /*
+        * Syncronize DMA buffers.
+        */
+       bus_dmamap_sync(sc->dma_rxd_tag, sc->dma_rxd_map,
+                       BUS_DMASYNC_POSTREAD);
+       for (;;) {
+               rxd = (struct ae_rxd *)(sc->rxd_base + sc->rxd_cur);
+
+               flags = le16toh(rxd->flags);
+               if ((flags & AE_RXD_UPDATE) == 0)
+                       break;
+               rxd->flags = htole16(flags & ~AE_RXD_UPDATE);
+
+               /* Update stats. */
+               ae_update_stats_rx(flags, &sc->stats);
+
+               /*
+                * Update position index.
+                */
+               sc->rxd_cur = (sc->rxd_cur + 1) % AE_RXD_COUNT_DEFAULT;
+               if ((flags & AE_RXD_SUCCESS) == 0) {
+                       ifp->if_ierrors++;
+                       continue;
+               }
+
+               error = ae_rxeof(sc, rxd);
+               if (error)
+                       ifp->if_ierrors++;
+               else
+                       ifp->if_ipackets++;
+       }
+
+       /* Update Rx index. */
+       AE_WRITE_2(sc, AE_MB_RXD_IDX_REG, sc->rxd_cur);
+}
+
+static void
+ae_tx_intr(struct ae_softc *sc)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct ae_txd *txd;
+       struct ae_txs *txs;
+       uint16_t flags;
+
+       /*
+        * Syncronize DMA buffers.
+        */
+       bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_POSTREAD);
+       bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_POSTREAD);
+
+       for (;;) {
+               txs = sc->txs_base + sc->txs_ack;
+
+               flags = le16toh(txs->flags);
+               if ((flags & AE_TXS_UPDATE) == 0)
+                       break;
+               txs->flags = htole16(flags & ~AE_TXS_UPDATE);
+
+               /* Update stats. */
+               ae_update_stats_tx(flags, &sc->stats);
+
+               /*
+                * Update TxS position.
+                */
+               sc->txs_ack = (sc->txs_ack + 1) % AE_TXS_COUNT_DEFAULT;
+               sc->ae_flags |= AE_FLAG_TXAVAIL;
+               txd = (struct ae_txd *)(sc->txd_base + sc->txd_ack);
+               if (txs->len != txd->len) {
+                       device_printf(sc->ae_dev, "Size mismatch: "
+                               "TxS:%d TxD:%d\n",
+                               le16toh(txs->len), le16toh(txd->len));
+               }
+
+               /*
+                * Move txd ack and align on 4-byte boundary.
+                */
+               sc->txd_ack = ((sc->txd_ack + le16toh(txd->len) + 4 + 3) & ~3) %
+                   AE_TXD_BUFSIZE_DEFAULT;
+               if ((flags & AE_TXS_SUCCESS) != 0)
+                       ifp->if_opackets++;
+               else
+                       ifp->if_oerrors++;
+               sc->tx_inproc--;
+       }
+
+       if (sc->tx_inproc < 0) {
+               /* XXX assert? */
+               if_printf(ifp, "Received stray Tx interrupt(s).\n");
+               sc->tx_inproc = 0;
+       }
+       if (sc->tx_inproc == 0)
+               ifp->if_timer = 0;      /* Unarm watchdog. */
+       if (sc->ae_flags & AE_FLAG_TXAVAIL) {
+               ifp->if_flags &= ~IFF_OACTIVE;
+               if (!ifq_is_empty(&ifp->if_snd))
+#ifdef foo
+                       ae_intr(sc);
+#else
+                       if_devstart(ifp);
+#endif
+       }
+
+       /*
+        * Syncronize DMA buffers.
+        */
+       bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_PREWRITE);
+}
+
+static void
+ae_intr(void *xsc)
+{
+       struct ae_softc *sc = xsc;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       uint32_t val;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       val = AE_READ_4(sc, AE_ISR_REG);
+       if (val == 0 || (val & AE_IMR_DEFAULT) == 0)
+               return;
+
+#ifdef foo
+       AE_WRITE_4(sc, AE_ISR_REG, AE_ISR_DISABLE);
+#endif
+
+       /* Read interrupt status. */
+       val = AE_READ_4(sc, AE_ISR_REG);
+
+       /* Clear interrupts and disable them. */
+       AE_WRITE_4(sc, AE_ISR_REG, val | AE_ISR_DISABLE);
+
+       if (ifp->if_flags & IFF_RUNNING) {
+               if (val & (AE_ISR_DMAR_TIMEOUT |
+                          AE_ISR_DMAW_TIMEOUT |
+                          AE_ISR_PHY_LINKDOWN)) {
+                       ae_init(sc);
+               }
+               if (val & AE_ISR_TX_EVENT)
+                       ae_tx_intr(sc);
+               if (val & AE_ISR_RX_EVENT)
+                       ae_rx_intr(sc);
+       }
+
+       /* Re-enable interrupts. */
+       AE_WRITE_4(sc, AE_ISR_REG, 0);
+}
+
+static void
+ae_init(void *xsc)
+{
+       struct ae_softc *sc = xsc;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct mii_data *mii;
+       uint8_t eaddr[ETHER_ADDR_LEN];
+       uint32_t val;
+       bus_addr_t addr;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       mii = device_get_softc(sc->ae_miibus);
+       ae_stop(sc);
+       ae_reset(sc);
+       ae_pcie_init(sc);
+       ae_powersave_disable(sc);
+
+       /*
+        * Clear and disable interrupts.
+        */
+       AE_WRITE_4(sc, AE_ISR_REG, 0xffffffff);
+
+       /*
+        * Set the MAC address.
+        */
+       bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN);
+       val = eaddr[2] << 24 | eaddr[3] << 16 | eaddr[4] << 8 | eaddr[5];
+       AE_WRITE_4(sc, AE_EADDR0_REG, val);
+       val = eaddr[0] << 8 | eaddr[1];
+       AE_WRITE_4(sc, AE_EADDR1_REG, val);
+
+       /*
+        * Set ring buffers base addresses.
+        */
+       addr = sc->dma_rxd_busaddr;
+       AE_WRITE_4(sc, AE_DESC_ADDR_HI_REG, BUS_ADDR_HI(addr));
+       AE_WRITE_4(sc, AE_RXD_ADDR_LO_REG, BUS_ADDR_LO(addr));
+       addr = sc->dma_txd_busaddr;
+       AE_WRITE_4(sc, AE_TXD_ADDR_LO_REG, BUS_ADDR_LO(addr));
+       addr = sc->dma_txs_busaddr;
+       AE_WRITE_4(sc, AE_TXS_ADDR_LO_REG, BUS_ADDR_LO(addr));
+
+       /*
+        * Configure ring buffers sizes.
+        */
+       AE_WRITE_2(sc, AE_RXD_COUNT_REG, AE_RXD_COUNT_DEFAULT);
+       AE_WRITE_2(sc, AE_TXD_BUFSIZE_REG, AE_TXD_BUFSIZE_DEFAULT / 4);
+       AE_WRITE_2(sc, AE_TXS_COUNT_REG, AE_TXS_COUNT_DEFAULT);
+
+       /*
+        * Configure interframe gap parameters.
+        */
+       val = ((AE_IFG_TXIPG_DEFAULT << AE_IFG_TXIPG_SHIFT) &
+           AE_IFG_TXIPG_MASK) |
+           ((AE_IFG_RXIPG_DEFAULT << AE_IFG_RXIPG_SHIFT) &
+           AE_IFG_RXIPG_MASK) |
+           ((AE_IFG_IPGR1_DEFAULT << AE_IFG_IPGR1_SHIFT) &
+           AE_IFG_IPGR1_MASK) |
+           ((AE_IFG_IPGR2_DEFAULT << AE_IFG_IPGR2_SHIFT) &
+           AE_IFG_IPGR2_MASK);
+       AE_WRITE_4(sc, AE_IFG_REG, val);
+
+       /*
+        * Configure half-duplex operation.
+        */
+       val = ((AE_HDPX_LCOL_DEFAULT << AE_HDPX_LCOL_SHIFT) &
+           AE_HDPX_LCOL_MASK) |
+           ((AE_HDPX_RETRY_DEFAULT << AE_HDPX_RETRY_SHIFT) &
+           AE_HDPX_RETRY_MASK) |
+           ((AE_HDPX_ABEBT_DEFAULT << AE_HDPX_ABEBT_SHIFT) &
+           AE_HDPX_ABEBT_MASK) |
+           ((AE_HDPX_JAMIPG_DEFAULT << AE_HDPX_JAMIPG_SHIFT) &
+           AE_HDPX_JAMIPG_MASK) | AE_HDPX_EXC_EN;
+       AE_WRITE_4(sc, AE_HDPX_REG, val);
+
+       /*
+        * Configure interrupt moderate timer.
+        */
+       AE_WRITE_2(sc, AE_IMT_REG, AE_IMT_DEFAULT);
+       val = AE_READ_4(sc, AE_MASTER_REG);
+       val |= AE_MASTER_IMT_EN;
+       AE_WRITE_4(sc, AE_MASTER_REG, val);
+
+       /*
+        * Configure interrupt clearing timer.
+        */
+       AE_WRITE_2(sc, AE_ICT_REG, AE_ICT_DEFAULT);
+
+       /*
+        * Configure MTU.
+        */
+       val = ifp->if_mtu + ETHER_HDR_LEN + sizeof(struct ether_vlan_header) +
+           ETHER_CRC_LEN;
+       AE_WRITE_2(sc, AE_MTU_REG, val);
+
+       /*
+        * Configure cut-through threshold.
+        */
+       AE_WRITE_4(sc, AE_CUT_THRESH_REG, AE_CUT_THRESH_DEFAULT);
+
+       /*
+        * Configure flow control.
+        */
+       AE_WRITE_2(sc, AE_FLOW_THRESH_HI_REG, (AE_RXD_COUNT_DEFAULT / 8) * 7);
+       AE_WRITE_2(sc, AE_FLOW_THRESH_LO_REG, (AE_RXD_COUNT_MIN / 8) >
+           (AE_RXD_COUNT_DEFAULT / 12) ? (AE_RXD_COUNT_MIN / 8) :
+           (AE_RXD_COUNT_DEFAULT / 12));
+
+       /*
+        * Init mailboxes.
+        */
+       sc->txd_cur = sc->rxd_cur = 0;
+       sc->txd_cur = sc->rxd_cur = 0;
+       sc->txs_ack = sc->txd_ack = 0;
+       sc->rxd_cur = 0;
+       AE_WRITE_2(sc, AE_MB_TXD_IDX_REG, sc->txd_cur);
+       AE_WRITE_2(sc, AE_MB_RXD_IDX_REG, sc->rxd_cur);
+       sc->tx_inproc = 0;
+       sc->ae_flags |= AE_FLAG_TXAVAIL; /* Free Tx's available. */
+
+       /*
+        * Enable DMA.
+        */
+       AE_WRITE_1(sc, AE_DMAREAD_REG, AE_DMAREAD_EN);
+       AE_WRITE_1(sc, AE_DMAWRITE_REG, AE_DMAWRITE_EN);
+
+       /*
+        * Check if everything is OK.
+        */
+       val = AE_READ_4(sc, AE_ISR_REG);
+       if ((val & AE_ISR_PHY_LINKDOWN) != 0) {
+               device_printf(sc->ae_dev, "Initialization failed.\n");
+               return;
+       }
+
+       /*
+        * Clear interrupt status.
+        */
+       AE_WRITE_4(sc, AE_ISR_REG, 0x3fffffff);
+       AE_WRITE_4(sc, AE_ISR_REG, 0x0);
+
+       /*
+        * Enable interrupts.
+        */
+       val = AE_READ_4(sc, AE_MASTER_REG);
+       AE_WRITE_4(sc, AE_MASTER_REG, val | AE_MASTER_MANUAL_INT);
+       AE_WRITE_4(sc, AE_IMR_REG, AE_IMR_DEFAULT);
+
+       /*
+        * Disable WOL.
+        */
+       AE_WRITE_4(sc, AE_WOL_REG, 0);
+
+       /*
+        * Configure MAC.
+        */
+       val = AE_MAC_TX_CRC_EN | AE_MAC_TX_AUTOPAD |
+           AE_MAC_FULL_DUPLEX | AE_MAC_CLK_PHY |
+           AE_MAC_TX_FLOW_EN | AE_MAC_RX_FLOW_EN |
+           ((AE_HALFBUF_DEFAULT << AE_HALFBUF_SHIFT) & AE_HALFBUF_MASK) |
+           ((AE_MAC_PREAMBLE_DEFAULT << AE_MAC_PREAMBLE_SHIFT) &
+           AE_MAC_PREAMBLE_MASK);
+       AE_WRITE_4(sc, AE_MAC_REG, val);
+
+       /*
+        * Configure Rx MAC.
+        */
+       ae_rxfilter(sc);
+       ae_rxvlan(sc);
+
+       /*
+        * Enable Tx/Rx.
+        */
+       val = AE_READ_4(sc, AE_MAC_REG);
+       AE_WRITE_4(sc, AE_MAC_REG, val | AE_MAC_TX_EN | AE_MAC_RX_EN);
+
+       sc->ae_flags &= ~AE_FLAG_LINK;
+       mii_mediachg(mii);      /* Switch to the current media. */
+
+       callout_reset(&sc->ae_tick_ch, hz, ae_tick, sc);
+       ifp->if_flags |= IFF_RUNNING;
+       ifp->if_flags &= ~IFF_OACTIVE;
+}
+
+static void
+ae_watchdog(struct ifnet *ifp)
+{
+       struct ae_softc *sc = ifp->if_softc;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       if ((sc->ae_flags & AE_FLAG_LINK) == 0)
+               if_printf(ifp, "watchdog timeout (missed link).\n");
+       else
+               if_printf(ifp, "watchdog timeout - resetting.\n");
+       ifp->if_oerrors++;
+
+       ae_init(sc);
+       if (!ifq_is_empty(&ifp->if_snd))
+               if_devstart(ifp);
+}
+
+static void
+ae_tick(void *xsc)
+{
+       struct ae_softc *sc = xsc;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct mii_data *mii = device_get_softc(sc->ae_miibus);
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       mii_tick(mii);
+       callout_reset(&sc->ae_tick_ch, hz, ae_tick, sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static void
+ae_rxvlan(struct ae_softc *sc)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       uint32_t val;
+
+       val = AE_READ_4(sc, AE_MAC_REG);
+       val &= ~AE_MAC_RMVLAN_EN;
+       if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING)
+               val |= AE_MAC_RMVLAN_EN;
+       AE_WRITE_4(sc, AE_MAC_REG, val);
+}
+
+static void
+ae_rxfilter(struct ae_softc *sc)
+{
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct ifmultiaddr *ifma;
+       uint32_t crc;
+       uint32_t mchash[2];
+       uint32_t rxcfg;
+
+       rxcfg = AE_READ_4(sc, AE_MAC_REG);
+       rxcfg &= ~(AE_MAC_MCAST_EN | AE_MAC_BCAST_EN | AE_MAC_PROMISC_EN);
+       rxcfg |= AE_MAC_BCAST_EN;
+       if (ifp->if_flags & IFF_PROMISC)
+               rxcfg |= AE_MAC_PROMISC_EN;
+       if (ifp->if_flags & IFF_ALLMULTI)
+               rxcfg |= AE_MAC_MCAST_EN;
+
+       /*
+        * Wipe old settings.
+        */
+       AE_WRITE_4(sc, AE_REG_MHT0, 0);
+       AE_WRITE_4(sc, AE_REG_MHT1, 0);
+       if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+               AE_WRITE_4(sc, AE_REG_MHT0, 0xffffffff);
+               AE_WRITE_4(sc, AE_REG_MHT1, 0xffffffff);
+               AE_WRITE_4(sc, AE_MAC_REG, rxcfg);
+               return;
+       }
+
+       /*
+        * Load multicast tables.
+        */
+       bzero(mchash, sizeof(mchash));
+       LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+               if (ifma->ifma_addr->sa_family != AF_LINK)
+                       continue;
+               crc = ether_crc32_le(LLADDR((struct sockaddr_dl *)
+                       ifma->ifma_addr), ETHER_ADDR_LEN);
+               mchash[crc >> 31] |= 1 << ((crc >> 26) & 0x1f);
+       }
+       AE_WRITE_4(sc, AE_REG_MHT0, mchash[0]);
+       AE_WRITE_4(sc, AE_REG_MHT1, mchash[1]);
+       AE_WRITE_4(sc, AE_MAC_REG, rxcfg);
+}
+
+static unsigned int
+ae_tx_avail_size(struct ae_softc *sc)
+{
+       unsigned int avail;
+
+       if (sc->txd_cur >= sc->txd_ack)
+               avail = AE_TXD_BUFSIZE_DEFAULT - (sc->txd_cur - sc->txd_ack);
+       else
+               avail = sc->txd_ack - sc->txd_cur;
+       return (avail - 4);     /* 4-byte header. */
+}
+
+static int
+ae_encap(struct ae_softc *sc, struct mbuf **m_head)
+{
+       struct mbuf *m0;
+       struct ae_txd *hdr;
+       unsigned int to_end;
+       uint16_t len;
+
+       M_ASSERTPKTHDR((*m_head));
+       m0 = *m_head;
+       len = m0->m_pkthdr.len;
+       if ((sc->ae_flags & AE_FLAG_TXAVAIL) == 0 ||
+           ae_tx_avail_size(sc) < len) {
+#ifdef AE_DEBUG
+               if_printf(sc->ifp, "No free Tx available.\n");
+#endif
+               return ENOBUFS;
+       }
+
+       hdr = (struct ae_txd *)(sc->txd_base + sc->txd_cur);
+       bzero(hdr, sizeof(*hdr));
+
+       /* Header size. */
+       sc->txd_cur = (sc->txd_cur + 4) % AE_TXD_BUFSIZE_DEFAULT;
+
+       /* Space available to the end of the ring */
+       to_end = AE_TXD_BUFSIZE_DEFAULT - sc->txd_cur;
+
+       if (to_end >= len) {
+               m_copydata(m0, 0, len, (caddr_t)(sc->txd_base + sc->txd_cur));
+       } else {
+               m_copydata(m0, 0, to_end, (caddr_t)(sc->txd_base +
+                   sc->txd_cur));
+               m_copydata(m0, to_end, len - to_end, (caddr_t)sc->txd_base);
+       }
+
+       /*
+        * Set TxD flags and parameters.
+        */
+       if ((m0->m_flags & M_VLANTAG) != 0) {
+               hdr->vlan = htole16(AE_TXD_VLAN(m0->m_pkthdr.ether_vlantag));
+               hdr->len = htole16(len | AE_TXD_INSERT_VTAG);
+       } else {
+               hdr->len = htole16(len);
+       }
+
+       /*
+        * Set current TxD position and round up to a 4-byte boundary.
+        */
+       sc->txd_cur = ((sc->txd_cur + len + 3) & ~3) % AE_TXD_BUFSIZE_DEFAULT;
+       if (sc->txd_cur == sc->txd_ack)
+               sc->ae_flags &= ~AE_FLAG_TXAVAIL;
+#ifdef AE_DEBUG
+       if_printf(sc->ifp, "New txd_cur = %d.\n", sc->txd_cur);
+#endif
+
+       /*
+        * Update TxS position and check if there are empty TxS available.
+        */
+       sc->txs_base[sc->txs_cur].flags &= ~htole16(AE_TXS_UPDATE);
+       sc->txs_cur = (sc->txs_cur + 1) % AE_TXS_COUNT_DEFAULT;
+       if (sc->txs_cur == sc->txs_ack)
+               sc->ae_flags &= ~AE_FLAG_TXAVAIL;
+
+       /*
+        * Synchronize DMA memory.
+        */
+       bus_dmamap_sync(sc->dma_txd_tag, sc->dma_txd_map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->dma_txs_tag, sc->dma_txs_map, BUS_DMASYNC_PREWRITE);
+
+       return (0);
+}
+
+static void
+ae_start(struct ifnet *ifp)
+{
+       struct ae_softc *sc = ifp->if_softc;
+       int error, trans;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+#ifdef AE_DEBUG
+       if_printf(ifp, "Start called.\n");
+#endif
+       if ((sc->ae_flags & AE_FLAG_LINK) == 0) {
+               ifq_purge(&ifp->if_snd);
+               return;
+       }
+       if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+               return;
+
+       trans = 0;
+       while (!ifq_is_empty(&ifp->if_snd)) {
+               struct mbuf *m0;
+
+               m0 = ifq_dequeue(&ifp->if_snd, NULL);
+               if (m0 == NULL)
+                       break;  /* Nothing to do. */
+
+               error = ae_encap(sc, &m0);
+               if (error != 0) {
+                       if (m0 != NULL) {
+                               ifq_prepend(&ifp->if_snd, m0);
+                               ifp->if_flags |= IFF_OACTIVE;
+#ifdef AE_DEBUG
+                               if_printf(ifp, "Setting OACTIVE.\n");
+#endif
+                       }
+                       break;
+               }
+               trans = 1;
+               sc->tx_inproc++;
+
+               /* Bounce a copy of the frame to BPF. */
+               ETHER_BPF_MTAP(ifp, m0);
+               m_freem(m0);
+       }
+       if (trans) {    /* Something was dequeued. */
+               AE_WRITE_2(sc, AE_MB_TXD_IDX_REG, sc->txd_cur / 4);
+               ifp->if_timer = AE_TX_TIMEOUT; /* Load watchdog. */
+#ifdef AE_DEBUG
+               if_printf(ifp, "%d packets dequeued.\n", count);
+               if_printf(ifp, "Tx pos now is %d.\n", sc->txd_cur);
+#endif
+       }
+}
+
+static int
+ae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+{
+        struct ae_softc *sc = ifp->if_softc;
+        struct ifreq *ifr;
+        struct mii_data *mii;
+        int error = 0, mask;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       ifr = (struct ifreq *)data;
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if (ifp->if_flags & IFF_RUNNING) {
+                               if (((ifp->if_flags ^ sc->ae_if_flags)
+                                   & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
+                                       ae_rxfilter(sc);
+                       } else {
+                               ae_init(sc);
+                       }
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               ae_stop(sc);
+               }
+               sc->ae_if_flags = ifp->if_flags;
+               break;
+
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               if (ifp->if_flags & IFF_RUNNING)
+                       ae_rxfilter(sc);
+               break;
+
+       case SIOCSIFMEDIA:
+       case SIOCGIFMEDIA:
+               mii = device_get_softc(sc->ae_miibus);
+               error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+               break;
+
+       case SIOCSIFCAP:
+               mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+               if (mask & IFCAP_VLAN_HWTAGGING) {
+                       ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
+                       ae_rxvlan(sc);
+               }
+               break;
+
+       default:
+               error = ether_ioctl(ifp, cmd, data);
+               break;
+       }
+       return (error);
+}
+
+static int
+ae_attach(device_t dev)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       int error = 0;
+
+       sc->ae_dev = dev;
+       if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+       callout_init(&sc->ae_tick_ch);
+
+       /* Enable bus mastering */
+       pci_enable_busmaster(dev);
+
+       /*
+        * Allocate memory mapped IO
+        */
+       sc->ae_mem_rid = PCIR_BAR(0);
+       sc->ae_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+                                               &sc->ae_mem_rid, RF_ACTIVE);
+       if (sc->ae_mem_res == NULL) {
+               device_printf(dev, "can't allocate IO memory\n");
+               return ENXIO;
+       }
+       sc->ae_mem_bt = rman_get_bustag(sc->ae_mem_res);
+       sc->ae_mem_bh = rman_get_bushandle(sc->ae_mem_res);
+
+       /*
+        * Allocate IRQ
+        */
+       sc->ae_irq_rid = 0;
+       sc->ae_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+                                               &sc->ae_irq_rid,
+                                               RF_SHAREABLE | RF_ACTIVE);
+       if (sc->ae_irq_res == NULL) {
+               device_printf(dev, "can't allocate irq\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       /* Set PHY address. */
+       sc->ae_phyaddr = AE_PHYADDR_DEFAULT;
+
+       /* Create sysctl tree */
+       ae_sysctl_node(sc);
+
+       /* Reset PHY. */
+       ae_phy_reset(sc);
+
+       /*
+        * Reset the ethernet controller.
+        */
+       ae_reset(sc);
+       ae_pcie_init(sc);
+
+       /*
+        * Get PCI and chip id/revision.
+        */
+       sc->ae_rev = pci_get_revid(dev);
+       sc->ae_chip_rev =
+       (AE_READ_4(sc, AE_MASTER_REG) >> AE_MASTER_REVNUM_SHIFT) &
+       AE_MASTER_REVNUM_MASK;
+       if (bootverbose) {
+               device_printf(dev, "PCI device revision : 0x%04x\n", sc->ae_rev);
+               device_printf(dev, "Chip id/revision : 0x%04x\n",
+                   sc->ae_chip_rev);
+       }
+
+       /*
+        * XXX
+        * Unintialized hardware returns an invalid chip id/revision
+        * as well as 0xFFFFFFFF for Tx/Rx fifo length. It seems that
+        * unplugged cable results in putting hardware into automatic
+        * power down mode which in turn returns invalld chip revision.
+        */
+       if (sc->ae_chip_rev == 0xFFFF) {
+               device_printf(dev,"invalid chip revision : 0x%04x -- "
+                   "not initialized?\n", sc->ae_chip_rev);
+               error = ENXIO;
+               goto fail;
+       }
+#if 0
+       /* Get DMA parameters from PCIe device control register. */
+       pcie_ptr = pci_get_pciecap_ptr(dev);
+       if (pcie_ptr) {
+               uint16_t devctl;
+               sc->ae_flags |= AE_FLAG_PCIE;
+               devctl = pci_read_config(dev, pcie_ptr + PCIER_DEVCTRL, 2);
+               /* Max read request size. */
+               sc->ae_dma_rd_burst = ((devctl >> 12) & 0x07) <<
+                   DMA_CFG_RD_BURST_SHIFT;
+               /* Max payload size. */
+               sc->ae_dma_wr_burst = ((devctl >> 5) & 0x07) <<
+                   DMA_CFG_WR_BURST_SHIFT;
+               if (bootverbose) {
+                       device_printf(dev, "Read request size : %d bytes.\n",
+                           128 << ((devctl >> 12) & 0x07));
+                       device_printf(dev, "TLP payload size : %d bytes.\n",
+                           128 << ((devctl >> 5) & 0x07));
+               }
+       } else {
+               sc->ae_dma_rd_burst = DMA_CFG_RD_BURST_128;
+               sc->ae_dma_wr_burst = DMA_CFG_WR_BURST_128;
+       }
+#endif
+
+       /* Create DMA stuffs */
+       error = ae_dma_alloc(sc);
+       if (error)
+               goto fail;
+
+       /* Load station address. */
+       ae_get_eaddr(sc);
+
+       ifp->if_softc = sc;
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_ioctl = ae_ioctl;
+       ifp->if_start = ae_start;
+       ifp->if_init = ae_init;
+       ifp->if_watchdog = ae_watchdog;
+       ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN - 1);
+       ifq_set_ready(&ifp->if_snd);
+       ifp->if_capabilities = IFCAP_VLAN_MTU |
+                              IFCAP_VLAN_HWTAGGING;
+       ifp->if_hwassist = 0;
+       ifp->if_capenable = ifp->if_capabilities;
+
+       /* Set up MII bus. */
+       error = mii_phy_probe(dev, &sc->ae_miibus,
+                             ae_mediachange, ae_mediastatus);
+       if (error) {
+               device_printf(dev, "no PHY found!\n");
+               goto fail;
+       }
+       ether_ifattach(ifp, sc->ae_eaddr, NULL);
+
+       /* Tell the upper layer(s) we support long frames. */
+       ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+
+       error = bus_setup_intr(dev, sc->ae_irq_res, INTR_MPSAFE, ae_intr, sc,
+                              &sc->ae_irq_handle, ifp->if_serializer);
+       if (error) {
+               device_printf(dev, "could not set up interrupt handler.\n");
+               ether_ifdetach(ifp);
+               goto fail;
+       }
+       ifp->if_cpuid = ithread_cpuid(rman_get_start(sc->ae_irq_res));
+       KKASSERT(ifp->if_cpuid >= 0 && ifp->if_cpuid < ncpus);
+       return 0;
+fail:
+       ae_detach(dev);
+       return (error);
+}
+
+static int
+ae_detach(device_t dev)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+
+       if (device_is_attached(dev)) {
+               struct ifnet *ifp = &sc->arpcom.ac_if;
+
+               lwkt_serialize_enter(ifp->if_serializer);
+               sc->ae_flags |= AE_FLAG_DETACH;
+               ae_stop(sc);
+               bus_teardown_intr(dev, sc->ae_irq_res, sc->ae_irq_handle);
+               lwkt_serialize_exit(ifp->if_serializer);
+
+               ether_ifdetach(ifp);
+       }
+
+       if (sc->ae_miibus != NULL)
+               device_delete_child(dev, sc->ae_miibus);
+       bus_generic_detach(dev);
+
+       if (sc->ae_irq_res != NULL) {
+               bus_release_resource(dev, SYS_RES_IRQ, sc->ae_irq_rid,
+                                    sc->ae_irq_res);
+       }
+       if (sc->ae_mem_res != NULL) {
+               bus_release_resource(dev, SYS_RES_MEMORY, sc->ae_mem_rid,
+                                    sc->ae_mem_res);
+       }
+
+       if (sc->ae_sysctl_tree != NULL)
+               sysctl_ctx_free(&sc->ae_sysctl_ctx);
+
+       ae_dma_free(sc);
+
+       return (0);
+}
+
+static void
+ae_dma_free(struct ae_softc *sc)
+{
+       if (sc->dma_txd_tag != NULL) {
+               bus_dmamap_unload(sc->dma_txd_tag, sc->dma_txd_map);
+               bus_dmamem_free(sc->dma_txd_tag, sc->txd_base,
+                   sc->dma_txd_map);
+               bus_dma_tag_destroy(sc->dma_txd_tag);
+       }
+       if (sc->dma_txs_tag != NULL) {
+               bus_dmamap_unload(sc->dma_txs_tag, sc->dma_txs_map);
+               bus_dmamem_free(sc->dma_txs_tag, sc->txs_base,
+                   sc->dma_txs_map);
+               bus_dma_tag_destroy(sc->dma_txs_tag);
+       }
+       if (sc->dma_rxd_tag != NULL) {
+               bus_dmamap_unload(sc->dma_rxd_tag, sc->dma_rxd_map);
+               bus_dmamem_free(sc->dma_rxd_tag,
+                   sc->rxd_base_dma, sc->dma_rxd_map);
+               bus_dma_tag_destroy(sc->dma_rxd_tag);
+       }
+       if (sc->dma_parent_tag != NULL)
+               bus_dma_tag_destroy(sc->dma_parent_tag);
+}
+
+static void
+ae_pcie_init(struct ae_softc *sc)
+{
+       AE_WRITE_4(sc, AE_PCIE_LTSSM_TESTMODE_REG,
+                  AE_PCIE_LTSSM_TESTMODE_DEFAULT);
+       AE_WRITE_4(sc, AE_PCIE_DLL_TX_CTRL_REG,
+                  AE_PCIE_DLL_TX_CTRL_DEFAULT);
+}
+
+static void
+ae_phy_reset(struct ae_softc *sc)
+{
+       AE_WRITE_4(sc, AE_PHY_ENABLE_REG, AE_PHY_ENABLE);
+       DELAY(1000);    /* XXX: pause(9) ? */
+}
+
+static int
+ae_reset(struct ae_softc *sc)
+{
+       int i;
+
+       /*
+        * Issue a soft reset.
+        */
+       AE_WRITE_4(sc, AE_MASTER_REG, AE_MASTER_SOFT_RESET);
+       bus_space_barrier(sc->ae_mem_bt, sc->ae_mem_bh, AE_MASTER_REG, 4,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+
+       /*
+        * Wait for reset to complete.
+        */
+       for (i = 0; i < AE_RESET_TIMEOUT; i++) {
+               if ((AE_READ_4(sc, AE_MASTER_REG) & AE_MASTER_SOFT_RESET) == 0)
+                       break;
+               DELAY(10);
+       }
+       if (i == AE_RESET_TIMEOUT) {
+               device_printf(sc->ae_dev, "reset timeout.\n");
+               return (ENXIO);
+       }
+
+       /*
+        * Wait for everything to enter idle state.
+        */
+       for (i = 0; i < AE_IDLE_TIMEOUT; i++) {
+               if (AE_READ_4(sc, AE_IDLE_REG) == 0)
+                       break;
+               DELAY(100);
+       }
+       if (i == AE_IDLE_TIMEOUT) {
+               device_printf(sc->ae_dev, "could not enter idle state.\n");
+               return (ENXIO);
+       }
+       return (0);
+}
+
+static int
+ae_check_eeprom_present(struct ae_softc *sc, int *vpdc)
+{
+       int error;
+       uint32_t val;
+
+       /*
+        * Not sure why, but Linux does this.
+        */
+       val = AE_READ_4(sc, AE_SPICTL_REG);
+       if ((val & AE_SPICTL_VPD_EN) != 0) {
+               val &= ~AE_SPICTL_VPD_EN;
+               AE_WRITE_4(sc, AE_SPICTL_REG, val);
+       }
+       error = pci_find_extcap(sc->ae_dev, PCIY_VPD, vpdc);
+       return (error);
+}
+
+static int
+ae_vpd_read_word(struct ae_softc *sc, int reg, uint32_t *word)
+{
+       uint32_t val;
+       int i;
+
+       AE_WRITE_4(sc, AE_VPD_DATA_REG, 0);     /* Clear register value. */
+
+       /*
+        * VPD registers start at offset 0x100. Read them.
+        */
+       val = 0x100 + reg * 4;
+       AE_WRITE_4(sc, AE_VPD_CAP_REG, (val << AE_VPD_CAP_ADDR_SHIFT) &
+           AE_VPD_CAP_ADDR_MASK);
+       for (i = 0; i < AE_VPD_TIMEOUT; i++) {
+               DELAY(2000);
+               val = AE_READ_4(sc, AE_VPD_CAP_REG);
+               if ((val & AE_VPD_CAP_DONE) != 0)
+                       break;
+       }
+       if (i == AE_VPD_TIMEOUT) {
+               device_printf(sc->ae_dev, "timeout reading VPD register %d.\n",
+                   reg);
+               return (ETIMEDOUT);
+       }
+       *word = AE_READ_4(sc, AE_VPD_DATA_REG);
+       return (0);
+}
+
+static int
+ae_get_vpd_eaddr(struct ae_softc *sc, uint32_t *eaddr)
+{
+       uint32_t word, reg, val;
+       int error;
+       int found;
+       int vpdc;
+       int i;
+
+       /*
+        * Check for EEPROM.
+        */
+       error = ae_check_eeprom_present(sc, &vpdc);
+       if (error != 0)
+               return (error);
+
+       /*
+        * Read the VPD configuration space.
+        * Each register is prefixed with signature,
+        * so we can check if it is valid.
+        */
+       for (i = 0, found = 0; i < AE_VPD_NREGS; i++) {
+               error = ae_vpd_read_word(sc, i, &word);
+               if (error != 0)
+                       break;
+
+               /*
+                * Check signature.
+                */
+               if ((word & AE_VPD_SIG_MASK) != AE_VPD_SIG)
+                       break;
+               reg = word >> AE_VPD_REG_SHIFT;
+               i++;    /* Move to the next word. */
+               if (reg != AE_EADDR0_REG && reg != AE_EADDR1_REG)
+                       continue;
+
+               error = ae_vpd_read_word(sc, i, &val);
+               if (error != 0)
+                       break;
+               if (reg == AE_EADDR0_REG)
+                       eaddr[0] = val;
+               else
+                       eaddr[1] = val;
+               found++;
+       }
+       if (found < 2)
+               return (ENOENT);
+
+       eaddr[1] &= 0xffff;     /* Only last 2 bytes are used. */
+       if (AE_CHECK_EADDR_VALID(eaddr) != 0) {
+               if (bootverbose)
+                       device_printf(sc->ae_dev,
+                           "VPD ethernet address registers are invalid.\n");
+               return (EINVAL);
+       }
+       return (0);
+}
+
+static int
+ae_get_reg_eaddr(struct ae_softc *sc, uint32_t *eaddr)
+{
+       /*
+        * BIOS is supposed to set this.
+        */
+       eaddr[0] = AE_READ_4(sc, AE_EADDR0_REG);
+       eaddr[1] = AE_READ_4(sc, AE_EADDR1_REG);
+       eaddr[1] &= 0xffff;     /* Only last 2 bytes are used. */
+       if (AE_CHECK_EADDR_VALID(eaddr) != 0) {
+               if (bootverbose)
+                       device_printf(sc->ae_dev,
+                           "Ethetnet address registers are invalid.\n");
+               return (EINVAL);
+       }
+       return (0);
+}
+
+static void
+ae_get_eaddr(struct ae_softc *sc)
+{
+       uint32_t eaddr[2] = {0, 0};
+       int error;
+
+       /*
+        *Check for EEPROM.
+        */
+       error = ae_get_vpd_eaddr(sc, eaddr);
+       if (error)
+               error = ae_get_reg_eaddr(sc, eaddr);
+       if (error) {
+               if (bootverbose)
+                       device_printf(sc->ae_dev,
+                           "Generating random ethernet address.\n");
+               eaddr[0] = karc4random();
+               /*
+                * Set OUI to ASUSTek COMPUTER INC.
+                */
+               sc->ae_eaddr[0] = 0x02; /* U/L bit set. */
+               sc->ae_eaddr[1] = 0x1f;
+               sc->ae_eaddr[2] = 0xc6;
+               sc->ae_eaddr[3] = (eaddr[0] >> 16) & 0xff;
+               sc->ae_eaddr[4] = (eaddr[0] >> 8) & 0xff;
+               sc->ae_eaddr[5] = (eaddr[0] >> 0) & 0xff;
+       } else {
+               sc->ae_eaddr[0] = (eaddr[1] >> 8) & 0xff;
+               sc->ae_eaddr[1] = (eaddr[1] >> 0) & 0xff;
+               sc->ae_eaddr[2] = (eaddr[0] >> 24) & 0xff;
+               sc->ae_eaddr[3] = (eaddr[0] >> 16) & 0xff;
+               sc->ae_eaddr[4] = (eaddr[0] >> 8) & 0xff;
+               sc->ae_eaddr[5] = (eaddr[0] >> 0) & 0xff;
+       }
+}
+
+static int
+ae_mediachange(struct ifnet *ifp)
+{
+       struct ae_softc *sc = ifp->if_softc;
+       struct mii_data *mii = device_get_softc(sc->ae_miibus);
+       int error;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+       if (mii->mii_instance != 0) {
+               struct mii_softc *miisc;
+               LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+                       mii_phy_reset(miisc);
+       }
+       error = mii_mediachg(mii);
+       return (error);
+}
+
+static void
+ae_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+       struct ae_softc *sc = ifp->if_softc;
+       struct mii_data *mii = device_get_softc(sc->ae_miibus);
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+       mii_pollstat(mii);
+       ifmr->ifm_status = mii->mii_media_status;
+       ifmr->ifm_active = mii->mii_media_active;
+}
+
+static void
+ae_update_stats_tx(uint16_t flags, struct ae_stats *stats)
+{
+       if ((flags & AE_TXS_BCAST) != 0)
+               stats->tx_bcast++;
+       if ((flags & AE_TXS_MCAST) != 0)
+               stats->tx_mcast++;
+       if ((flags & AE_TXS_PAUSE) != 0)
+               stats->tx_pause++;
+       if ((flags & AE_TXS_CTRL) != 0)
+               stats->tx_ctrl++;
+       if ((flags & AE_TXS_DEFER) != 0)
+               stats->tx_defer++;
+       if ((flags & AE_TXS_EXCDEFER) != 0)
+               stats->tx_excdefer++;
+       if ((flags & AE_TXS_SINGLECOL) != 0)
+               stats->tx_singlecol++;
+       if ((flags & AE_TXS_MULTICOL) != 0)
+               stats->tx_multicol++;
+       if ((flags & AE_TXS_LATECOL) != 0)
+               stats->tx_latecol++;
+       if ((flags & AE_TXS_ABORTCOL) != 0)
+               stats->tx_abortcol++;
+       if ((flags & AE_TXS_UNDERRUN) != 0)
+               stats->tx_underrun++;
+}
+
+static void
+ae_update_stats_rx(uint16_t flags, struct ae_stats *stats)
+{
+       if ((flags & AE_RXD_BCAST) != 0)
+               stats->rx_bcast++;
+       if ((flags & AE_RXD_MCAST) != 0)
+               stats->rx_mcast++;
+       if ((flags & AE_RXD_PAUSE) != 0)
+               stats->rx_pause++;
+       if ((flags & AE_RXD_CTRL) != 0)
+               stats->rx_ctrl++;
+       if ((flags & AE_RXD_CRCERR) != 0)
+               stats->rx_crcerr++;
+       if ((flags & AE_RXD_CODEERR) != 0)
+               stats->rx_codeerr++;
+       if ((flags & AE_RXD_RUNT) != 0)
+               stats->rx_runt++;
+       if ((flags & AE_RXD_FRAG) != 0)
+               stats->rx_frag++;
+       if ((flags & AE_RXD_TRUNC) != 0)
+               stats->rx_trunc++;
+       if ((flags & AE_RXD_ALIGN) != 0)
+               stats->rx_align++;
+}
+
+static int
+ae_resume(device_t dev)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+#if 0
+       AE_READ_4(sc, AE_WOL_REG);      /* Clear WOL status. */
+#endif
+       if ((ifp->if_flags & IFF_UP) != 0)
+               ae_init(sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+       return (0);
+}
+
+static int
+ae_suspend(device_t dev)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       ae_stop(sc);
+#if 0
+       /* we don't use ae_pm_init because we don't want WOL */
+       ae_pm_init(sc);
+#endif
+       lwkt_serialize_exit(ifp->if_serializer);
+       return (0);
+}
+
+static int
+ae_shutdown(device_t dev)
+{
+       struct ae_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+
+       ae_suspend(dev);
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       ae_powersave_enable(sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+
+       return (0);
+}
+
+static void
+ae_powersave_disable(struct ae_softc *sc)
+{
+       uint32_t val;
+
+       AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 0);
+       val = AE_PHY_READ(sc, AE_PHY_DBG_DATA);
+       if (val & AE_PHY_DBG_POWERSAVE) {
+               val &= ~AE_PHY_DBG_POWERSAVE;
+               AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, val);
+               DELAY(1000);
+       }
+}
+
+static void
+ae_powersave_enable(struct ae_softc *sc)
+{
+       uint32_t val;
+
+       /*
+        * XXX magic numbers.
+        */
+       AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 0);
+       val = AE_PHY_READ(sc, AE_PHY_DBG_DATA);
+       AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, val | 0x1000);
+       AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 2);
+       AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, 0x3000);
+       AE_PHY_WRITE(sc, AE_PHY_DBG_ADDR, 3);
+       AE_PHY_WRITE(sc, AE_PHY_DBG_DATA, 0);
+}
diff --git a/sys/dev/netif/ae/if_aereg.h b/sys/dev/netif/ae/if_aereg.h
new file mode 100644 (file)
index 0000000..e892a60
--- /dev/null
@@ -0,0 +1,384 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ae/if_aereg.h,v 1.1.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+/*
+ * Master configuration register
+ */
+#define        AE_MASTER_REG           0x1400
+
+#define        AE_MASTER_SOFT_RESET    0x1     /* Reset adapter. */
+#define        AE_MASTER_MTIMER_EN     0x2     /* Unknown. */
+#define        AE_MASTER_IMT_EN        0x4     /* Interrupt moderation timer enable. */
+#define        AE_MASTER_MANUAL_INT    0x8     /* Software manual interrupt. */
+#define        AE_MASTER_REVNUM_SHIFT  16      /* Chip revision number. */
+#define        AE_MASTER_REVNUM_MASK   0xff
+#define        AE_MASTER_DEVID_SHIFT   24      /* PCI device id. */
+#define        AE_MASTER_DEVID_MASK    0xff
+
+/*
+ * Interrupt status register
+ */
+#define        AE_ISR_REG              0x1600
+#define        AE_ISR_TIMER            0x00000001      /* Counter expired. */
+#define        AE_ISR_MANUAL           0x00000002      /* Manual interrupt occuried. */
+#define        AE_ISR_RXF_OVERFLOW     0x00000004      /* RxF overflow occuried. */
+#define        AE_ISR_TXF_UNDERRUN     0x00000008      /* TxF underrun occuried. */
+#define        AE_ISR_TXS_OVERFLOW     0x00000010      /* TxS overflow occuried. */
+#define        AE_ISR_RXS_OVERFLOW     0x00000020      /* Internal RxS ring overflow. */
+#define        AE_ISR_LINK_CHG         0x00000040      /* Link state changed. */
+#define        AE_ISR_TXD_UNDERRUN     0x00000080      /* TxD underrun occuried. */
+#define        AE_ISR_RXD_OVERFLOW     0x00000100      /* RxD overflow occuried. */
+#define        AE_ISR_DMAR_TIMEOUT     0x00000200      /* DMA read timeout. */
+#define        AE_ISR_DMAW_TIMEOUT     0x00000400      /* DMA write timeout. */
+#define        AE_ISR_PHY              0x00000800      /* PHY interrupt. */
+#define        AE_ISR_TXS_UPDATED      0x00010000      /* Tx status updated. */
+#define        AE_ISR_RXD_UPDATED      0x00020000      /* Rx status updated. */
+#define        AE_ISR_TX_EARLY         0x00040000      /* TxMAC started transmit. */
+#define        AE_ISR_FIFO_UNDERRUN    0x01000000      /* FIFO underrun. */
+#define        AE_ISR_FRAME_ERROR      0x02000000      /* Frame receive error. */
+#define        AE_ISR_FRAME_SUCCESS    0x04000000      /* Frame receive success. */
+#define        AE_ISR_CRC_ERROR        0x08000000      /* CRC error occuried. */
+#define        AE_ISR_PHY_LINKDOWN     0x10000000      /* PHY link down. */
+#define        AE_ISR_DISABLE          0x80000000      /* Disable interrupts. */
+
+#define        AE_ISR_TX_EVENT         (AE_ISR_TXF_UNDERRUN | AE_ISR_TXS_OVERFLOW | \
+                                AE_ISR_TXD_UNDERRUN | AE_ISR_TXS_UPDATED | \
+                                AE_ISR_TX_EARLY)
+#define        AE_ISR_RX_EVENT         (AE_ISR_RXF_OVERFLOW | AE_ISR_RXS_OVERFLOW | \
+                                AE_ISR_RXD_OVERFLOW | AE_ISR_RXD_UPDATED)
+
+/* Interrupt mask register. */
+#define        AE_IMR_REG              0x1604
+
+#define        AE_IMR_DEFAULT          (AE_ISR_DMAR_TIMEOUT | AE_ISR_DMAW_TIMEOUT | \
+                                AE_ISR_PHY_LINKDOWN | \
+                                AE_ISR_TXS_UPDATED | AE_ISR_RXD_UPDATED )
+
+/*
+ * Ethernet address register.
+ */
+#define        AE_EADDR0_REG           0x1488  /* 5 - 2 bytes */
+#define        AE_EADDR1_REG           0x148c  /* 1 - 0 bytes */
+
+/*
+ * Desriptor rings registers.
+ * L2 supports 64-bit addressing but all rings base addresses
+ * should have the same high 32 bits of address.
+ */
+#define        AE_DESC_ADDR_HI_REG     0x1540  /* High 32 bits of ring base address. */
+#define        AE_RXD_ADDR_LO_REG      0x1554  /* Low 32 bits of RxD ring address. */
+#define        AE_TXD_ADDR_LO_REG      0x1544  /* Low 32 bits of TxD ring address. */
+#define        AE_TXS_ADDR_LO_REG      0x154c  /* Low 32 bits of TxS ring address. */
+#define        AE_RXD_COUNT_REG        0x1558  /* Number of RxD descriptors in ring.
+                                          Should be 120-byte aligned (i.e.
+                                          the 'data' field of RxD should
+                                          have 128-byte alignment). */
+#define        AE_TXD_BUFSIZE_REG      0x1548  /* Size of TxD ring in 4-byte units.
+                                          Should be 4-byte aligned. */
+#define        AE_TXS_COUNT_REG        0x1550  /* Number of TxS descriptors in ring.
+                                          4 byte alignment. */
+#define        AE_RXD_COUNT_MIN        16
+#define        AE_RXD_COUNT_MAX        512
+#define        AE_RXD_COUNT_DEFAULT    64
+
+#define        AE_TXD_BUFSIZE_MIN      4096
+#define        AE_TXD_BUFSIZE_MAX      65536
+#define        AE_TXD_BUFSIZE_DEFAULT  8192
+
+#define        AE_TXS_COUNT_MIN        8       /* Not sure. */
+#define        AE_TXS_COUNT_MAX        160
+#define        AE_TXS_COUNT_DEFAULT    64      /* AE_TXD_BUFSIZE_DEFAULT / 128 */
+
+/*
+ * Inter-frame gap configuration register.
+ */
+#define        AE_IFG_REG              0x1484
+
+#define        AE_IFG_TXIPG_DEFAULT    0x60    /* 96-bit IFG time. */
+#define        AE_IFG_TXIPG_SHIFT      0
+#define        AE_IFG_TXIPG_MASK       0x7f
+
+#define        AE_IFG_RXIPG_DEFAULT    0x50    /* 80-bit IFG time. */
+#define        AE_IFG_RXIPG_SHIFT      8
+#define        AE_IFG_RXIPG_MASK       0xff00
+
+#define        AE_IFG_IPGR1_DEFAULT    0x40    /* Carrier-sense window. */
+#define        AE_IFG_IPGR1_SHIFT      16
+#define        AE_IFG_IPGR1_MASK       0x7f0000
+
+#define        AE_IFG_IPGR2_DEFAULT    0x60    /* IFG window. */
+#define        AE_IFG_IPGR2_SHIFT      24
+#define        AE_IFG_IPGR2_MASK       0x7f000000
+
+/*
+ * Half-duplex mode configuration register.
+ */
+#define        AE_HDPX_REG             0x1498
+
+/* Collision window. */
+#define        AE_HDPX_LCOL_SHIFT      0
+#define        AE_HDPX_LCOL_MASK       0x000003ff
+#define        AE_HDPX_LCOL_DEFAULT    0x37
+
+/* Max retransmission time, after that the packet will be discarded. */
+#define        AE_HDPX_RETRY_SHIFT     12
+#define        AE_HDPX_RETRY_MASK      0x0000f000
+#define        AE_HDPX_RETRY_DEFAULT   0x0f
+
+/* Alternative binary exponential back-off time. */
+#define        AE_HDPX_ABEBT_SHIFT     20
+#define        AE_HDPX_ABEBT_MASK      0x00f00000
+#define        AE_HDPX_ABEBT_DEFAULT   0x0a
+
+/* IFG to start JAM for collision based flow control (8-bit time units).*/
+#define        AE_HDPX_JAMIPG_SHIFT    24
+#define        AE_HDPX_JAMIPG_MASK     0x0f000000
+#define        AE_HDPX_JAMIPG_DEFAULT  0x07
+
+/* Allow the transmission of a packet which has been excessively deferred. */
+#define        AE_HDPX_EXC_EN          0x00010000
+/* No back-off on collision, immediately start the retransmission. */
+#define        AE_HDPX_NO_BACK_C       0x00020000
+/* No back-off on backpressure, immediately start the transmission. */
+#define        AE_HDPX_NO_BACK_P       0x00040000
+/* Alternative binary exponential back-off enable. */
+#define        AE_HDPX_ABEBE           0x00080000
+
+/*
+ * Interrupt moderation timer configuration register.
+ */
+#define        AE_IMT_REG              0x1408  /* Timer value in 2 us units. */
+#define        AE_IMT_MAX              65000
+#define        AE_IMT_MIN              50
+#define        AE_IMT_DEFAULT          100     /* 200 microseconds. */
+
+/*
+ * Interrupt clearing timer configuration register.
+ */
+#define        AE_ICT_REG              0x140e  /* Maximum time allowed to clear
+                                          interrupt. In 2 us units.  */
+#define        AE_ICT_DEFAULT          50000   /* 100ms */
+
+/*
+ * MTU configuration register.
+ */
+#define        AE_MTU_REG              0x149c  /* MTU size in bytes. */
+
+/*
+ * Cut-through configuration register.
+ */
+#define        AE_CUT_THRESH_REG       0x1590  /* Cut-through threshold in unknown units. */
+#define        AE_CUT_THRESH_DEFAULT   0x177
+
+/*
+ * Flow-control configuration registers.
+ */
+#define        AE_FLOW_THRESH_HI_REG   0x15a8  /* High watermark of RxD
+                                          overflow threshold. */
+#define        AE_FLOW_THRESH_LO_REG   0x15aa  /* Lower watermark of RxD
+                                          overflow threshold */
+
+/*
+ * Mailbox configuration registers.
+*/
+#define        AE_MB_TXD_IDX_REG       0x15f0  /* TxD read index. */
+#define        AE_MB_RXD_IDX_REG       0x15f4  /* RxD write index. */
+
+/*
+ * DMA configuration registers.
+ */
+#define        AE_DMAREAD_REG          0x1580  /* Read DMA configuration register. */
+#define        AE_DMAREAD_EN           1
+#define        AE_DMAWRITE_REG         0x15a0  /* Write DMA configuration register. */
+#define        AE_DMAWRITE_EN          1
+
+/*
+ * MAC configuration register.
+ */
+#define        AE_MAC_REG              0x1480
+
+#define        AE_MAC_TX_EN            0x00000001      /* Enable transmit. */
+#define        AE_MAC_RX_EN            0x00000002      /* Enable receive. */
+#define        AE_MAC_TX_FLOW_EN       0x00000004      /* Enable Tx flow control. */
+#define        AE_MAC_RX_FLOW_EN       0x00000008      /* Enable Rx flow control. */
+#define        AE_MAC_LOOPBACK         0x00000010      /* Loopback at MII. */
+#define        AE_MAC_FULL_DUPLEX      0x00000020      /* Enable full-duplex. */
+#define        AE_MAC_TX_CRC_EN        0x00000040      /* Enable CRC generation. */
+#define        AE_MAC_TX_AUTOPAD       0x00000080      /* Pad short frames. */
+#define        AE_MAC_PREAMBLE_MASK    0x00003c00      /* Preamble length. */
+#define        AE_MAC_PREAMBLE_SHIFT   10
+#define        AE_MAC_PREAMBLE_DEFAULT 0x07            /* By standard. */
+#define        AE_MAC_RMVLAN_EN        0x00004000      /* Remove VLAN tags in
+                                                  incoming packets. */
+#define        AE_MAC_PROMISC_EN       0x00008000      /* Enable promiscue mode. */
+#define        AE_MAC_TX_MAXBACKOFF    0x00100000      /* Unknown. */
+#define        AE_MAC_MCAST_EN         0x02000000      /* Pass all multicast frames. */
+#define        AE_MAC_BCAST_EN         0x04000000      /* Pass all broadcast frames. */
+#define        AE_MAC_CLK_PHY          0x08000000      /* If 1 uses loopback clock
+                                                  PHY, if 0 - system clock. */
+#define        AE_HALFBUF_MASK         0xf0000000      /* Half-duplex retry buffer. */
+#define        AE_HALFBUF_SHIFT        28
+#define        AE_HALFBUF_DEFAULT      2               /* XXX: From Linux. */
+
+/*
+ * MDIO control register.
+ */
+#define        AE_MDIO_REG             0x1414
+#define        AE_MDIO_DATA_MASK       0xffff
+#define        AE_MDIO_DATA_SHIFT      0
+#define        AE_MDIO_REGADDR_MASK    0x1f0000
+#define        AE_MDIO_REGADDR_SHIFT   16
+#define        AE_MDIO_READ            0x00200000      /* Read operation. */
+#define        AE_MDIO_SUP_PREAMBLE    0x00400000      /* Suppress preamble. */
+#define        AE_MDIO_START           0x00800000      /* Initiate MDIO transfer. */
+#define        AE_MDIO_CLK_SHIFT       24              /* Clock selection. */
+#define        AE_MDIO_CLK_MASK        0x07000000      /* Clock selection. */
+#define        AE_MDIO_CLK_25_4        0               /* Dividers? */
+#define        AE_MDIO_CLK_25_6        2
+#define        AE_MDIO_CLK_25_8        3
+#define        AE_MDIO_CLK_25_10       4
+#define        AE_MDIO_CLK_25_14       5
+#define        AE_MDIO_CLK_25_20       6
+#define        AE_MDIO_CLK_25_28       7
+#define        AE_MDIO_BUSY            0x08000000      /* MDIO is busy. */
+
+/*
+ * Idle status register.
+ */
+#define        AE_IDLE_REG             0x1410
+
+/*
+ * Idle status bits.
+ * If bit is set then the corresponding module is in non-idle state.
+ */
+#define        AE_IDLE_RXMAC           1
+#define        AE_IDLE_TXMAC           2
+#define        AE_IDLE_DMAREAD         8
+#define        AE_IDLE_DMAWRITE        4
+
+/*
+ * Multicast hash tables registers.
+ */
+#define        AE_REG_MHT0             0x1490
+#define        AE_REG_MHT1             0x1494
+
+/*
+ * Wake on lan (WOL).
+ */
+#define        AE_WOL_REG              0x14a0
+#define        AE_WOL_MAGIC            0x00000004
+#define        AE_WOL_MAGIC_PME        0x00000008
+#define        AE_WOL_LNKCHG           0x00000010
+#define        AE_WOL_LNKCHG_PME       0x00000020
+
+/*
+ * PCIE configuration registers. Descriptions unknown.
+ */
+#define        AE_PCIE_LTSSM_TESTMODE_REG      0x12fc
+#define        AE_PCIE_LTSSM_TESTMODE_DEFAULT  0x6500
+#define        AE_PCIE_DLL_TX_CTRL_REG         0x1104
+#define        AE_PCIE_DLL_TX_CTRL_SEL_NOR_CLK 0x0400
+#define        AE_PCIE_DLL_TX_CTRL_DEFAULT     0x0568
+#define        AE_PCIE_PHYMISC_REG             0x1000
+#define        AE_PCIE_PHYMISC_FORCE_RCV_DET   0x4
+
+/*
+ * PHY enable register.
+ */
+#define        AE_PHY_ENABLE_REG       0x140c
+#define        AE_PHY_ENABLE           1
+
+/*
+ * VPD registers.
+ */
+#define        AE_VPD_CAP_REG          0x6c    /* Command register. */
+#define        AE_VPD_CAP_ID_MASK      0xff
+#define        AE_VPD_CAP_ID_SHIFT     0
+#define        AE_VPD_CAP_NEXT_MASK    0xff00
+#define        AE_VPD_CAP_NEXT_SHIFT   8
+#define        AE_VPD_CAP_ADDR_MASK    0x7fff0000
+#define        AE_VPD_CAP_ADDR_SHIFT   16
+#define        AE_VPD_CAP_DONE         0x80000000
+#define        AE_VPD_DATA_REG         0x70    /* Data register. */
+
+#define        AE_VPD_NREGS            64      /* Maximum number of VPD regs. */
+#define        AE_VPD_SIG_MASK         0xff
+#define        AE_VPD_SIG              0x5a    /* VPD block signature. */
+#define        AE_VPD_REG_SHIFT        16      /* Register id offset. */
+
+/*
+ * SPI registers.
+ */
+#define        AE_SPICTL_REG           0x200
+#define        AE_SPICTL_VPD_EN        0x2000  /* Enable VPD. */
+
+/*
+ * PHY-specific registers constants.
+ */
+#define        AE_PHY_DBG_ADDR         0x1d
+#define        AE_PHY_DBG_DATA         0x1e
+#define        AE_PHY_DBG_POWERSAVE    0x1000
+
+/*
+ * TxD flags.
+ */
+#define        AE_TXD_INSERT_VTAG      0x8000  /* Insert VLAN tag on transfer. */
+
+/*
+ * TxS flags.
+ */
+#define        AE_TXS_SUCCESS          0x0001  /* Packed transmitted successfully. */
+#define        AE_TXS_BCAST            0x0002  /* Transmitted broadcast frame. */
+#define        AE_TXS_MCAST            0x0004  /* Transmitted multicast frame. */
+#define        AE_TXS_PAUSE            0x0008  /* Transmitted pause frame. */
+#define        AE_TXS_CTRL             0x0010  /* Transmitted control frame. */
+#define        AE_TXS_DEFER            0x0020  /* Frame transmitted with defer. */
+#define        AE_TXS_EXCDEFER         0x0040  /* Excessive collision. */
+#define        AE_TXS_SINGLECOL        0x0080  /* Single collision occuried. */
+#define        AE_TXS_MULTICOL         0x0100  /* Multiple collisions occuried. */
+#define        AE_TXS_LATECOL          0x0200  /* Late collision occuried. */
+#define        AE_TXS_ABORTCOL         0x0400  /* Frame abort due to collisions. */
+#define        AE_TXS_UNDERRUN         0x0800  /* Tx SRAM underrun occuried. */
+#define        AE_TXS_UPDATE           0x8000
+
+/*
+ * RxD flags.
+ */
+#define        AE_RXD_SUCCESS          0x0001
+#define        AE_RXD_BCAST            0x0002  /* Broadcast frame received. */
+#define        AE_RXD_MCAST            0x0004  /* Multicast frame received. */
+#define        AE_RXD_PAUSE            0x0008  /* Pause frame received. */
+#define        AE_RXD_CTRL             0x0010  /* Control frame received. */
+#define        AE_RXD_CRCERR           0x0020  /* Invalid frame CRC. */
+#define        AE_RXD_CODEERR          0x0040  /* Invalid frame opcode. */
+#define        AE_RXD_RUNT             0x0080  /* Runt frame received. */
+#define        AE_RXD_FRAG             0x0100  /* Collision fragment received. */
+#define        AE_RXD_TRUNC            0x0200  /* The frame was truncated due
+                                          to Rx SRAM underrun. */
+#define        AE_RXD_ALIGN            0x0400  /* Frame alignment error. */
+#define        AE_RXD_HAS_VLAN         0x0800  /* VLAN tag present. */
+#define        AE_RXD_UPDATE           0x8000
diff --git a/sys/dev/netif/ae/if_aevar.h b/sys/dev/netif/ae/if_aevar.h
new file mode 100644 (file)
index 0000000..f37009c
--- /dev/null
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ae/if_aevar.h,v 1.1.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef IF_AEVAR_H
+#define IF_AEVAR_H
+
+/*
+ * Supported chips identifiers.
+*/
+#define        VENDORID_ATTANSIC       0x1969
+#define        DEVICEID_ATTANSIC_L2    0x2048
+
+/* How much to wait for reset to complete (10 microsecond units). */
+#define        AE_RESET_TIMEOUT        100
+
+/* How much to wait for device to enter idle state (100 microsecond units). */
+#define        AE_IDLE_TIMEOUT         100
+
+/* How much to wait for MDIO to do the work (2 microsecond units). */
+#define        AE_MDIO_TIMEOUT         10
+
+/* How much to wait for VPD reading operation to complete (2 ms units). */
+#define AE_VPD_TIMEOUT         10
+
+/* How much to wait for send operation to complete (HZ units). */
+#define        AE_TX_TIMEOUT           5
+
+/* Default PHY address. */
+#define        AE_PHYADDR_DEFAULT      0
+
+/* Tx packet descriptor header format. */
+struct ae_txd {
+       uint16_t        len;
+       uint16_t        vlan;
+} __packed;
+
+/* Tx status descriptor format. */
+struct ae_txs {
+       uint16_t        len;
+       uint16_t        flags;
+} __packed;
+
+/* Rx packet descriptor format. */
+struct ae_rxd {
+       uint16_t        len;
+       uint16_t        flags;
+       uint16_t        vlan;
+       uint16_t        __pad;
+       uint8_t         data[1528];
+} __packed;
+
+/* Statistics. */
+struct ae_stats {
+       uint32_t        rx_bcast;
+       uint32_t        rx_mcast;
+       uint32_t        rx_pause;
+       uint32_t        rx_ctrl;
+       uint32_t        rx_crcerr;
+       uint32_t        rx_codeerr;
+       uint32_t        rx_runt;
+       uint32_t        rx_frag;
+       uint32_t        rx_trunc;
+       uint32_t        rx_align;
+       uint32_t        tx_bcast;
+       uint32_t        tx_mcast;
+       uint32_t        tx_pause;
+       uint32_t        tx_ctrl;
+       uint32_t        tx_defer;
+       uint32_t        tx_excdefer;
+       uint32_t        tx_singlecol;
+       uint32_t        tx_multicol;
+       uint32_t        tx_latecol;
+       uint32_t        tx_abortcol;
+       uint32_t        tx_underrun;
+};
+
+/* Software state structure. */
+struct ae_softc        {
+       struct arpcom           arpcom;
+       device_t                ae_dev;
+
+       int                     ae_mem_rid;
+       struct resource         *ae_mem_res;
+        bus_space_tag_t                ae_mem_bt;
+        bus_space_handle_t     ae_mem_bh;
+
+       int                     ae_irq_rid;
+       struct resource         *ae_irq_res;
+       void                    *ae_irq_handle;
+
+       int                     ae_phyaddr;
+       device_t                ae_miibus;
+
+       int                     ae_rev;
+       int                     ae_chip_rev;
+       uint8_t                 ae_eaddr[ETHER_ADDR_LEN];
+       uint8_t                 ae_flags;
+       int                     ae_if_flags;
+
+       struct callout          ae_tick_ch;
+
+       /* DMA tags. */
+       bus_dma_tag_t           dma_parent_tag;
+       bus_dma_tag_t           dma_rxd_tag;
+       bus_dma_tag_t           dma_txd_tag;
+       bus_dma_tag_t           dma_txs_tag;
+       bus_dmamap_t            dma_rxd_map;
+       bus_dmamap_t            dma_txd_map;
+       bus_dmamap_t            dma_txs_map;
+
+       bus_addr_t              dma_rxd_busaddr;
+       bus_addr_t              dma_txd_busaddr;
+       bus_addr_t              dma_txs_busaddr;
+
+       uint8_t                 *rxd_base_dma;  /* Start of allocated area. */
+       struct ae_rxd           *rxd_base;      /* Start of RxD ring. */
+       uint8_t                 *txd_base;      /* Start of TxD ring. */
+       struct ae_txs           *txs_base;      /* Start of TxS ring. */
+
+       /* Ring pointers. */
+       unsigned int            rxd_cur;
+       unsigned int            txd_cur;
+       unsigned int            txs_cur;
+       unsigned int            txs_ack;
+       unsigned int            txd_ack;
+
+       int                     tx_inproc;      /* Active Tx frames in ring. */
+       int                     wd_timer;       /* XXX remove */
+
+       struct ae_stats         stats;
+       struct sysctl_ctx_list  ae_sysctl_ctx;
+       struct sysctl_oid       *ae_sysctl_tree;
+};
+
+#define        BUS_ADDR_LO(x)          ((uint64_t) (x) & 0xFFFFFFFF)
+#define        BUS_ADDR_HI(x)          ((uint64_t) (x) >> 32)
+
+#define        AE_FLAG_LINK            0x01    /* Has link. */
+#define        AE_FLAG_DETACH          0x02    /* Is detaching. */
+#define        AE_FLAG_TXAVAIL         0x04    /* Tx'es available. */
+#define        AE_FLAG_MSI             0x08    /* Using MSI. */
+#define        AE_FLAG_PMG             0x10    /* Supports PCI power management. */
+
+#endif /* IF_AEVAR_H */