Synch the XL driver with FreeBSD-4.x.
[dragonfly.git] / sys / dev / netif / xl / if_xl.c
index 7d0385f..90846d1 100644 (file)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/pci/if_xl.c,v 1.72.2.25 2003/02/05 22:03:58 mbr Exp $
- * $DragonFly: src/sys/dev/netif/xl/if_xl.c,v 1.6 2004/01/06 01:40:50 dillon Exp $
- *
- * $FreeBSD: src/sys/pci/if_xl.c,v 1.72.2.25 2003/02/05 22:03:58 mbr Exp $
+ * $FreeBSD: src/sys/pci/if_xl.c,v 1.72.2.28 2003/10/08 06:01:57 murray Exp $
+ * $DragonFly: src/sys/dev/netif/xl/if_xl.c,v 1.7 2004/01/24 06:34:02 dillon Exp $
  */
 
 /*
@@ -83,7 +81,7 @@
  * (3c59x) also supported a bus master mode, however for those chips
  * you could only DMA packets to/from a contiguous memory buffer. For
  * transmission this would mean copying the contents of the queued mbuf
- * chain into a an mbuf cluster and then DMAing the cluster. This extra
+ * chain into an mbuf cluster and then DMAing the cluster. This extra
  * copy would sort of defeat the purpose of the bus master support for
  * any packet that doesn't fit into a single mbuf.
  *
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/sockio.h>
+#include <sys/endian.h>
 #include <sys/mbuf.h>
-#include <sys/malloc.h>
 #include <sys/kernel.h>
 #include <sys/socket.h>
 
 #include <net/ethernet.h>
 #include <net/if_dl.h>
 #include <net/if_media.h>
+#include <net/vlan/if_vlan_var.h>
 
 #include <net/bpf.h>
 
-#include <vm/vm.h>              /* for vtophys */
-#include <vm/pmap.h>            /* for vtophys */
-#include <machine/clock.h>      /* for DELAY */
 #include <machine/bus_memio.h>
 #include <machine/bus_pio.h>
 #include <machine/bus.h>
+#include <machine/clock.h>      /* for DELAY */
 #include <machine/resource.h>
 #include <sys/bus.h>
 #include <sys/rman.h>
 /* "controller miibus0" required.  See GENERIC if you get errors here. */
 #include "miibus_if.h"
 
-/*
- * The following #define causes the code to use PIO to access the
- * chip's registers instead of memory mapped mode. The reason PIO mode
- * is on by default is that the Etherlink XL manual seems to indicate
- * that only the newer revision chips (3c905B) support both PIO and
- * memory mapped access. Since we want to be compatible with the older
- * bus master chips, we use PIO here. If you comment this out, the
- * driver will use memory mapped I/O, which may be faster but which
- * might not work on some devices.
- */
-#define XL_USEIOSPACE
-
 #include "if_xlreg.h"
 
 #define XL905B_CSUM_FEATURES   (CSUM_IP | CSUM_TCP | CSUM_UDP)
@@ -262,6 +247,11 @@ static int xl_list_tx_init_90xB    (struct xl_softc *);
 static void xl_wait            (struct xl_softc *);
 static void xl_mediacheck      (struct xl_softc *);
 static void xl_choose_xcvr     (struct xl_softc *, int);
+static void xl_dma_map_addr    (void *, bus_dma_segment_t *, int, int);
+static void xl_dma_map_rxbuf   (void *, bus_dma_segment_t *, int, bus_size_t,
+                                               int);
+static void xl_dma_map_txbuf   (void *, bus_dma_segment_t *, int, bus_size_t,
+                                               int);
 #ifdef notdef
 static void xl_testpacket      (struct xl_softc *);
 #endif
@@ -271,14 +261,6 @@ static int xl_miibus_writereg      (device_t, int, int, int);
 static void xl_miibus_statchg  (device_t);
 static void xl_miibus_mediainit        (device_t);
 
-#ifdef XL_USEIOSPACE
-#define XL_RES                 SYS_RES_IOPORT
-#define XL_RID                 XL_PCI_LOIO
-#else
-#define XL_RES                 SYS_RES_MEMORY
-#define XL_RID                 XL_PCI_LOMEM
-#endif
-
 static device_method_t xl_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,         xl_probe),
@@ -314,6 +296,65 @@ MODULE_DEPEND(if_xl, miibus, 1, 1, 1);
 DRIVER_MODULE(if_xl, pci, xl_driver, xl_devclass, 0, 0);
 DRIVER_MODULE(miibus, xl, miibus_driver, miibus_devclass, 0, 0);
 
+static void
+xl_dma_map_addr(arg, segs, nseg, error)
+       void *arg;
+       bus_dma_segment_t *segs;
+       int nseg, error;
+{
+       u_int32_t *paddr;
+       
+       paddr = arg;
+       *paddr = segs->ds_addr;
+}
+
+static void
+xl_dma_map_rxbuf(arg, segs, nseg, mapsize, error)
+       void *arg;
+       bus_dma_segment_t *segs;
+       int nseg;
+       bus_size_t mapsize;
+       int error;
+{
+       u_int32_t *paddr;
+
+       if (error)
+               return;
+       KASSERT(nseg == 1, ("xl_dma_map_rxbuf: too many DMA segments"));
+       paddr = arg;
+       *paddr = segs->ds_addr;
+}
+
+static void
+xl_dma_map_txbuf(arg, segs, nseg, mapsize, error)
+       void *arg;
+       bus_dma_segment_t *segs;
+       int nseg;
+       bus_size_t mapsize;
+       int error;
+{
+       struct xl_list *l;
+       int i, total_len;
+
+       if (error)
+               return;
+
+       KASSERT(nseg <= XL_MAXFRAGS, ("too many DMA segments"));
+
+       total_len = 0;
+       l = arg;
+       for (i = 0; i < nseg; i++) {
+               KASSERT(segs[i].ds_len <= MCLBYTES, ("segment size too large"));
+               l->xl_frag[i].xl_addr = htole32(segs[i].ds_addr);
+               l->xl_frag[i].xl_len = htole32(segs[i].ds_len);
+               total_len += segs[i].ds_len;
+       }
+       l->xl_frag[nseg - 1].xl_len = htole32(segs[nseg - 1].ds_len |
+           XL_LAST_FRAG);
+       l->xl_status = htole32(total_len);
+       l->xl_next = 0;
+}
+
 /*
  * Murphy's law says that it's possible the chip can wedge and
  * the 'command in progress' bit may never clear. Hence, we wait
@@ -350,11 +391,11 @@ xl_wait(sc)
  */
 #define MII_SET(x)                                     \
        CSR_WRITE_2(sc, XL_W4_PHY_MGMT,                 \
-               CSR_READ_2(sc, XL_W4_PHY_MGMT) | x)
+               CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x))
 
 #define MII_CLR(x)                                     \
        CSR_WRITE_2(sc, XL_W4_PHY_MGMT,                 \
-               CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~x)
+               CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x))
 
 /*
  * Sync the PHYs by setting data bit and strobing the clock 32 times.
@@ -370,9 +411,11 @@ xl_mii_sync(sc)
 
        for (i = 0; i < 32; i++) {
                MII_SET(XL_MII_CLK);
-               DELAY(1);
+               MII_SET(XL_MII_DATA);
+               MII_SET(XL_MII_DATA);
                MII_CLR(XL_MII_CLK);
-               DELAY(1);
+               MII_SET(XL_MII_DATA);
+               MII_SET(XL_MII_DATA);
        }
 
        return;
@@ -398,9 +441,7 @@ xl_mii_send(sc, bits, cnt)
                 } else {
                        MII_CLR(XL_MII_DATA);
                 }
-               DELAY(1);
                MII_CLR(XL_MII_CLK);
-               DELAY(1);
                MII_SET(XL_MII_CLK);
        }
 }
@@ -450,19 +491,15 @@ xl_mii_readreg(sc, frame)
 
        /* Idle bit */
        MII_CLR((XL_MII_CLK|XL_MII_DATA));
-       DELAY(1);
        MII_SET(XL_MII_CLK);
-       DELAY(1);
 
        /* Turn off xmit. */
        MII_CLR(XL_MII_DIR);
 
        /* Check for ack */
        MII_CLR(XL_MII_CLK);
-       DELAY(1);
        ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA;
        MII_SET(XL_MII_CLK);
-       DELAY(1);
 
        /*
         * Now try reading data bits. If the ack failed, we still
@@ -471,31 +508,24 @@ xl_mii_readreg(sc, frame)
        if (ack) {
                for(i = 0; i < 16; i++) {
                        MII_CLR(XL_MII_CLK);
-                       DELAY(1);
                        MII_SET(XL_MII_CLK);
-                       DELAY(1);
                }
                goto fail;
        }
 
        for (i = 0x8000; i; i >>= 1) {
                MII_CLR(XL_MII_CLK);
-               DELAY(1);
                if (!ack) {
                        if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA)
                                frame->mii_data |= i;
-                       DELAY(1);
                }
                MII_SET(XL_MII_CLK);
-               DELAY(1);
        }
 
 fail:
 
        MII_CLR(XL_MII_CLK);
-       DELAY(1);
        MII_SET(XL_MII_CLK);
-       DELAY(1);
 
        splx(s);
 
@@ -516,6 +546,7 @@ xl_mii_writereg(sc, frame)
        int                     s;
 
        s = splimp();
+
        /*
         * Set up frame for TX.
         */
@@ -545,9 +576,7 @@ xl_mii_writereg(sc, frame)
 
        /* Idle bit. */
        MII_SET(XL_MII_CLK);
-       DELAY(1);
        MII_CLR(XL_MII_CLK);
-       DELAY(1);
 
        /*
         * Turn off xmit.
@@ -869,8 +898,8 @@ xl_setmulti_hash(sc)
                CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i);
 
        /* now program new ones */
-       for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
-                               ifma = ifma->ifma_link.le_next) {
+        for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+                                ifma = ifma->ifma_link.le_next) {
                if (ifma->ifma_addr->sa_family != AF_LINK)
                        continue;
                h = xl_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
@@ -1047,6 +1076,16 @@ xl_reset(sc)
                    ((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
                     XL_RESETOPT_DISADVFD:0));
 
+       /*
+        * If we're using memory mapped register mode, pause briefly
+        * after issuing the reset command before trying to access any
+        * other registers. With my 3c575C cardbus card, failing to do
+        * this results in the system locking up while trying to poll
+        * the command busy bit in the status register.
+        */
+       if (sc->xl_flags & XL_FLAG_USE_MMIO)
+               DELAY(100000);
+
        for (i = 0; i < XL_TIMEOUT; i++) {
                DELAY(10);
                if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
@@ -1059,7 +1098,7 @@ xl_reset(sc)
        /* Reset TX and RX. */
        /* Note: the RX reset takes an absurd amount of time
         * on newer versions of the Tornado chips such as those
-        * on the 3c905CX and newer 3c908C cards. We wait an    
+        * on the 3c905CX and newer 3c908C cards. We wait an
         * extra amount of time so that xl_wait() doesn't complain
         * and annoy the users.
         */
@@ -1265,17 +1304,20 @@ xl_attach(dev)
 {
        int                     s;
        u_char                  eaddr[ETHER_ADDR_LEN];
+       u_int16_t               xcvr[2];
        u_int32_t               command;
        struct xl_softc         *sc;
        struct ifnet            *ifp;
        int                     media = IFM_ETHER|IFM_100_TX|IFM_FDX;
-       int                     unit, error = 0, rid;
+       int                     unit, error = 0, rid, res;
 
        s = splimp();
 
        sc = device_get_softc(dev);
        unit = device_get_unit(dev);
 
+       ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts);
+
        sc->xl_flags = 0;
        if (pci_get_device(dev) == TC_DEVICEID_HURRICANE_555)
                sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
@@ -1311,7 +1353,7 @@ xl_attach(dev)
                    XL_FLAG_INVERT_LED_PWR;
        if (pci_get_device(dev) == TC_DEVICEID_TORNADO_10_100BT_920B)
                sc->xl_flags |= XL_FLAG_PHYOK;
-
+#ifndef BURN_BRIDGES
        /*
         * If this is a 3c905B, we have to check one extra thing.
         * The 905B supports power management and may be placed in
@@ -1350,7 +1392,7 @@ xl_attach(dev)
                pci_write_config(dev, XL_PCI_LOMEM, membase, 4);
                pci_write_config(dev, XL_PCI_INTLINE, irq, 4);
        }
-
+#endif
        /*
         * Map control/status registers.
         */
@@ -1359,28 +1401,36 @@ xl_attach(dev)
        pci_enable_io(dev, SYS_RES_MEMORY);
        command = pci_read_config(dev, PCIR_COMMAND, 4);
 
-#ifdef XL_USEIOSPACE
-       if (!(command & PCIM_CMD_PORTEN)) {
-               printf("xl%d: failed to enable I/O ports!\n", unit);
-               error = ENXIO;
-               goto fail;
-       }
-#else
-       if (!(command & PCIM_CMD_MEMEN)) {
-               printf("xl%d: failed to enable memory mapping!\n", unit);
+       if (!(command & PCIM_CMD_PORTEN) && !(command & PCIM_CMD_MEMEN)) {
+               printf("xl%d: failed to enable I/O ports and memory mappings!\n", unit);
                error = ENXIO;
                goto fail;
        }
-#endif
 
-       rid = XL_RID;
-       sc->xl_res = bus_alloc_resource(dev, XL_RES, &rid,
+       rid = XL_PCI_LOMEM;
+       res = SYS_RES_MEMORY;
+
+#if 0
+       sc->xl_res = bus_alloc_resource(dev, res, &rid,
            0, ~0, 1, RF_ACTIVE);
+#endif
 
-       if (sc->xl_res == NULL) {
-               printf ("xl%d: couldn't map ports/memory\n", unit);
-               error = ENXIO;
-               goto fail;
+       if (sc->xl_res != NULL) {
+               sc->xl_flags |= XL_FLAG_USE_MMIO;
+               if (bootverbose)
+                       printf("xl%d: using memory mapped I/O\n", unit);
+       } else {
+               rid = XL_PCI_LOIO;
+               res = SYS_RES_IOPORT;
+               sc->xl_res = bus_alloc_resource(dev, res, &rid,
+                   0, ~0, 1, RF_ACTIVE);
+               if (sc->xl_res == NULL) {
+                       printf ("xl%d: couldn't map ports/memory\n", unit);
+                       error = ENXIO;
+                       goto fail;
+               }
+               if (bootverbose)
+                       printf("xl%d: using port I/O\n", unit);
        }
 
        sc->xl_btag = rman_get_bustag(sc->xl_res);
@@ -1393,7 +1443,6 @@ xl_attach(dev)
 
                if (sc->xl_fres == NULL) {
                        printf ("xl%d: couldn't map ports/memory\n", unit);
-                       bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
                        error = ENXIO;
                        goto fail;
                }
@@ -1402,33 +1451,16 @@ xl_attach(dev)
                sc->xl_fhandle = rman_get_bushandle(sc->xl_fres);
        }
 
+       /* Allocate interrupt */
        rid = 0;
        sc->xl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
            RF_SHAREABLE | RF_ACTIVE);
-
        if (sc->xl_irq == NULL) {
                printf("xl%d: couldn't map interrupt\n", unit);
-               if (sc->xl_fres != NULL)
-                       bus_release_resource(dev, SYS_RES_MEMORY,
-                           XL_PCI_FUNCMEM, sc->xl_fres);
-               bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
                error = ENXIO;
                goto fail;
        }
 
-       error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET,
-           xl_intr, sc, &sc->xl_intrhand);
-
-       if (error) {
-               bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
-               if (sc->xl_fres != NULL)
-                       bus_release_resource(dev, SYS_RES_MEMORY,
-                           XL_PCI_FUNCMEM, sc->xl_fres);
-               bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
-               printf("xl%d: couldn't set up irq\n", unit);
-               goto fail;
-       }
-
        /* Reset the adapter. */
        xl_reset(sc);
 
@@ -1437,12 +1469,6 @@ xl_attach(dev)
         */
        if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) {
                printf("xl%d: failed to read station address\n", sc->xl_unit);
-               bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
-               bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
-               if (sc->xl_fres != NULL)
-                       bus_release_resource(dev, SYS_RES_MEMORY,
-                           XL_PCI_FUNCMEM, sc->xl_fres);
-               bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
                error = ENXIO;
                goto fail;
        }
@@ -1456,30 +1482,110 @@ xl_attach(dev)
        callout_handle_init(&sc->xl_stat_ch);
        bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
 
-       sc->xl_ldata = contigmalloc(sizeof(struct xl_list_data), M_DEVBUF,
-           M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
+       /*
+        * Now allocate a tag for the DMA descriptor lists and a chunk
+        * of DMA-able memory based on the tag.  Also obtain the DMA
+        * addresses of the RX and TX ring, which we'll need later.
+        * All of our lists are allocated as a contiguous block
+        * of memory.
+        */
+       error = bus_dma_tag_create(NULL, 8, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+           XL_RX_LIST_SZ, 1, XL_RX_LIST_SZ, 0, 
+           &sc->xl_ldata.xl_rx_tag);
+       if (error) {
+               printf("xl%d: failed to allocate rx dma tag\n", unit);
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(sc->xl_ldata.xl_rx_tag,
+           (void **)&sc->xl_ldata.xl_rx_list, BUS_DMA_NOWAIT,
+           &sc->xl_ldata.xl_rx_dmamap);
+       if (error) {
+               printf("xl%d: no memory for rx list buffers!\n", unit);
+               bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
+               sc->xl_ldata.xl_rx_tag = NULL;
+               goto fail;
+       }
+
+       error = bus_dmamap_load(sc->xl_ldata.xl_rx_tag,
+           sc->xl_ldata.xl_rx_dmamap, sc->xl_ldata.xl_rx_list,
+           XL_RX_LIST_SZ, xl_dma_map_addr,
+           &sc->xl_ldata.xl_rx_dmaaddr, BUS_DMA_NOWAIT);
+       if (error) {
+               printf("xl%d: cannot get dma address of the rx ring!\n", unit);
+               bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
+                   sc->xl_ldata.xl_rx_dmamap);
+               bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
+               sc->xl_ldata.xl_rx_tag = NULL;
+               goto fail;
+       }
+
+       error = bus_dma_tag_create(NULL, 8, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+           XL_TX_LIST_SZ, 1, XL_TX_LIST_SZ, 0, 
+           &sc->xl_ldata.xl_tx_tag);
+       if (error) {
+               printf("xl%d: failed to allocate tx dma tag\n", unit);
+               goto fail;
+       }
 
-       if (sc->xl_ldata == NULL) {
+       error = bus_dmamem_alloc(sc->xl_ldata.xl_tx_tag,
+           (void **)&sc->xl_ldata.xl_tx_list, BUS_DMA_NOWAIT,
+           &sc->xl_ldata.xl_tx_dmamap);
+       if (error) {
                printf("xl%d: no memory for list buffers!\n", unit);
-               bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
-               bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
-               if (sc->xl_fres != NULL)
-                       bus_release_resource(dev, SYS_RES_MEMORY,
-                           XL_PCI_FUNCMEM, sc->xl_fres);
-               bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
-               error = ENXIO;
+               bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
+               sc->xl_ldata.xl_tx_tag = NULL;
+               goto fail;
+       }
+
+       error = bus_dmamap_load(sc->xl_ldata.xl_tx_tag,
+           sc->xl_ldata.xl_tx_dmamap, sc->xl_ldata.xl_tx_list,
+           XL_TX_LIST_SZ, xl_dma_map_addr,
+           &sc->xl_ldata.xl_tx_dmaaddr, BUS_DMA_NOWAIT);
+       if (error) {
+               printf("xl%d: cannot get dma address of the tx ring!\n", unit);
+               bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
+                   sc->xl_ldata.xl_tx_dmamap);
+               bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
+               sc->xl_ldata.xl_tx_tag = NULL;
+               goto fail;
+       }
+
+       /*
+        * Allocate a DMA tag for the mapping of mbufs.
+        */
+       error = bus_dma_tag_create(NULL, 1, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+           MCLBYTES * XL_MAXFRAGS, XL_MAXFRAGS, MCLBYTES, 0,
+           &sc->xl_mtag);
+       if (error) {
+               printf("xl%d: failed to allocate mbuf dma tag\n", unit);
                goto fail;
        }
 
-       bzero(sc->xl_ldata, sizeof(struct xl_list_data));
+       bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ);
+       bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ);
+
+       /* We need a spare DMA map for the RX ring. */
+       error = bus_dmamap_create(sc->xl_mtag, 0, &sc->xl_tmpmap);
+       if (error)
+               goto fail;
 
        /*
         * Figure out the card type. 3c905B adapters have the
         * 'supportsNoTxLength' bit set in the capabilities
         * word in the EEPROM.
+        * Note: my 3c575C cardbus card lies. It returns a value
+        * of 0x1578 for its capabilities word, which is somewhat
+        * nonsensical. Another way to distinguish a 3c90x chip
+        * from a 3c90xB/C chip is to check for the 'supportsLargePackets'
+        * bit. This will only be set for 3c90x boomerage chips.
         */
        xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
-       if (sc->xl_caps & XL_CAPS_NO_TXLENGTH)
+       if (sc->xl_caps & XL_CAPS_NO_TXLENGTH ||
+           !(sc->xl_caps & XL_CAPS_LARGE_PKTS))
                sc->xl_type = XL_TYPE_905B;
        else
                sc->xl_type = XL_TYPE_90X;
@@ -1491,12 +1597,14 @@ xl_attach(dev)
        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
        ifp->if_ioctl = xl_ioctl;
        ifp->if_output = ether_output;
+       ifp->if_capabilities = 0;
        if (sc->xl_type == XL_TYPE_905B) {
                ifp->if_start = xl_start_90xB;
                ifp->if_hwassist = XL905B_CSUM_FEATURES;
-               ifp->if_capabilities = IFCAP_HWCSUM;
-       } else
+               ifp->if_capabilities |= IFCAP_HWCSUM;
+       } else {
                ifp->if_start = xl_start;
+       }
        ifp->if_watchdog = xl_watchdog;
        ifp->if_init = xl_init;
        ifp->if_baudrate = 10000000;
@@ -1514,7 +1622,8 @@ xl_attach(dev)
                printf("xl%d: media options word: %x\n", sc->xl_unit,
                                                         sc->xl_media);
 
-       xl_read_eeprom(sc, (char *)&sc->xl_xcvr, XL_EE_ICFG_0, 2, 0);
+       xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
+       sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
        sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
        sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
 
@@ -1528,11 +1637,6 @@ xl_attach(dev)
                if (mii_phy_probe(dev, &sc->xl_miibus,
                    xl_ifmedia_upd, xl_ifmedia_sts)) {
                        printf("xl%d: no PHY found!\n", sc->xl_unit);
-                       bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
-                       bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
-                       bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
-                       contigfree(sc->xl_ldata,
-                           sizeof(struct xl_list_data), M_DEVBUF);
                        error = ENXIO;
                        goto fail;
                }
@@ -1551,9 +1655,6 @@ xl_attach(dev)
        /*
         * Do ifmedia setup.
         */
-
-       ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts);
-
        if (sc->xl_media & XL_MEDIAOPT_BT) {
                if (bootverbose)
                        printf("xl%d: found 10baseT\n", sc->xl_unit);
@@ -1652,17 +1753,43 @@ done:
         */
        ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
 
+        /*
+         * Tell the upper layer(s) we support long frames.
+         */
+        ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+
+       /* Hook interrupt last to avoid having to lock softc */
+       error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET,
+           xl_intr, sc, &sc->xl_intrhand);
+       if (error) {
+               printf("xl%d: couldn't set up irq\n", unit);
+               ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+               goto fail;
+       }
+
 fail:
+       if (error)
+               xl_detach(dev);
+
        splx(s);
+
        return(error);
 }
 
+/*
+ * Shutdown hardware and free up resources. This can be called any
+ * time after the mutex has been initialized. It is called in both
+ * the error case in attach and the normal detach case so it needs
+ * to be careful about only freeing resources that have actually been
+ * allocated.
+ */
 static int
 xl_detach(dev)
        device_t                dev;
 {
        struct xl_softc         *sc;
        struct ifnet            *ifp;
+       int                     rid, res;
        int                     s;
 
        s = splimp();
@@ -1670,25 +1797,51 @@ xl_detach(dev)
        sc = device_get_softc(dev);
        ifp = &sc->arpcom.ac_if;
 
+       if (sc->xl_flags & XL_FLAG_USE_MMIO) {
+               rid = XL_PCI_LOMEM;  
+               res = SYS_RES_MEMORY;
+       } else {
+               rid = XL_PCI_LOIO;   
+               res = SYS_RES_IOPORT;
+       }
+
        xl_reset(sc);
        xl_stop(sc);
        ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
-
-       /* Delete any miibus and phy devices attached to this interface */
-       if (sc->xl_miibus != NULL) {
-               bus_generic_detach(dev);
+       
+       if (sc->xl_miibus)
                device_delete_child(dev, sc->xl_miibus);
-       }
+       bus_generic_detach(dev);
+       ifmedia_removeall(&sc->ifmedia);
 
-       bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
-       bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
+       if (sc->xl_intrhand)
+               bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
+       if (sc->xl_irq)
+               bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
        if (sc->xl_fres != NULL)
                bus_release_resource(dev, SYS_RES_MEMORY,
                    XL_PCI_FUNCMEM, sc->xl_fres);
-       bus_release_resource(dev, XL_RES, XL_RID, sc->xl_res);
+       if (sc->xl_res)
+               bus_release_resource(dev, res, rid, sc->xl_res);
 
-       ifmedia_removeall(&sc->ifmedia);
-       contigfree(sc->xl_ldata, sizeof(struct xl_list_data), M_DEVBUF);
+       if (sc->xl_mtag) {
+               bus_dmamap_destroy(sc->xl_mtag, sc->xl_tmpmap);
+               bus_dma_tag_destroy(sc->xl_mtag);
+       }
+       if (sc->xl_ldata.xl_rx_tag) {
+               bus_dmamap_unload(sc->xl_ldata.xl_rx_tag,
+                   sc->xl_ldata.xl_rx_dmamap);
+               bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
+                   sc->xl_ldata.xl_rx_dmamap);
+               bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
+       }
+       if (sc->xl_ldata.xl_tx_tag) {
+               bus_dmamap_unload(sc->xl_ldata.xl_tx_tag,
+                   sc->xl_ldata.xl_tx_dmamap);
+               bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
+                   sc->xl_ldata.xl_tx_dmamap);
+               bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
+       }
 
        splx(s);
 
@@ -1704,12 +1857,18 @@ xl_list_tx_init(sc)
 {
        struct xl_chain_data    *cd;
        struct xl_list_data     *ld;
-       int                     i;
+       int                     error, i;
 
        cd = &sc->xl_cdata;
-       ld = sc->xl_ldata;
+       ld = &sc->xl_ldata;
        for (i = 0; i < XL_TX_LIST_CNT; i++) {
                cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
+               error = bus_dmamap_create(sc->xl_mtag, 0,
+                   &cd->xl_tx_chain[i].xl_map);
+               if (error)
+                       return(error);
+               cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
+                   i * sizeof(struct xl_list);
                if (i == (XL_TX_LIST_CNT - 1))
                        cd->xl_tx_chain[i].xl_next = NULL;
                else
@@ -1719,24 +1878,31 @@ xl_list_tx_init(sc)
        cd->xl_tx_free = &cd->xl_tx_chain[0];
        cd->xl_tx_tail = cd->xl_tx_head = NULL;
 
+       bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
        return(0);
 }
 
 /*
  * Initialize the transmit descriptors.
  */
-static int xl_list_tx_init_90xB(sc)
+static int
+xl_list_tx_init_90xB(sc)
        struct xl_softc         *sc;
 {
        struct xl_chain_data    *cd;
        struct xl_list_data     *ld;
-       int                     i;
+       int                     error, i;
 
        cd = &sc->xl_cdata;
-       ld = sc->xl_ldata;
+       ld = &sc->xl_ldata;
        for (i = 0; i < XL_TX_LIST_CNT; i++) {
                cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
-               cd->xl_tx_chain[i].xl_phys = vtophys(&ld->xl_tx_list[i]);
+               error = bus_dmamap_create(sc->xl_mtag, 0,
+                   &cd->xl_tx_chain[i].xl_map);
+               if (error)
+                       return(error);
+               cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
+                   i * sizeof(struct xl_list);
                if (i == (XL_TX_LIST_CNT - 1))
                        cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0];
                else
@@ -1749,14 +1915,14 @@ static int xl_list_tx_init_90xB(sc)
                            &cd->xl_tx_chain[i - 1];
        }
 
-       bzero((char *)ld->xl_tx_list,
-           sizeof(struct xl_list) * XL_TX_LIST_CNT);
-       ld->xl_tx_list[0].xl_status = XL_TXSTAT_EMPTY;
+       bzero(ld->xl_tx_list, XL_TX_LIST_SZ);
+       ld->xl_tx_list[0].xl_status = htole32(XL_TXSTAT_EMPTY);
 
        cd->xl_tx_prod = 1;
        cd->xl_tx_cons = 1;
        cd->xl_tx_cnt = 0;
 
+       bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
        return(0);
 }
 
@@ -1771,27 +1937,32 @@ xl_list_rx_init(sc)
 {
        struct xl_chain_data    *cd;
        struct xl_list_data     *ld;
-       int                     i;
+       int                     error, i, next;
+       u_int32_t               nextptr;
 
        cd = &sc->xl_cdata;
-       ld = sc->xl_ldata;
+       ld = &sc->xl_ldata;
 
        for (i = 0; i < XL_RX_LIST_CNT; i++) {
-               cd->xl_rx_chain[i].xl_ptr =
-                       (struct xl_list_onefrag *)&ld->xl_rx_list[i];
-               if (xl_newbuf(sc, &cd->xl_rx_chain[i]) == ENOBUFS)
-                       return(ENOBUFS);
-               if (i == (XL_RX_LIST_CNT - 1)) {
-                       cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[0];
-                       ld->xl_rx_list[i].xl_next =
-                           vtophys(&ld->xl_rx_list[0]);
-               } else {
-                       cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[i + 1];
-                       ld->xl_rx_list[i].xl_next =
-                           vtophys(&ld->xl_rx_list[i + 1]);
-               }
+               cd->xl_rx_chain[i].xl_ptr = &ld->xl_rx_list[i];
+               error = bus_dmamap_create(sc->xl_mtag, 0,
+                   &cd->xl_rx_chain[i].xl_map);
+               if (error)
+                       return(error);
+               error = xl_newbuf(sc, &cd->xl_rx_chain[i]);
+               if (error)
+                       return(error);
+               if (i == (XL_RX_LIST_CNT - 1))
+                       next = 0;
+               else
+                       next = i + 1;
+               nextptr = ld->xl_rx_dmaaddr +
+                   next * sizeof(struct xl_list_onefrag);
+               cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[next];
+               ld->xl_rx_list[i].xl_next = htole32(nextptr);
        }
 
+       bus_dmamap_sync(ld->xl_rx_tag, ld->xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
        cd->xl_rx_head = &cd->xl_rx_chain[0];
 
        return(0);
@@ -1799,6 +1970,8 @@ xl_list_rx_init(sc)
 
 /*
  * Initialize an RX descriptor and attach an MBUF cluster.
+ * If we fail to do so, we need to leave the old mbuf and
+ * the old DMA map untouched so that it can be reused.
  */
 static int
 xl_newbuf(sc, c)
@@ -1806,27 +1979,36 @@ xl_newbuf(sc, c)
        struct xl_chain_onefrag *c;
 {
        struct mbuf             *m_new = NULL;
+       bus_dmamap_t            map;
+       int                     error;
+       u_int32_t               baddr;
 
-       MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+       m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
        if (m_new == NULL)
                return(ENOBUFS);
 
-       MCLGET(m_new, M_DONTWAIT);
-       if (!(m_new->m_flags & M_EXT)) {
-               m_freem(m_new);
-               return(ENOBUFS);
-       }
-
        m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
 
        /* Force longword alignment for packet payload. */
        m_adj(m_new, ETHER_ALIGN);
 
+       error = bus_dmamap_load_mbuf(sc->xl_mtag, sc->xl_tmpmap, m_new,
+           xl_dma_map_rxbuf, &baddr, BUS_DMA_NOWAIT);
+       if (error) {
+               m_freem(m_new);
+               printf("xl%d: can't map mbuf (error %d)\n", sc->xl_unit, error);
+               return(error);
+       }
+
+       bus_dmamap_unload(sc->xl_mtag, c->xl_map);
+       map = c->xl_map;
+       c->xl_map = sc->xl_tmpmap;
+       sc->xl_tmpmap = map;
        c->xl_mbuf = m_new;
-       c->xl_ptr->xl_frag.xl_addr = vtophys(mtod(m_new, caddr_t));
-       c->xl_ptr->xl_frag.xl_len = MCLBYTES | XL_LAST_FRAG;
+       c->xl_ptr->xl_frag.xl_len = htole32(m_new->m_len | XL_LAST_FRAG);
        c->xl_ptr->xl_status = 0;
-
+       c->xl_ptr->xl_frag.xl_addr = htole32(baddr);
+       bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREREAD);
        return(0);
 }
 
@@ -1861,7 +2043,7 @@ static void
 xl_rxeof(sc)
        struct xl_softc         *sc;
 {
-        struct ether_header    *eh;
+       struct ether_header     *eh;
         struct mbuf            *m;
         struct ifnet           *ifp;
        struct xl_chain_onefrag *cur_rx;
@@ -1872,9 +2054,21 @@ xl_rxeof(sc)
 
 again:
 
-       while((rxstat = sc->xl_cdata.xl_rx_head->xl_ptr->xl_status)) {
+       bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap,
+           BUS_DMASYNC_POSTREAD);
+       while((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) {
                cur_rx = sc->xl_cdata.xl_rx_head;
                sc->xl_cdata.xl_rx_head = cur_rx->xl_next;
+               total_len = rxstat & XL_RXSTAT_LENMASK;
+
+               /*
+                * Since we have told the chip to allow large frames,
+                * we need to trap giant frame errors in software. We allow
+                * a little more than the normal frame size to account for
+                * frames with VLAN tags.
+                */
+               if (total_len > XL_MAX_FRAMELEN)
+                       rxstat |= (XL_RXSTAT_UP_ERROR|XL_RXSTAT_OVERSIZE);
 
                /*
                 * If an error occurs, update stats, clear the
@@ -1885,25 +2079,30 @@ again:
                if (rxstat & XL_RXSTAT_UP_ERROR) {
                        ifp->if_ierrors++;
                        cur_rx->xl_ptr->xl_status = 0;
+                       bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
+                           sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
                        continue;
                }
 
                /*
-                * If there error bit was not set, the upload complete
+                * If the error bit was not set, the upload complete
                 * bit should be set which means we have a valid packet.
                 * If not, something truly strange has happened.
                 */
                if (!(rxstat & XL_RXSTAT_UP_CMPLT)) {
                        printf("xl%d: bad receive status -- "
-                           "packet dropped", sc->xl_unit);
+                           "packet dropped\n", sc->xl_unit);
                        ifp->if_ierrors++;
                        cur_rx->xl_ptr->xl_status = 0;
+                       bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
+                           sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
                        continue;
                }
 
                /* No errors; receive the packet. */    
+               bus_dmamap_sync(sc->xl_mtag, cur_rx->xl_map,
+                   BUS_DMASYNC_POSTREAD);
                m = cur_rx->xl_mbuf;
-               total_len = cur_rx->xl_ptr->xl_status & XL_RXSTAT_LENMASK;
 
                /*
                 * Try to conjure up a new mbuf cluster. If that
@@ -1912,11 +2111,15 @@ again:
                 * result in a lost packet, but there's little else we
                 * can do in this situation.
                 */
-               if (xl_newbuf(sc, cur_rx) == ENOBUFS) {
+               if (xl_newbuf(sc, cur_rx)) {
                        ifp->if_ierrors++;
                        cur_rx->xl_ptr->xl_status = 0;
+                       bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
+                           sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
                        continue;
                }
+               bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
+                   sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
 
                ifp->if_ipackets++;
                eh = mtod(m, struct ether_header *);
@@ -1926,7 +2129,7 @@ again:
                /* Remove header from mbuf and pass it on. */
                m_adj(m, sizeof(struct ether_header));
 
-               if (sc->xl_type == XL_TYPE_905B) {
+               if (ifp->if_capenable & IFCAP_RXCSUM) {
                        /* Do IP checksum checking. */
                        if (rxstat & XL_RXSTAT_IPCKOK)
                                m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
@@ -1941,6 +2144,7 @@ again:
                                m->m_pkthdr.csum_data = 0xffff;
                        }
                }
+
                ether_input(ifp, eh, m);
        }
 
@@ -1960,8 +2164,7 @@ again:
                CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) {
                CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
                xl_wait(sc);
-               CSR_WRITE_4(sc, XL_UPLIST_PTR,
-                       vtophys(&sc->xl_ldata->xl_rx_list[0]));
+               CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
                sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0];
                CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
                goto again;
@@ -2002,6 +2205,9 @@ xl_txeof(sc)
                        break;
 
                sc->xl_cdata.xl_tx_head = cur_tx->xl_next;
+               bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
                m_freem(cur_tx->xl_mbuf);
                cur_tx->xl_mbuf = NULL;
                ifp->if_opackets++;
@@ -2017,7 +2223,7 @@ xl_txeof(sc)
                if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED ||
                        !CSR_READ_4(sc, XL_DOWNLIST_PTR)) {
                        CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
-                               vtophys(sc->xl_cdata.xl_tx_head->xl_ptr));
+                               sc->xl_cdata.xl_tx_head->xl_phys);
                        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
                }
        }
@@ -2025,7 +2231,8 @@ xl_txeof(sc)
        return;
 }
 
-static void xl_txeof_90xB(sc)
+static void
+xl_txeof_90xB(sc)
        struct xl_softc         *sc;
 {
        struct xl_chain         *cur_tx = NULL;
@@ -2034,15 +2241,21 @@ static void xl_txeof_90xB(sc)
 
        ifp = &sc->arpcom.ac_if;
 
+       bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
+           BUS_DMASYNC_POSTREAD);
        idx = sc->xl_cdata.xl_tx_cons;
        while(idx != sc->xl_cdata.xl_tx_prod) {
 
                cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
 
-               if (!(cur_tx->xl_ptr->xl_status & XL_TXSTAT_DL_COMPLETE))
+               if (!(le32toh(cur_tx->xl_ptr->xl_status) &
+                     XL_TXSTAT_DL_COMPLETE))
                        break;
 
                if (cur_tx->xl_mbuf != NULL) {
+                       bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
+                           BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
                        m_freem(cur_tx->xl_mbuf);
                        cur_tx->xl_mbuf = NULL;
                }
@@ -2094,7 +2307,7 @@ xl_txeoc(sc)
                        } else {
                                if (sc->xl_cdata.xl_tx_head != NULL)
                                        CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
-                               vtophys(sc->xl_cdata.xl_tx_head->xl_ptr));
+                                           sc->xl_cdata.xl_tx_head->xl_phys);
                        }
                        /*
                         * Remember to set this for the
@@ -2249,29 +2462,24 @@ xl_encap(sc, c, m_head)
        struct xl_chain         *c;
        struct mbuf             *m_head;
 {
-       int                     frag;
-       struct xl_frag          *f = NULL;
-       int                     total_len;
-       struct mbuf             *m;
+       int                     error;
+       u_int32_t               status;
+       struct ifnet            *ifp;
+
+       ifp = &sc->arpcom.ac_if;
 
        /*
         * Start packing the mbufs in this chain into
         * the fragment pointers. Stop when we run out
         * of fragments or hit the end of the mbuf chain.
         */
-       m = m_head;
-       total_len = 0;
+       error = bus_dmamap_load_mbuf(sc->xl_mtag, c->xl_map, m_head,
+           xl_dma_map_txbuf, c->xl_ptr, BUS_DMA_NOWAIT);
 
-       for (m = m_head, frag = 0; m != NULL; m = m->m_next) {
-               if (m->m_len != 0) {
-                       if (frag == XL_MAXFRAGS)
-                               break;
-                       total_len += m->m_len;
-                       f = &c->xl_ptr->xl_frag[frag];
-                       f->xl_addr = vtophys(mtod(m, vm_offset_t));
-                       f->xl_len = m->m_len;
-                       frag++;
-               }
+       if (error && error != EFBIG) {
+               m_freem(m_head);
+               printf("xl%d: can't map mbuf (error %d)\n", sc->xl_unit, error);
+               return(1);
        }
 
        /*
@@ -2282,51 +2490,43 @@ xl_encap(sc, c, m_head)
         * pointers/counters; it wouldn't gain us anything,
         * and would waste cycles.
         */
-       if (m != NULL) {
-               struct mbuf             *m_new = NULL;
+       if (error) {
+               struct mbuf             *m_new;
 
-               MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+               m_new = m_defrag(m_head, M_DONTWAIT);
                if (m_new == NULL) {
-                       printf("xl%d: no memory for tx list", sc->xl_unit);
+                       m_freem(m_head);
                        return(1);
+               } else {
+                       m_head = m_new;
                }
-               if (m_head->m_pkthdr.len > MHLEN) {
-                       MCLGET(m_new, M_DONTWAIT);
-                       if (!(m_new->m_flags & M_EXT)) {
-                               m_freem(m_new);
-                               printf("xl%d: no memory for tx list",
-                                               sc->xl_unit);
-                               return(1);
-                       }
+
+               error = bus_dmamap_load_mbuf(sc->xl_mtag, c->xl_map,
+                       m_head, xl_dma_map_txbuf, c->xl_ptr, BUS_DMA_NOWAIT);
+               if (error) {
+                       m_freem(m_head);
+                       printf("xl%d: can't map mbuf (error %d)\n",
+                           sc->xl_unit, error);
+                       return(1);
                }
-               m_copydata(m_head, 0, m_head->m_pkthdr.len,     
-                                       mtod(m_new, caddr_t));
-               m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len;
-               m_freem(m_head);
-               m_head = m_new;
-               f = &c->xl_ptr->xl_frag[0];
-               f->xl_addr = vtophys(mtod(m_new, caddr_t));
-               f->xl_len = total_len = m_new->m_len;
        }
 
        if (sc->xl_type == XL_TYPE_905B) {
-               c->xl_ptr->xl_status = XL_TXSTAT_RND_DEFEAT;
+               status = XL_TXSTAT_RND_DEFEAT;
 
                if (m_head->m_pkthdr.csum_flags) {
                        if (m_head->m_pkthdr.csum_flags & CSUM_IP)
-                               c->xl_ptr->xl_status |= XL_TXSTAT_IPCKSUM;
+                               status |= XL_TXSTAT_IPCKSUM;
                        if (m_head->m_pkthdr.csum_flags & CSUM_TCP)
-                               c->xl_ptr->xl_status |= XL_TXSTAT_TCPCKSUM;
+                               status |= XL_TXSTAT_TCPCKSUM;
                        if (m_head->m_pkthdr.csum_flags & CSUM_UDP)
-                               c->xl_ptr->xl_status |= XL_TXSTAT_UDPCKSUM;
+                               status |= XL_TXSTAT_UDPCKSUM;
                }
-       } else
-               c->xl_ptr->xl_status = total_len;
+               c->xl_ptr->xl_status = htole32(status);
+       }
 
        c->xl_mbuf = m_head;
-       f->xl_len |= XL_LAST_FRAG;
-       c->xl_ptr->xl_next = 0;
-
+       bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREWRITE);
        return(0);
 }
 
@@ -2343,9 +2543,11 @@ xl_start(ifp)
        struct xl_softc         *sc;
        struct mbuf             *m_head = NULL;
        struct xl_chain         *prev = NULL, *cur_tx = NULL, *start_tx;
+       struct xl_chain         *prev_tx;
+       u_int32_t               status;
+       int                     error;
 
        sc = ifp->if_softc;
-
        /*
         * Check for an available queue slot. If there are none,
         * punt.
@@ -2367,18 +2569,23 @@ xl_start(ifp)
                        break;
 
                /* Pick a descriptor off the free list. */
+               prev_tx = cur_tx;
                cur_tx = sc->xl_cdata.xl_tx_free;
-               sc->xl_cdata.xl_tx_free = cur_tx->xl_next;
-
-               cur_tx->xl_next = NULL;
 
                /* Pack the data into the descriptor. */
-               xl_encap(sc, cur_tx, m_head);
+               error = xl_encap(sc, cur_tx, m_head);
+               if (error) {
+                       cur_tx = prev_tx;
+                       continue;
+               }
+
+               sc->xl_cdata.xl_tx_free = cur_tx->xl_next;
+               cur_tx->xl_next = NULL;
 
                /* Chain it together. */
                if (prev != NULL) {
                        prev->xl_next = cur_tx;
-                       prev->xl_ptr->xl_next = vtophys(cur_tx->xl_ptr);
+                       prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
                }
                prev = cur_tx;
 
@@ -2393,8 +2600,9 @@ xl_start(ifp)
        /*
         * If there are no packets queued, bail.
         */
-       if (cur_tx == NULL)
+       if (cur_tx == NULL) {
                return;
+       }
 
        /*
         * Place the request for the upload interrupt
@@ -2403,7 +2611,10 @@ xl_start(ifp)
         * get an interupt once for the whole chain rather than
         * once for each packet.
         */
-       cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR;
+       cur_tx->xl_ptr->xl_status = htole32(le32toh(cur_tx->xl_ptr->xl_status) |
+           XL_TXSTAT_DL_INTR);
+       bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
+           BUS_DMASYNC_PREWRITE);
 
        /*
         * Queue the packets. If the TX channel is clear, update
@@ -2415,16 +2626,17 @@ xl_start(ifp)
        if (sc->xl_cdata.xl_tx_head != NULL) {
                sc->xl_cdata.xl_tx_tail->xl_next = start_tx;
                sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next =
-                                       vtophys(start_tx->xl_ptr);
-               sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &=
-                                       ~XL_TXSTAT_DL_INTR;
+                   htole32(start_tx->xl_phys);
+               status = sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status;
+               sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status =
+                   htole32(le32toh(status) & ~XL_TXSTAT_DL_INTR);
                sc->xl_cdata.xl_tx_tail = cur_tx;
        } else {
                sc->xl_cdata.xl_tx_head = start_tx;
                sc->xl_cdata.xl_tx_tail = cur_tx;
        }
        if (!CSR_READ_4(sc, XL_DOWNLIST_PTR))
-               CSR_WRITE_4(sc, XL_DOWNLIST_PTR, vtophys(start_tx->xl_ptr));
+               CSR_WRITE_4(sc, XL_DOWNLIST_PTR, start_tx->xl_phys);
 
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
 
@@ -2455,18 +2667,21 @@ xl_start(ifp)
        return;
 }
 
-static void xl_start_90xB(ifp)
+static void
+xl_start_90xB(ifp)
        struct ifnet            *ifp;
 {
        struct xl_softc         *sc;
        struct mbuf             *m_head = NULL;
        struct xl_chain         *prev = NULL, *cur_tx = NULL, *start_tx;
-       int                     idx;
+       struct xl_chain         *prev_tx;
+       int                     error, idx;
 
        sc = ifp->if_softc;
 
-       if (ifp->if_flags & IFF_OACTIVE)
+       if (ifp->if_flags & IFF_OACTIVE) {
                return;
+       }
 
        idx = sc->xl_cdata.xl_tx_prod;
        start_tx = &sc->xl_cdata.xl_tx_chain[idx];
@@ -2482,14 +2697,19 @@ static void xl_start_90xB(ifp)
                if (m_head == NULL)
                        break;
 
+               prev_tx = cur_tx;
                cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
 
                /* Pack the data into the descriptor. */
-               xl_encap(sc, cur_tx, m_head);
+               error = xl_encap(sc, cur_tx, m_head);
+               if (error) {
+                       cur_tx = prev_tx;
+                       continue;
+               }
 
                /* Chain it together. */
                if (prev != NULL)
-                       prev->xl_ptr->xl_next = cur_tx->xl_phys;
+                       prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
                prev = cur_tx;
 
                /*
@@ -2506,8 +2726,9 @@ static void xl_start_90xB(ifp)
        /*
         * If there are no packets queued, bail.
         */
-       if (cur_tx == NULL)
+       if (cur_tx == NULL) {
                return;
+       }
 
        /*
         * Place the request for the upload interrupt
@@ -2516,11 +2737,14 @@ static void xl_start_90xB(ifp)
         * get an interupt once for the whole chain rather than
         * once for each packet.
         */
-       cur_tx->xl_ptr->xl_status |= XL_TXSTAT_DL_INTR;
+       cur_tx->xl_ptr->xl_status = htole32(le32toh(cur_tx->xl_ptr->xl_status) |
+           XL_TXSTAT_DL_INTR);
+       bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
+           BUS_DMASYNC_PREWRITE);
 
        /* Start transmission */
        sc->xl_cdata.xl_tx_prod = idx;
-       start_tx->xl_prev->xl_ptr->xl_next = start_tx->xl_phys;
+       start_tx->xl_prev->xl_ptr->xl_next = htole32(start_tx->xl_phys);
 
        /*
         * Set a timeout in case the chip goes out to lunch.
@@ -2536,9 +2760,10 @@ xl_init(xsc)
 {
        struct xl_softc         *sc = xsc;
        struct ifnet            *ifp = &sc->arpcom.ac_if;
-       int                     s, i;
+       int                     error, i;
        u_int16_t               rxfilt = 0;
        struct mii_data         *mii = NULL;
+       int                     s;
 
        s = splimp();
 
@@ -2576,9 +2801,10 @@ xl_init(xsc)
        xl_wait(sc);
 #endif
        /* Init circular RX list. */
-       if (xl_list_rx_init(sc) == ENOBUFS) {
-               printf("xl%d: initialization failed: no "
-                       "memory for rx buffers\n", sc->xl_unit);
+       error = xl_list_rx_init(sc);
+       if (error) {
+               printf("xl%d: initialization of the rx ring failed (%d)\n",
+                   sc->xl_unit, error);
                xl_stop(sc);
                splx(s);
                return;
@@ -2586,9 +2812,15 @@ xl_init(xsc)
 
        /* Init TX descriptors. */
        if (sc->xl_type == XL_TYPE_905B)
-               xl_list_tx_init_90xB(sc);
+               error = xl_list_tx_init_90xB(sc);
        else
-               xl_list_tx_init(sc);
+               error = xl_list_tx_init(sc);
+       if (error) {
+               printf("xl%d: initialization of the tx ring failed (%d)\n",
+                   sc->xl_unit, error);
+               xl_stop(sc);
+               splx(s);
+       }
 
        /*
         * Set the TX freethresh value.
@@ -2663,7 +2895,7 @@ xl_init(xsc)
         */
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
        xl_wait(sc);
-       CSR_WRITE_4(sc, XL_UPLIST_PTR, vtophys(&sc->xl_ldata->xl_rx_list[0]));
+       CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
        xl_wait(sc);
 
@@ -2675,7 +2907,7 @@ xl_init(xsc)
                CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
                xl_wait(sc);
                CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
-                   vtophys(&sc->xl_ldata->xl_tx_list[0]));
+                   sc->xl_cdata.xl_tx_chain[0].xl_phys);
                CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
                xl_wait(sc);
        }
@@ -2690,9 +2922,22 @@ xl_init(xsc)
        else
                CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
 
-       /* increase packet size to allow reception of 802.1q or ISL packets */
-        if (sc->xl_type == XL_TYPE_905B) 
+       /*
+        * increase packet size to allow reception of 802.1q or ISL packets.
+        * For the 3c90x chip, set the 'allow large packets' bit in the MAC
+        * control register. For 3c90xB/C chips, use the RX packet size
+        * register.
+        */
+       
+       if (sc->xl_type == XL_TYPE_905B) 
                CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
+       else {
+               u_int8_t macctl;
+               macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
+               macctl |= XL_MACCTRL_ALLOW_LARGE_PACK;
+               CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
+       }
+
        /* Clear out the stats counters. */
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
        sc->xl_stats_no_timeout = 1;
@@ -2708,7 +2953,8 @@ xl_init(xsc)
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
-       if (sc->xl_flags & XL_FLAG_FUNCREG) bus_space_write_4 (sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
+       if (sc->xl_flags & XL_FLAG_FUNCREG)
+           bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
 
        /* Set the RX early threshold */
        CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
@@ -2729,10 +2975,10 @@ xl_init(xsc)
        ifp->if_flags |= IFF_RUNNING;
        ifp->if_flags &= ~IFF_OACTIVE;
 
-       (void)splx(s);
-
        sc->xl_stat_ch = timeout(xl_stats_update, sc, hz);
 
+       splx(s);
+
        return;
 }
 
@@ -2853,9 +3099,10 @@ xl_ioctl(ifp, command, data)
 {
        struct xl_softc         *sc = ifp->if_softc;
        struct ifreq            *ifr = (struct ifreq *) data;
-       int                     s, error = 0;
+       int                     error = 0;
        struct mii_data         *mii = NULL;
        u_int8_t                rxfilt;
+       int                     s;
 
        s = splimp();
 
@@ -2911,13 +3158,19 @@ xl_ioctl(ifp, command, data)
                        error = ifmedia_ioctl(ifp, ifr,
                            &mii->mii_media, command);
                break;
+        case SIOCSIFCAP:
+               ifp->if_capenable = ifr->ifr_reqcap;
+               if (ifp->if_capenable & IFCAP_TXCSUM)
+                       ifp->if_hwassist = XL905B_CSUM_FEATURES;
+               else
+                       ifp->if_hwassist = 0;
+               break;
        default:
                error = EINVAL;
                break;
        }
 
-       (void)splx(s);
-
+       splx(s);
        return(error);
 }
 
@@ -2993,23 +3246,29 @@ xl_stop(sc)
         */
        for (i = 0; i < XL_RX_LIST_CNT; i++) {
                if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) {
+                       bus_dmamap_unload(sc->xl_mtag,
+                           sc->xl_cdata.xl_rx_chain[i].xl_map);
+                       bus_dmamap_destroy(sc->xl_mtag,
+                           sc->xl_cdata.xl_rx_chain[i].xl_map);
                        m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf);
                        sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL;
                }
        }
-       bzero((char *)&sc->xl_ldata->xl_rx_list,
-               sizeof(sc->xl_ldata->xl_rx_list));
+       bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ);
        /*
         * Free the TX list buffers.
         */
        for (i = 0; i < XL_TX_LIST_CNT; i++) {
                if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) {
+                       bus_dmamap_unload(sc->xl_mtag,
+                           sc->xl_cdata.xl_tx_chain[i].xl_map);
+                       bus_dmamap_destroy(sc->xl_mtag,
+                           sc->xl_cdata.xl_tx_chain[i].xl_map);
                        m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf);
                        sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL;
                }
        }
-       bzero((char *)&sc->xl_ldata->xl_tx_list,
-               sizeof(sc->xl_ldata->xl_tx_list));
+       bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ);
 
        ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 
@@ -3041,10 +3300,12 @@ xl_suspend(dev)
        struct xl_softc         *sc;
        int                     s;
 
+       s = splimp();
+
        sc = device_get_softc(dev);
 
-       s = splimp();
        xl_stop(sc);
+
        splx(s);
 
        return(0);
@@ -3059,6 +3320,7 @@ xl_resume(dev)
        int                     s;
 
        s = splimp();
+
        sc = device_get_softc(dev);
        ifp = &sc->arpcom.ac_if;