]> Zhao Yanbai Git Server - minix.git/commitdiff
Driver for Attansic L2 FastEthernet (atl2)
authorDavid van Moolenbroek <david@minix3.org>
Wed, 30 Dec 2009 22:42:44 +0000 (22:42 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 30 Dec 2009 22:42:44 +0000 (22:42 +0000)
commands/scripts/netconf.sh
drivers/Makefile
drivers/atl2/Makefile [new file with mode: 0644]
drivers/atl2/atl2.c [new file with mode: 0644]
drivers/atl2/atl2.h [new file with mode: 0644]
drivers/pci/pci_table.c
etc/system.conf
etc/usr/rc

index e4c6c53be424786bf9d35a14198b3a62972d4407..0c7200a91aa312248aa523c4007ac305b02f19a7 100755 (executable)
@@ -110,7 +110,8 @@ cards()
     card 6 "NE2000, 3com 503 or WD based card (also emulated by Bochs)"
     card 7 "AMD LANCE (also emulated by VMWare and VirtualBox)" "1022:2000"
     card 8 "Intel PRO/1000 Gigabit" "8086:100E" "8086:107C" "8086:10CD"
-    card 9 "Different Ethernet card (no networking)"
+    card 9 "Attansic/Atheros L2 FastEthernet" "1969:2048"
+    card 10 "Different Ethernet card (no networking)"
 }
 
 warn()
@@ -157,7 +158,8 @@ drv_params()
                ;;
         7) driver="lance"; ;;    
        8) driver="e1000"; ;;
-        9) driver="psip0"; ;;    
+        9) driver="atl2";   ;;
+        10) driver="psip0"; ;;    
         *) warn "choose a number"
       esac
 }
index 4e3156125c4be60bb9d0972db57766a848565983..d8f34c8a9408e3a111452830c8035a9917ffa3d3 100644 (file)
@@ -27,6 +27,7 @@ all install depend clean:
        cd ./orinoco && $(MAKE) $@
        cd ./fxp && $(MAKE) $@
        cd ./dpeth && $(MAKE) $@
+       cd ./atl2 && $(MAKE) $@
        cd ./log && $(MAKE) $@
        cd ./bios_wini && $(MAKE) $@
        cd ./filter && $(MAKE) $@
diff --git a/drivers/atl2/Makefile b/drivers/atl2/Makefile
new file mode 100644 (file)
index 0000000..e97dfd4
--- /dev/null
@@ -0,0 +1,40 @@
+# Makefile for the Attansic/Atheros L2 ethernet driver (ATL2)
+DRIVER = atl2
+
+# directories
+u = /usr
+i = $u/include
+s = $i/sys
+m = $i/minix
+b = $i/ibm
+d = ..
+
+# programs, flags, etc.
+MAKE = exec make
+CC =   exec cc
+CFLAGS = -I$i $(CPROFILE)
+LDFLAGS = -i
+LIBS = -lsys
+
+OBJ = atl2.o
+
+# build local binary
+all build:     $(DRIVER)
+$(DRIVER):     $(OBJ) 
+       $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
+
+# install with other drivers
+install:       /usr/sbin/$(DRIVER)
+/usr/sbin/$(DRIVER):   $(DRIVER)
+       install -o root -cs $? $@
+
+# clean up local files
+clean:
+       rm -f $(DRIVER) *.o *.bak 
+
+depend: 
+       mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
+
diff --git a/drivers/atl2/atl2.c b/drivers/atl2/atl2.c
new file mode 100644 (file)
index 0000000..e57a898
--- /dev/null
@@ -0,0 +1,1343 @@
+/* Attansic/Atheros L2 FastEthernet driver, by D.C. van Moolenbroek */
+
+/* No documentation is available for this card. The FreeBSD driver is based
+ * heavily on the official Linux driver; this driver is based heavily on both.
+ */
+
+#include "../drivers.h"
+
+#include <signal.h>
+#include <sys/mman.h>
+#include <minix/ds.h>
+#include <minix/vm.h>
+#include <ibm/pci.h>
+#include <net/gen/ether.h>
+#include <net/gen/eth_io.h>
+#include <assert.h>
+
+#include "atl2.h"
+
+#define VERBOSE                0       /* Verbose debugging output */
+#define ATL2_FKEY      1       /* Register Shift+F11 for dumping statistics */
+
+#if VERBOSE
+#define ATL2_DEBUG(x) printf x
+#else
+#define ATL2_DEBUG(x)
+#endif
+
+typedef struct {
+       u32_t hdr;
+       u32_t vtag;
+       u8_t data[ATL2_RXD_SIZE - sizeof(u32_t) * 2];
+} rxd_t;
+
+PRIVATE struct {
+       int devind;             /* PCI device index */
+       int irq;                /* IRQ number */
+       int hook_id;            /* IRQ hook ID */
+       int mode;               /* datalink mode */
+       char *base;             /* base address of memory-mapped registers */
+       u32_t hwaddr[2];        /* MAC address, in register representation */
+
+       u8_t *txd_base;         /* local address of TxD ring buffer base */
+       u32_t *txs_base;        /* local address of TxS ring buffer base */
+       u8_t *rxd_base_u;       /* unaligned base address of RxD ring buffer */
+       rxd_t *rxd_base;        /* local address of RxD ring buffer base */
+
+       int rxd_align;          /* alignment offset of RxD ring buffer */
+
+       vir_bytes txd_phys;     /* physical address of TxD ring buffer */
+       vir_bytes txs_phys;     /* physical address of TxS ring buffer */
+       vir_bytes rxd_phys;     /* physical address of RxD ring buffer */
+
+       int txd_tail;           /* tail index into TxD, in bytes */
+       int txd_num;            /* head-tail offset into TxD, in bytes */
+       int txs_tail;           /* tail index into TxS, in elements */
+       int txs_num;            /* head-tail offset into TxS, in elements */
+       int rxd_tail;           /* tail index into RxD, in elements */
+
+       int flags;              /* state flags (ATL2_FLAG_) */
+       message read_msg;       /* suspended read request (READ_PEND) */
+       message write_msg;      /* suspended write request (WRITE_PEND) */
+       endpoint_t task_endpt;  /* requester endpoint (PACK_RCVD|PACK_SENT) */
+       size_t recv_count;      /* packet size (PACK_RCVD) */
+
+       eth_stat_t stat;        /* statistics */
+} state;
+
+#define ATL2_FLAG_RX_AVAIL     0x01    /* packet available for receipt */
+#define ATL2_FLAG_READ_PEND    0x02    /* read request pending */
+#define ATL2_FLAG_WRITE_PEND   0x04    /* write request pending */
+#define ATL2_FLAG_PACK_RCVD    0x08    /* packet received */
+#define ATL2_FLAG_PACK_SENT    0x10    /* packet transmitted */
+
+#define ATL2_READ_U8(off) (* (u8_t *) (state.base + (off)))
+#define ATL2_READ_U16(off) (* (u16_t *) (state.base + (off)))
+#define ATL2_READ_U32(off) (* (u32_t *) (state.base + (off)))
+#define ATL2_WRITE_U8(off, val) * (u8_t *) (state.base + (off)) = (val);
+#define ATL2_WRITE_U16(off, val) * (u16_t *) (state.base + (off)) = (val);
+#define ATL2_WRITE_U32(off, val) * (u32_t *) (state.base + (off)) = (val);
+
+#define ATL2_ALIGN_32(n) (((n) + 3) & ~3)
+
+PRIVATE iovec_s_t iovec[NR_IOREQS];
+
+PRIVATE struct {
+       u16_t vid;
+       u16_t did;
+} pcitab[] = {
+       { 0x1969, 0x2048 },     /* Attansic Technology Corp, L2 FastEthernet */
+       { 0x0000, 0x0000 }
+};
+
+/*===========================================================================*
+ *                             atl2_read_vpd                                *
+ *===========================================================================*/
+PRIVATE int atl2_read_vpd(int index, u32_t *res)
+{
+       /* Read a value from the VPD register area.
+        */
+       u32_t off, val;
+       int i;
+
+       ATL2_WRITE_U32(ATL2_VPD_DATA_REG, 0);
+
+       off = ATL2_VPD_REGBASE + index * sizeof(u32_t);
+
+       ATL2_WRITE_U32(ATL2_VPD_CAP_REG,
+               (off << ATL2_VPD_CAP_ADDR_SHIFT) & ATL2_VPD_CAP_ADDR_MASK);
+
+       for (i = 0; i < ATL2_VPD_NTRIES; i++) {
+               micro_delay(ATL2_VPD_DELAY);
+
+               val = ATL2_READ_U32(ATL2_VPD_CAP_REG);
+               if (val & ATL2_VPD_CAP_DONE)
+                       break;
+       }
+
+       if (i == ATL2_VPD_NTRIES) {
+               printf("ATL2: timeout reading EEPROM register %d\n", index);
+               return FALSE;
+       }
+
+       *res = ATL2_READ_U32(ATL2_VPD_DATA_REG);
+       return TRUE;
+}
+
+/*===========================================================================*
+ *                             atl2_get_vpd_hwaddr                          *
+ *===========================================================================*/
+PRIVATE int atl2_get_vpd_hwaddr(void)
+{
+       /* Read the MAC address from the EEPROM, using the Vital Product Data
+        * register interface.
+        */
+       u32_t key, val;
+       int i, n, found[2];
+
+       /* No idea, copied from FreeBSD which copied it from Linux. */
+       val = ATL2_READ_U32(ATL2_SPICTL_REG);
+       if (val & ATL2_SPICTL_VPD_EN) {
+               val &= ~ATL2_SPICTL_VPD_EN;
+               ATL2_WRITE_U32(ATL2_SPICTL_REG, val);
+       }
+
+       /* Is VPD supported? */
+#ifdef PCI_CAP_VPD     /* FIXME: just a guess at the future name */
+       if (!pci_find_cap(state.devind, PCI_CAP_VPD, &n))
+               return FALSE;
+#endif
+
+       /* Read out the set of key/value pairs. Look for the two parts that
+        * make up the MAC address.
+        */
+       found[0] = found[1] = FALSE;
+       for (i = 0; i < ATL2_VPD_NREGS; i += 2) {
+               if (!atl2_read_vpd(i, &key))
+                       break;
+
+               if ((key & ATL2_VPD_SIG_MASK) != ATL2_VPD_SIG)
+                       break;
+
+               key >>= ATL2_VPD_REG_SHIFT;
+
+               if (key != ATL2_HWADDR0_REG && key != ATL2_HWADDR1_REG)
+                       continue;
+
+               if (!atl2_read_vpd(i + 1, &val))
+                       break;
+
+               n = (key == ATL2_HWADDR1_REG);
+               state.hwaddr[n] = val;
+               found[n] = TRUE;
+
+               if (found[1 - n]) break;
+       }
+
+       return found[0] && found[1];
+}
+
+/*===========================================================================*
+ *                             atl2_get_hwaddr                              *
+ *===========================================================================*/
+PRIVATE void atl2_get_hwaddr(void)
+{
+       /* Get the MAC address of the card. First try the EEPROM; if that
+        * fails, just use whatever the card was already set to.
+        */
+
+       if (!atl2_get_vpd_hwaddr()) {
+               printf("ATL2: unable to read from VPD\n");
+
+               state.hwaddr[0] = ATL2_READ_U32(ATL2_HWADDR0_REG);
+               state.hwaddr[1] = ATL2_READ_U32(ATL2_HWADDR1_REG) & 0xffff;
+       }
+
+       ATL2_DEBUG(("ATL2: MAC address %04lx%08lx\n",
+               state.hwaddr[1], state.hwaddr[0]));
+}
+
+/*===========================================================================*
+ *                             atl2_read_mdio                               *
+ *===========================================================================*/
+PRIVATE int atl2_read_mdio(int addr, u16_t *res)
+{
+       /* Read a MII PHY register using MDIO.
+        */
+       u32_t rval;
+       int i;
+
+       rval = ((addr << ATL2_MDIO_ADDR_SHIFT) & ATL2_MDIO_ADDR_MASK) |
+               ATL2_MDIO_START | ATL2_MDIO_READ | ATL2_MDIO_SUP_PREAMBLE |
+               ATL2_MDIO_CLK_25_4;
+
+       ATL2_WRITE_U32(ATL2_MDIO_REG, rval);
+
+       for (i = 0; i < ATL2_MDIO_NTRIES; i++) {
+               micro_delay(ATL2_MDIO_DELAY);
+
+               rval = ATL2_READ_U32(ATL2_MDIO_REG);
+
+               if (!(rval & (ATL2_MDIO_START | ATL2_MDIO_BUSY)))
+                       break;
+       }
+
+       if (i == ATL2_MDIO_NTRIES) return FALSE;
+
+       *res = (u16_t) (rval & ATL2_MDIO_DATA_MASK);
+       return TRUE;
+}
+
+/*===========================================================================*
+ *                             atl2_alloc_dma                               *
+ *===========================================================================*/
+PRIVATE int atl2_alloc_dma(void)
+{
+       /* Allocate DMA ring buffers.
+        */
+
+       state.txd_base = alloc_contig(ATL2_TXD_BUFSIZE,
+               AC_ALIGN4K, &state.txd_phys);
+       state.txs_base = alloc_contig(ATL2_TXS_COUNT * sizeof(u32_t),
+               AC_ALIGN4K, &state.txs_phys);
+
+       /* The data buffer in each RxD descriptor must be 128-byte aligned.
+        * The two Tx buffers merely require a 4-byte start alignment.
+        */
+       state.rxd_align = 128 - offsetof(rxd_t, data);
+       state.rxd_base_u =
+               alloc_contig(state.rxd_align + ATL2_RXD_COUNT * ATL2_RXD_SIZE,
+               AC_ALIGN4K, &state.rxd_phys);
+
+       /* Unlike mmap, alloc_contig returns NULL on failure. */
+       if (!state.txd_base || !state.txs_base || !state.rxd_base_u)
+               return ENOMEM;
+
+       state.rxd_base = (rxd_t *) (state.rxd_base_u + state.rxd_align);
+       state.rxd_phys += state.rxd_align;
+
+       /* Zero out just in case. */
+       memset(state.txd_base, 0, ATL2_TXD_BUFSIZE);
+       memset(state.txs_base, 0, ATL2_TXS_COUNT * sizeof(u32_t));
+       memset(state.rxd_base, 0, ATL2_RXD_COUNT * ATL2_RXD_SIZE);
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             atl2_stop                                    *
+ *===========================================================================*/
+PRIVATE int atl2_stop(void)
+{
+       /* Stop the device.
+        */
+       u32_t val;
+       int i;
+
+       /* Clear and disable interrupts. */
+       ATL2_WRITE_U32(ATL2_IMR_REG, 0);
+       ATL2_WRITE_U32(ATL2_ISR_REG, 0xffffffff);
+
+       /* Stop Rx/Tx MACs. */
+       val = ATL2_READ_U32(ATL2_MAC_REG);
+       if (val & (ATL2_MAC_RX_EN | ATL2_MAC_TX_EN)) {
+               val &= ~(ATL2_MAC_RX_EN | ATL2_MAC_TX_EN);
+               ATL2_WRITE_U32(ATL2_MAC_REG, val);
+       }
+
+       ATL2_WRITE_U8(ATL2_DMAWRITE_REG, 0);
+       ATL2_WRITE_U8(ATL2_DMAREAD_REG, 0);
+
+       /* Wait until everything is idle. */
+       for (i = 0; i < ATL2_IDLE_NTRIES; i++) {
+               if (ATL2_READ_U32(ATL2_IDLE_REG) == 0)
+                       break;
+
+               micro_delay(ATL2_IDLE_DELAY);
+       }
+
+       /* The caller will generally ignore this return value. */
+       return (i < ATL2_IDLE_NTRIES);
+}
+
+/*===========================================================================*
+ *                             atl2_reset                                   *
+ *===========================================================================*/
+PRIVATE int atl2_reset(void)
+{
+       /* Reset the device to a known good state.
+        */
+       u32_t val;
+       int i;
+
+       /* Issue a soft reset, and wait for the device to respond. */
+       ATL2_WRITE_U32(ATL2_MASTER_REG, ATL2_MASTER_SOFT_RESET);
+
+       for (i = 0; i < ATL2_RESET_NTRIES; i++) {
+               val = ATL2_READ_U32(ATL2_MASTER_REG);
+               if (!(val & ATL2_MASTER_SOFT_RESET))
+                       break;
+
+               micro_delay(ATL2_RESET_DELAY);
+       }
+
+       if (i == ATL2_RESET_NTRIES)
+               return FALSE;
+
+       /* Wait until everything is idle. */
+       for (i = 0; i < ATL2_IDLE_NTRIES; i++) {
+               if (ATL2_READ_U32(ATL2_IDLE_REG) == 0)
+                       break;
+
+               micro_delay(ATL2_IDLE_DELAY);
+       }
+
+       return (i < ATL2_IDLE_NTRIES);
+}
+
+/*===========================================================================*
+ *                             atl2_set_mode                                *
+ *===========================================================================*/
+PRIVATE void atl2_set_mode(void)
+{
+       /* Reconfigure the device's promiscuity, multicast, and broadcast mode
+        * settings.
+        */
+       u32_t val;
+
+       val = ATL2_READ_U32(ATL2_MAC_REG);
+       val &= ~(ATL2_MAC_PROMISC_EN | ATL2_MAC_MCAST_EN | ATL2_MAC_BCAST_EN);
+
+       if (state.mode & DL_PROMISC_REQ)
+               val |= ATL2_MAC_PROMISC_EN;
+       if (state.mode & DL_MULTI_REQ)
+               val |= ATL2_MAC_MCAST_EN;
+       if (state.mode & DL_BROAD_REQ)
+               val |= ATL2_MAC_BCAST_EN;
+
+       ATL2_WRITE_U32(ATL2_MAC_REG, val);
+}
+
+/*===========================================================================*
+ *                             atl2_setup                                   *
+ *===========================================================================*/
+PRIVATE int atl2_setup(void)
+{
+       /* Set up the device for normal operation.
+        */
+       u32_t val;
+
+       atl2_stop();
+
+       if (!atl2_reset())
+               return FALSE;
+
+       /* Initialize PCIE module. Magic. */
+       ATL2_WRITE_U32(ATL2_LTSSM_TESTMODE_REG, ATL2_LTSSM_TESTMODE_DEFAULT);
+       ATL2_WRITE_U32(ATL2_DLL_TX_CTRL_REG, ATL2_DLL_TX_CTRL_DEFAULT);
+
+       /* Enable PHY. */
+       ATL2_WRITE_U32(ATL2_PHY_ENABLE_REG, ATL2_PHY_ENABLE);
+       micro_delay(1000);
+
+       /* Clear and disable interrupts. */
+       ATL2_WRITE_U32(ATL2_ISR_REG, 0xffffffff);
+
+       /* Set the MAC address. */
+       ATL2_WRITE_U32(ATL2_HWADDR0_REG, state.hwaddr[0]);
+       ATL2_WRITE_U32(ATL2_HWADDR1_REG, state.hwaddr[1]);
+
+       /* Initialize ring buffer addresses and sizes. */
+       ATL2_WRITE_U32(ATL2_DESC_ADDR_HI_REG, 0);       /* no 64 bit */
+       ATL2_WRITE_U32(ATL2_TXD_ADDR_LO_REG, state.txd_phys);
+       ATL2_WRITE_U32(ATL2_TXS_ADDR_LO_REG, state.txs_phys);
+       ATL2_WRITE_U32(ATL2_RXD_ADDR_LO_REG, state.rxd_phys);
+
+       ATL2_WRITE_U16(ATL2_RXD_COUNT_REG, ATL2_RXD_COUNT);
+       ATL2_WRITE_U16(ATL2_TXD_BUFSIZE_REG, ATL2_TXD_BUFSIZE / sizeof(u32_t));
+       ATL2_WRITE_U16(ATL2_TXS_COUNT_REG, ATL2_TXS_COUNT);
+
+       /* A whole lot of other initialization copied from Linux/FreeBSD. */
+       ATL2_WRITE_U32(ATL2_IFG_REG, ATL2_IFG_DEFAULT);
+
+       ATL2_WRITE_U32(ATL2_HDPX_REG, ATL2_HDPX_DEFAULT);
+
+       ATL2_WRITE_U16(ATL2_IMT_REG, ATL2_IMT_DEFAULT);
+       val = ATL2_READ_U32(ATL2_MASTER_REG);
+       ATL2_WRITE_U32(ATL2_MASTER_REG, val | ATL2_MASTER_IMT_EN);
+
+       ATL2_WRITE_U16(ATL2_ICT_REG, ATL2_ICT_DEFAULT);
+
+       ATL2_WRITE_U32(ATL2_CUT_THRESH_REG, ATL2_CUT_THRESH_DEFAULT);
+
+       ATL2_WRITE_U16(ATL2_FLOW_THRESH_HI_REG, (ATL2_RXD_COUNT / 8) * 7);
+       ATL2_WRITE_U16(ATL2_FLOW_THRESH_LO_REG, ATL2_RXD_COUNT / 12);
+
+       /* Set MTU. */
+       ATL2_WRITE_U16(ATL2_MTU_REG, ATL2_MTU_DEFAULT);
+
+       /* Reset descriptors, and enable DMA. */
+       state.txd_tail = state.txs_tail = state.rxd_tail = 0;
+       state.txd_num = state.txs_num = 0;
+       state.flags &= ~ATL2_FLAG_RX_AVAIL;
+       ATL2_WRITE_U16(ATL2_TXD_IDX_REG, 0);
+       ATL2_WRITE_U16(ATL2_RXD_IDX_REG, 0);
+
+       ATL2_WRITE_U8(ATL2_DMAREAD_REG, ATL2_DMAREAD_EN);
+       ATL2_WRITE_U8(ATL2_DMAWRITE_REG, ATL2_DMAWRITE_EN);
+
+       /* Did everything go alright? */
+       val = ATL2_READ_U32(ATL2_ISR_REG);
+       if (val & ATL2_ISR_PHY_LINKDOWN) {
+               printf("ATL2: initialization failed\n");
+               return FALSE;
+       }
+
+       /* Clear interrupt status. */
+       ATL2_WRITE_U32(ATL2_ISR_REG, 0x3fffffff);
+       ATL2_WRITE_U32(ATL2_ISR_REG, 0);
+
+       /* Enable interrupts. */
+       ATL2_WRITE_U32(ATL2_IMR_REG, ATL2_IMR_DEFAULT);
+
+       /* Configure MAC. */
+       ATL2_WRITE_U32(ATL2_MAC_REG, ATL2_MAC_DEFAULT);
+
+       /* Inet does not tell us about the multicast addresses that it is
+        * interested in, so we have to simply accept all multicast packets.
+        */
+       ATL2_WRITE_U32(ATL2_MHT0_REG, 0xffffffff);
+       ATL2_WRITE_U32(ATL2_MHT1_REG, 0xffffffff);
+
+       atl2_set_mode();
+
+       /* Enable Tx/Rx. */
+       val = ATL2_READ_U32(ATL2_MAC_REG);
+       ATL2_WRITE_U32(ATL2_MAC_REG, val | ATL2_MAC_TX_EN | ATL2_MAC_RX_EN);
+
+       return TRUE;
+}
+
+/*===========================================================================*
+ *                             atl2_probe                                   *
+ *===========================================================================*/
+PRIVATE int atl2_probe(int instance)
+{
+       /* Find a matching PCI device.
+        */
+       u16_t vid, did;
+       char *dname;
+       int i, r, devind, skip;
+
+       pci_init();
+
+       r = pci_first_dev(&devind, &vid, &did);
+       if (r <= 0)
+               return -1;
+
+       skip = 0;
+
+       for (;;) {
+               for (i = 0; pcitab[i].vid != 0; i++)
+                       if (pcitab[i].vid == vid && pcitab[i].did == did)
+                               break;
+
+               if (pcitab[i].vid != 0) {
+                       if (skip == instance) break;
+
+                       skip++;
+               }
+
+               r = pci_next_dev(&devind, &vid, &did);
+               if (r <= 0)
+                       return -1;
+       }
+
+       dname = pci_dev_name(vid, did);
+       ATL2_DEBUG(("ATL2: found %s (%x/%x) at %s\n",
+               dname ? dname : "<unknown>", vid, did,
+               pci_slot_name(devind)));
+
+       pci_reserve(devind);
+
+       return devind;
+}
+
+/*===========================================================================*
+ *                             atl2_init                                    *
+ *===========================================================================*/
+PRIVATE void atl2_init(int devind)
+{
+       /* Initialize the device.
+        */
+       u32_t bar;
+       int r;
+
+       /* Initialize global state. */
+       state.devind = devind;
+       state.mode = DL_NOMODE;
+       state.flags = 0;
+       state.recv_count = 0;
+
+       memset(&state.stat, 0, sizeof(state.stat));
+
+       /* FIXME: zero out the upper half of the 64-bit BAR. This is currently
+        * needed because the BIOS sets it to a nonzero value, and our PCI
+        * driver does not yet recognize 64-bit BARs at all. If either ever
+        * gets fixed, this will be a no-op, but for the time being, we simply
+        * hope that it will do the job.
+        */
+       pci_attr_w32(devind, PCI_BAR_2, 0);
+
+       bar = pci_attr_r32(devind, PCI_BAR) & 0xfffffff0;
+
+       /* FIXME: hardcoded length, as PCI doesn't expose the size, and it is
+        * not our job to compute the size from the BAR ourselves.
+        */
+       state.base = vm_map_phys(SELF, (void *) bar, ATL2_MMAP_SIZE);
+       if (state.base == MAP_FAILED)
+               panic("atl2", "unable to map in registers", NO_NUM);
+
+       if ((r = atl2_alloc_dma()) != OK)
+               panic("atl2", "unable to allocate DMA buffers", r);
+
+       state.irq = pci_attr_r8(devind, PCI_ILR);
+
+       if ((r = sys_irqsetpolicy(state.irq, 0, &state.hook_id)) != OK)
+               panic("atl2", "unable to register IRQ", r);
+
+       if (!atl2_reset())
+               panic("atl2", "unable to reset hardware", NO_NUM);
+
+       if ((r = sys_irqenable(&state.hook_id)) != OK)
+               panic("atl2", "unable to enable IRQ", r);
+
+       atl2_get_hwaddr();
+
+       atl2_setup();
+}
+
+/*===========================================================================*
+ *                             atl2_tx_stat                                 *
+ *===========================================================================*/
+PRIVATE void atl2_tx_stat(u32_t stat)
+{
+       /* Update statistics for packet transmission.
+        */
+
+       if (stat & ATL2_TXS_SUCCESS)
+               state.stat.ets_packetT++;
+       else
+               state.stat.ets_recvErr++;
+
+       if (stat & ATL2_TXS_DEFER)
+               state.stat.ets_transDef++;
+       if (stat & (ATL2_TXS_EXCDEFER | ATL2_TXS_ABORTCOL))
+               state.stat.ets_transAb++;
+       if (stat & ATL2_TXS_SINGLECOL)
+               state.stat.ets_collision++;
+       if (stat & ATL2_TXS_MULTICOL)
+               state.stat.ets_collision++;
+       if (stat & ATL2_TXS_LATECOL)
+               state.stat.ets_OWC++;
+       if (stat & ATL2_TXS_UNDERRUN)
+               state.stat.ets_fifoUnder++;
+}
+
+/*===========================================================================*
+ *                             atl2_rx_stat                                 *
+ *===========================================================================*/
+PRIVATE void atl2_rx_stat(u32_t stat)
+{
+       /* Update statistics for packet receipt.
+        */
+
+       if (stat & ATL2_RXD_SUCCESS)
+               state.stat.ets_packetR++;
+       else
+               state.stat.ets_recvErr++;
+
+       if (stat & ATL2_RXD_CRCERR)
+               state.stat.ets_CRCerr++;
+       if (stat & ATL2_RXD_FRAG)
+               state.stat.ets_collision++;
+       if (stat & ATL2_RXD_TRUNC)
+               state.stat.ets_fifoOver++;
+       if (stat & ATL2_RXD_ALIGN)
+               state.stat.ets_frameAll++;
+}
+
+/*===========================================================================*
+ *                             atl2_tx_advance                              *
+ *===========================================================================*/
+PRIVATE int atl2_tx_advance(void)
+{
+       /* Advance the TxD/TxS tails by as many sent packets as found.
+        */
+       u32_t stat, size, dsize;
+       int advanced;
+
+       advanced = FALSE;
+
+       while (state.txs_num > 0) {
+               /* Has the tail packet been processed by the driver? */
+               stat = state.txs_base[state.txs_tail];
+
+               if (!(stat & ATL2_TXS_UPDATE))
+                       break;
+
+               /* The packet size from the status must match the packet size
+                * we put in. If they don't, there's not much we can do..
+                */
+               size = stat & ATL2_TXS_SIZE_MASK;
+
+               assert(state.txd_tail <= ATL2_TXD_BUFSIZE - sizeof(u32_t));
+               dsize = * (u32_t *) (state.txd_base + state.txd_tail);
+               if (size != dsize)
+                       printf("ATL2: TxD/TxS size mismatch (%lx vs %lx)\n",
+                               size, dsize);
+
+               /* Advance tails accordingly. */
+               size = sizeof(u32_t) + ATL2_ALIGN_32(dsize);
+               assert(state.txd_num >= size);
+               state.txd_tail = (state.txd_tail + size) % ATL2_TXD_BUFSIZE;
+               state.txd_num -= size;
+
+               state.txs_tail = (state.txs_tail + 1) % ATL2_TXS_COUNT;
+               state.txs_num--;
+
+               if (stat & ATL2_TXS_SUCCESS)
+                       ATL2_DEBUG(("ATL2: successfully sent packet\n"));
+               else
+                       ATL2_DEBUG(("ATL2: failed to send packet\n"));
+
+               /* Update statistics. */
+               atl2_tx_stat(stat);
+
+               advanced = TRUE;
+       }
+
+       return advanced;
+}
+
+/*===========================================================================*
+ *                             atl2_rx_advance                              *
+ *===========================================================================*/
+PRIVATE void atl2_rx_advance(int next)
+{
+       /* Advance the RxD tail by as many failed receipts as possible, and
+        * see if there is an actual packet left to receive. If 'next' is set,
+        * the packet at the current tail has been processed.
+        */
+       int update_tail;
+       rxd_t *rxd;
+       u32_t hdr, size;
+
+       update_tail = FALSE;
+
+       if (next) {
+               state.rxd_tail = (state.rxd_tail + 1) % ATL2_RXD_COUNT;
+               update_tail = TRUE;
+
+               ATL2_DEBUG(("ATL2: successfully received packet\n"));
+
+               state.flags &= ~ATL2_FLAG_RX_AVAIL;
+       }
+
+       assert(!(state.flags & ATL2_FLAG_RX_AVAIL));
+
+       for (;;) {
+               /* Check the RxD tail for updates. */
+               rxd = &state.rxd_base[state.rxd_tail];
+
+               hdr = rxd->hdr;
+
+               if (!(hdr & ATL2_RXD_UPDATE))
+                       break;
+
+               rxd->hdr = hdr & ~(ATL2_RXD_UPDATE);
+
+               /* Update statistics. */
+               atl2_rx_stat(hdr);
+
+               /* Stop at the first successful receipt. The packet will be
+                * picked up by Inet later.
+                */
+               size = hdr & ATL2_RXD_SIZE_MASK;
+
+               if ((hdr & ATL2_RXD_SUCCESS) && size >= ETH_MIN_PACK_SIZE) {
+                       ATL2_DEBUG(("ATL2: packet available, size %ld\n",
+                               size));
+
+                       state.flags |= ATL2_FLAG_RX_AVAIL;
+                       break;
+               }
+
+               ATL2_DEBUG(("ATL2: packet receipt failed\n"));
+
+               /* Advance tail. */
+               state.rxd_tail = (state.rxd_tail + 1) % ATL2_RXD_COUNT;
+               update_tail = TRUE;
+       }
+
+       /* If new RxD descriptors are now up for reuse, tell the device. */
+       if (update_tail)
+               ATL2_WRITE_U32(ATL2_RXD_IDX_REG, state.rxd_tail);
+}
+
+/*===========================================================================*
+ *                             atl2_reply                                   *
+ *===========================================================================*/
+PRIVATE void atl2_reply(void)
+{
+       /* Send a task reply to Inet.
+        */
+       message m;
+       int r, stat;
+
+       stat = 0;
+       if (state.flags & ATL2_FLAG_PACK_SENT)
+               stat |= DL_PACK_SEND;
+       if (state.flags & ATL2_FLAG_PACK_RCVD)
+               stat |= DL_PACK_RECV;
+
+       m.m_type = DL_TASK_REPLY;
+       m.DL_PORT = 0;
+       m.DL_PROC = state.task_endpt;
+       m.DL_STAT = stat;
+       m.DL_COUNT = state.recv_count;
+
+       ATL2_DEBUG(("ATL2: sending reply stat %x count %d\n", stat,
+               m.DL_COUNT));
+
+       if ((r = send(state.task_endpt, &m)) != OK)
+               panic("atl2", "unable to reply", r);
+
+       state.flags &= ~(ATL2_FLAG_PACK_SENT | ATL2_FLAG_PACK_RCVD);
+       state.recv_count = 0;
+}
+
+/*===========================================================================*
+ *                             atl2_readv                                   *
+ *===========================================================================*/
+PRIVATE void atl2_readv(message *m, int from_int)
+{
+       /* Read packet data.
+        */
+       rxd_t *rxd;
+       iovec_s_t *iovp;
+       size_t count, off, left, size;
+       u8_t *pos;
+       int i, j, r, batch;
+
+       if (m->DL_PORT != 0) {
+               printf("ATL2: read from invalid port\n");
+
+               return;
+       }
+
+       /* We can deal with only one read request from Inet at a time. */
+       assert(from_int || !(state.flags & ATL2_FLAG_READ_PEND));
+
+       /* The exact semantics of DL_PROC are not clear. This driver takes it
+        * to be only the grant owner; other drivers treat it as the reply
+        * destination as well.
+        */
+       assert(m->m_source == m->DL_PROC);
+       state.task_endpt = m->m_source;
+
+       /* Are there any packets available at all? */
+       if (!(state.flags & ATL2_FLAG_RX_AVAIL))
+               goto suspend;
+
+       /* Get the first available packet's size. Cut off the CRC. */
+       rxd = &state.rxd_base[state.rxd_tail];
+
+       count = rxd->hdr & ATL2_RXD_SIZE_MASK;
+       count -= ETH_CRC_SIZE;
+
+       ATL2_DEBUG(("ATL2: readv: found packet with length %d\n", count));
+
+       /* Copy out the packet. */
+       off = 0;
+       left = count;
+       pos = rxd->data;
+
+       for (i = 0; i < m->DL_COUNT && left > 0; i += batch) {
+               /* Copy in the next batch. */
+               batch = MIN(m->DL_COUNT - i, NR_IOREQS);
+
+               r = sys_safecopyfrom(m->DL_PROC, m->DL_GRANT, off, 
+                       (vir_bytes) iovec, batch * sizeof(iovec[0]), D);
+               if (r != OK)
+                       panic("atl2", "vector copy failed", r);
+
+               /* Copy out each element in the batch, until we run out. */
+               for (j = 0, iovp = iovec; j < batch && left > 0; j++, iovp++) {
+                       size = MIN(iovp->iov_size, left);
+
+                       r = sys_safecopyto(m->DL_PROC, iovp->iov_grant, 0,
+                               (vir_bytes) pos, size, D);
+                       if (r != OK)
+                               panic("atl2", "safe copy failed", r);
+
+                       pos += size;
+                       left -= size;
+               }
+
+               off += batch * sizeof(iovec[0]);
+       }
+
+       /* Not sure what to do here. Inet shouldn't mess this up anyway. */
+       if (left > 0) {
+               printf("ATL2: truncated packet of %d bytes by %d bytes\n",
+                       count, left);
+               count -= left;
+       }
+
+       /* We are done with this packet. Move on to the next. */
+       atl2_rx_advance(TRUE /*next*/);
+
+       /* We have now successfully received a packet. */
+       state.flags &= ~ATL2_FLAG_READ_PEND;
+       state.flags |= ATL2_FLAG_PACK_RCVD;
+       state.recv_count = count;
+
+       /* If called from the interrupt handler, the caller will reply. */
+       if (!from_int)
+               atl2_reply();
+
+       return;
+
+suspend:
+       /* No packets are available at this time. If we were not already
+        * trying to resume receipt, save the read request for later, and tell
+        * Inet that the request has been suspended.
+        */
+       if (from_int)
+               return;
+
+       state.flags |= ATL2_FLAG_READ_PEND;
+       state.read_msg = *m;
+
+       atl2_reply();
+}
+
+/*===========================================================================*
+ *                             atl2_writev                                  *
+ *===========================================================================*/
+PRIVATE void atl2_writev(message *m, int from_int)
+{
+       /* Write packet data.
+        */
+       iovec_s_t *iovp;
+       size_t off, count, left, pos, size, skip;
+       u8_t *sizep;
+       int i, j, r, batch, maxnum;
+
+       if (m->DL_PORT != 0) {
+               printf("ATL2: write to invalid port\n");
+
+               return;
+       }
+
+       /* We can deal with only one write request from Inet at a time. */
+       assert(from_int || !(state.flags & ATL2_FLAG_WRITE_PEND));
+
+       assert(m->m_source == m->DL_PROC);
+       state.task_endpt = m->m_source;
+
+       /* If we are already certain that the packet won't fit, bail out.
+        * Keep at least some space between TxD head and tail, as it is not
+        * clear whether the device deals well with the case that they collide.
+        */
+       if (state.txs_num >= ATL2_TXS_COUNT)
+               goto suspend;
+       maxnum = ATL2_TXD_BUFSIZE - ETH_MIN_PACK_SIZE - sizeof(u32_t);
+       if (state.txd_num >= maxnum)
+               goto suspend;
+
+       /* Optimistically try to copy in the data; suspend if it turns out
+        * that it does not fit.
+        */
+       off = 0;
+       count = 0;
+       left = state.txd_num - sizeof(u32_t);
+       pos = (state.txd_tail + state.txd_num +
+               sizeof(u32_t)) % ATL2_TXD_BUFSIZE;
+
+       for (i = 0; i < m->DL_COUNT; i += batch) {
+               /* Copy in the next batch. */
+               batch = MIN(m->DL_COUNT - i, NR_IOREQS);
+
+               r = sys_safecopyfrom(m->DL_PROC, m->DL_GRANT, off, 
+                       (vir_bytes) iovec, batch * sizeof(iovec[0]), D);
+               if (r != OK)
+                       panic("atl2", "vector copy failed", r);
+
+               /* Copy in each element in the batch. */
+               for (j = 0, iovp = iovec; j < batch; j++, iovp++) {
+                       size = iovp->iov_size;
+                       if (size > left)
+                               goto suspend;
+
+                       skip = 0;
+                       if (size > ATL2_TXD_BUFSIZE - pos) {
+                               skip = ATL2_TXD_BUFSIZE - pos;
+                               r = sys_safecopyfrom(m->DL_PROC,
+                                       iovp->iov_grant, 0,
+                                       (vir_bytes) (state.txd_base + pos),
+                                       skip, D);
+                               if (r != OK)
+                                       panic("atl2", "safe copy failed", r);
+                               pos = 0;
+                       }
+
+                       r = sys_safecopyfrom(m->DL_PROC, iovp->iov_grant, skip,
+                               (vir_bytes) (state.txd_base + pos),
+                               size - skip, D);
+                       if (r != OK)
+                               panic("atl2", "safe copy failed", r);
+
+                       pos = (pos + size - skip) % ATL2_TXD_BUFSIZE;
+                       left -= size;
+                       count += size;
+               }
+
+               off += batch * sizeof(iovec[0]);
+       }
+
+       assert(count <= ETH_MAX_PACK_SIZE_TAGGED);
+
+       /* Write the length to the DWORD right before the packet. */
+       sizep = state.txd_base +
+               (state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE;
+       * (u32_t *) sizep = count;
+
+       /* Update the TxD head. */
+       state.txd_num += sizeof(u32_t) + ATL2_ALIGN_32(count);
+       pos = ATL2_ALIGN_32(pos) % ATL2_TXD_BUFSIZE;
+       assert(pos == (state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE);
+
+       /* Initialize and update the TxS head. */
+       state.txs_base[(state.txs_tail + state.txs_num) % ATL2_TXS_COUNT] = 0;
+       state.txs_num++;
+
+       /* Tell the device about our new position. */
+       ATL2_WRITE_U32(ATL2_TXD_IDX_REG, pos / sizeof(u32_t));
+
+       /* We have now successfully set up the transmission of a packet. */
+       state.flags &= ~ATL2_FLAG_WRITE_PEND;
+       state.flags |= ATL2_FLAG_PACK_SENT;
+
+       /* If called from the interrupt handler, the caller will reply. */
+       if (!from_int)
+               atl2_reply();
+
+       return;
+
+suspend:
+       /* We cannot transmit the packet at this time. If we were not already
+        * trying to resume transmission, save the write request for later,
+        * and tell Inet that the request has been suspended.
+        */
+       if (from_int)
+               return;
+
+       state.flags |= ATL2_FLAG_WRITE_PEND;
+       state.write_msg = *m;
+
+       atl2_reply();
+}
+
+/*===========================================================================*
+ *                             atl2_intr                                    *
+ *===========================================================================*/
+PRIVATE void atl2_intr(message *m)
+{
+       /* Interrupt received.
+        */
+       u32_t val;
+       int r, try_write, try_read;
+
+       /* Clear and disable interrupts. */
+       val = ATL2_READ_U32(ATL2_ISR_REG);
+
+       ATL2_WRITE_U32(ATL2_ISR_REG, val | ATL2_ISR_DISABLE);
+
+       ATL2_DEBUG(("ATL2: interrupt (0x%08lx)\n", val));
+
+       /* If an error occurred, reset the card. */
+       if (val & (ATL2_ISR_DMAR_TIMEOUT | ATL2_ISR_DMAW_TIMEOUT |
+                       ATL2_ISR_PHY_LINKDOWN)) {
+               atl2_setup();
+       }
+
+       try_write = try_read = FALSE;
+
+       /* Process sent data, and possibly send pending data. */
+       if (val & ATL2_ISR_TX_EVENT) {
+               if (atl2_tx_advance())
+                       try_write = (state.flags & ATL2_FLAG_WRITE_PEND);
+       }
+
+       /* Receive new data, and possible satisfy a pending receive request. */
+       if (val & ATL2_ISR_RX_EVENT) {
+               if (!(state.flags & ATL2_FLAG_RX_AVAIL)) {
+                       atl2_rx_advance(FALSE /*next*/);
+
+                       try_read = (state.flags & ATL2_FLAG_READ_PEND);
+               }
+       }
+
+       /* Reenable interrupts. */
+       ATL2_WRITE_U32(ATL2_ISR_REG, 0);
+
+       if ((r = sys_irqenable(&state.hook_id)) != OK)
+               panic("atl2", "unable to enable IRQ", r);
+
+       /* Attempt to satisfy pending write and read requests. */
+       if (try_write)
+               atl2_writev(&state.write_msg, TRUE /*from_int*/);
+       if (try_read)
+               atl2_readv(&state.read_msg, TRUE /*from_int*/);
+       if (state.flags & (ATL2_FLAG_PACK_SENT | ATL2_FLAG_PACK_RCVD))
+               atl2_reply();
+}
+
+/*===========================================================================*
+ *                             atl2_conf                                    *
+ *===========================================================================*/
+PRIVATE void atl2_conf(message *m)
+{
+       /* Configure the mode of the card.
+        */
+       ether_addr_t *addr;
+       int r;
+
+       if (m->DL_PORT != 0) {
+               m->m3_i1 = ENXIO;
+       }
+       else {
+               state.mode = m->DL_MODE;
+
+               atl2_set_mode();
+
+               addr = (ether_addr_t *) m->m3_ca1;
+
+               addr->ea_addr[0] = state.hwaddr[1] >> 8;
+               addr->ea_addr[1] = state.hwaddr[1] & 0xff;
+               addr->ea_addr[2] = state.hwaddr[0] >> 24;
+               addr->ea_addr[3] = (state.hwaddr[0] >> 16) & 0xff;
+               addr->ea_addr[4] = (state.hwaddr[0] >> 8) & 0xff;
+               addr->ea_addr[5] = state.hwaddr[0] & 0xff;
+
+               m->m3_i1 = OK;
+       }
+
+       m->m_type = DL_CONF_REPLY;
+
+       if ((r = send(m->m_source, m)) != OK)
+               printf("ATL2: unable to send reply (%d)\n", r);
+}
+
+/*===========================================================================*
+ *                             atl2_getstat                                 *
+ *===========================================================================*/
+PRIVATE void atl2_getstat(message *m)
+{
+       /* Copy out statistics.
+        */
+       int r;
+
+       if (m->DL_PORT != 0) {
+               r = ENXIO;
+       }
+       else {
+               r = sys_safecopyto(m->DL_PROC, m->DL_GRANT, 0,
+                       (vir_bytes) &state.stat, sizeof(state.stat), D);
+       }
+
+       m->m_type = DL_STAT_REPLY;
+       /* keep m->DL_PORT */
+       m->DL_STAT = r;
+
+       if ((r = send(m->m_source, m)) != OK)
+               printf("ATL2: unable to send reply (%d)\n", r);
+}
+
+/*===========================================================================*
+ *                             atl2_getname                                 *
+ *===========================================================================*/
+PRIVATE void atl2_getname(message *m, int instance)
+{
+       /* Tell Inet the name of this driver.
+        */
+       int r;
+
+       /* Each instance must have a unique name. */
+       m->m_type = DL_NAME_REPLY;
+       if (instance > 0)
+               snprintf(m->DL_NAME, sizeof(m->DL_NAME), "atl2:%u",
+                       instance - 1);
+       else
+               strcpy(m->DL_NAME, "atl2");
+
+       if ((r = send(m->m_source, m)) != OK)
+               printf("ATL2: unable to send reply (%d)\n", r);
+}
+
+/*===========================================================================*
+ *                             atl2_shutdown                                *
+ *===========================================================================*/
+PRIVATE void atl2_shutdown(void)
+{
+       /* Shut down this driver. Stop the device, and deallocate resources
+        * as proof of concept.
+        */
+       int r;
+
+       atl2_stop();
+
+       if ((r = sys_irqrmpolicy(&state.hook_id)) != OK)
+               panic("atl2", "unable to deregister IRQ", r);
+
+       munmap(state.txd_base, ATL2_TXD_BUFSIZE);
+       munmap(state.txs_base, ATL2_TXS_COUNT * sizeof(u32_t));
+       munmap(state.rxd_base_u,
+               state.rxd_align + ATL2_RXD_COUNT * ATL2_RXD_SIZE);
+
+       vm_unmap_phys(SELF, state.base, ATL2_MMAP_SIZE);
+
+       /* We cannot free the PCI device at this time. */
+
+       exit(0);
+}
+
+/*===========================================================================*
+ *                             atl2_dump_link                               *
+ *===========================================================================*/
+PRIVATE void atl2_dump_link(void)
+{
+       /* Dump link status.
+        */
+       u16_t val;
+       int link_up;
+
+       /* The link status bit is latched. Read the status register twice. */
+       atl2_read_mdio(ATL2_MII_BMSR, &val);
+       if (!atl2_read_mdio(ATL2_MII_BMSR, &val)) return;
+
+       link_up = val & ATL2_MII_BMSR_LSTATUS;
+       printf("link status:     %4s\t", link_up ? "up" : "down");
+
+       if (!link_up) return;
+
+       if (!atl2_read_mdio(ATL2_MII_PSSR, &val)) return;
+
+       if (!(val & ATL2_MII_PSSR_RESOLVED)) {
+               printf("(not resolved)\n");
+
+               return;
+       }
+
+       switch (val & ATL2_MII_PSSR_SPEED) {
+       case ATL2_MII_PSSR_10: printf("(10Mbps "); break;
+       case ATL2_MII_PSSR_100: printf("(100Mbps "); break;
+       case ATL2_MII_PSSR_1000: printf("(1000Mbps "); break;
+       default: printf("(unknown, ");
+       }
+
+       printf("%s duplex)", (val & ATL2_MII_PSSR_DUPLEX) ? "full" : "half");
+}
+
+/*===========================================================================*
+ *                             atl2_dump                                    *
+ *===========================================================================*/
+PRIVATE void atl2_dump(void)
+{
+       /* Dump statistics.
+        */
+
+       printf("\n");
+       printf("Attansic L2 statistics:\n");
+
+       printf("recvErr:     %8ld\t", state.stat.ets_recvErr);
+       printf("sendErr:     %8ld\t", state.stat.ets_sendErr);
+       printf("OVW:         %8ld\n", state.stat.ets_OVW);
+
+       printf("CRCerr:      %8ld\t", state.stat.ets_CRCerr);
+       printf("frameAll:    %8ld\t", state.stat.ets_frameAll);
+       printf("missedP:     %8ld\n", state.stat.ets_missedP);
+
+       printf("packetR:     %8ld\t", state.stat.ets_packetR);
+       printf("packetT:     %8ld\t", state.stat.ets_packetT);
+       printf("transDef:    %8ld\n", state.stat.ets_transDef);
+
+       printf("collision:   %8ld\t", state.stat.ets_collision);
+       printf("transAb:     %8ld\t", state.stat.ets_transAb);
+       printf("carrSense:   %8ld\n", state.stat.ets_carrSense);
+
+       printf("fifoUnder:   %8ld\t", state.stat.ets_fifoUnder);
+       printf("fifoOver:    %8ld\t", state.stat.ets_fifoOver);
+       printf("CDheartbeat: %8ld\n", state.stat.ets_CDheartbeat);
+
+       printf("OWC:         %8ld\t", state.stat.ets_OWC);
+       printf("TxD tail:    %8d\t", state.txd_tail);
+       printf("TxD count:   %8d\n", state.txd_num);
+
+       printf("RxD tail:    %8d\t", state.rxd_tail);
+       printf("TxS tail:    %8d\t", state.txs_tail);
+       printf("TxS count:   %8d\n", state.txs_num);
+
+       printf("flags:         0x%04x\t", state.flags);
+       atl2_dump_link();
+       printf("\n");
+}
+
+/*===========================================================================*
+ *                             sef_local_startup                            *
+ *===========================================================================*/
+PRIVATE void sef_local_startup(void)
+{
+       /* Initialize the System Event Framework.
+        */
+
+       /* No support for live update yet. */
+       sef_startup();
+}
+
+/*===========================================================================*
+ *                             main                                         *
+ *===========================================================================*/
+int main(int argc, char **argv)
+{
+       /* Driver task.
+        */
+       u32_t inet_endpt;
+       message m;
+       sigset_t set;
+       int r, devind;
+       long instance;
+#if ATL2_FKEY
+       int fkeys, sfkeys;
+#endif
+
+       /* Initialize SEF. */
+       sef_local_startup();
+
+       /* How many matching devices should we skip? */
+       instance = 0;
+       env_setargs(argc, argv);
+       env_parse("atl2_instance", "d", 0, &instance, 0, 32);
+
+       /* Try to find a recognized device. */
+       devind = atl2_probe(instance);
+
+       if (devind < 0)
+               panic("atl2", "no matching device found", NO_NUM);
+
+       /* Initialize the device. */
+       atl2_init(devind);
+
+       /* Notify Inet of our presence, if it has already been started. */
+       r = ds_retrieve_u32("inet", &inet_endpt);
+       if (r == OK)
+               notify(inet_endpt);
+       else if (r != ESRCH)
+               printf("ATL2: ds_retrieve_u32 failed for 'inet': %d\n", r);
+
+#if ATL2_FKEY
+       /* Register debug dump function key. */
+       fkeys = sfkeys = 0;
+       bit_set(sfkeys, 11);
+       if ((r = fkey_map(&fkeys, &sfkeys)) != OK)
+               printf("ATL2: warning, could not map Shift+F11 key (%d)\n", r);
+#endif
+
+       while (TRUE) {
+               if ((r = sef_receive(ANY, &m)) != OK)
+                       panic("atl2", "sef_receive failed", r);
+
+               if (is_notify(m.m_type)) {
+                       switch (m.m_source) {
+                       case HARDWARE:          /* interrupt */
+                               atl2_intr(&m);
+
+                               break;
+
+                       case PM_PROC_NR:        /* signal */
+                               if (getsigset(&set) != 0) break;
+
+                               if (sigismember(&set, SIGTERM))
+                                       atl2_shutdown();
+
+                               break;
+
+                       case TTY_PROC_NR:       /* function key */
+                               atl2_dump();
+
+                               break;
+
+                       default:
+                               printf("ATL2: illegal notify from %d\n",
+                                       m.m_source);
+                       }
+
+                       continue;
+               }
+
+               /* Process requests from Inet. */
+               switch (m.m_type) {
+               case DL_GETNAME:        atl2_getname(&m, instance);     break;
+               case DL_CONF:           atl2_conf(&m);                  break;
+               case DL_GETSTAT_S:      atl2_getstat(&m);               break;
+               case DL_WRITEV_S:       atl2_writev(&m, FALSE);         break;
+               case DL_READV_S:        atl2_readv(&m, FALSE);          break;
+               default:
+                       printf("ATL2: illegal message %d from %d\n",
+                               m.m_type, m.m_source);
+               }
+       }
+}
diff --git a/drivers/atl2/atl2.h b/drivers/atl2/atl2.h
new file mode 100644 (file)
index 0000000..06812bc
--- /dev/null
@@ -0,0 +1,183 @@
+/* Attansic/Atheros L2 FastEthernet driver, by D.C. van Moolenbroek */
+
+#define ATL2_MMAP_SIZE                 0x40000 /* memory-mapped registers */
+
+/* The first three are configurable to a certain extent; the last is not. */
+#define ATL2_TXD_BUFSIZE               8192    /* TxD ring buffer size */
+#define ATL2_TXS_COUNT                 64      /* Tx status ring array size */
+#define ATL2_RXD_COUNT                 64      /* Rx descriptors */
+#define ATL2_RXD_SIZE                  1536    /* Rx element size */
+
+#define ATL2_MASTER_REG                        0x1400  /* master register */
+#      define ATL2_MASTER_SOFT_RESET   0x00000001      /* soft reset */
+#      define ATL2_MASTER_IMT_EN       0x00000004      /* IMT enabled */
+
+#define ATL2_RESET_NTRIES              100     /* #tries to wait for reset */
+#define ATL2_RESET_DELAY               10      /* delay (us) between tries */
+
+#define ATL2_PHY_ENABLE_REG            0x140c  /* PHY enable register */
+#      define ATL2_PHY_ENABLE          1               /* enable PHY */
+
+#define ATL2_IDLE_REG                  0x1410  /* idle status register */
+
+#define ATL2_IDLE_NTRIES               100     /* #tries to wait for idle */
+#define ATL2_IDLE_DELAY                        100     /* delay (us) between tries */
+
+#define ATL2_HWADDR0_REG               0x1488  /* Hardware address (part 0) */
+#define ATL2_HWADDR1_REG               0x148c  /* Hardware address (part 1) */
+
+#define ATL2_ISR_REG                   0x1600  /* interrupt status register */
+#      define ATL2_ISR_RXF_OVERFLOW    0x00000004      /* RxF overflow */
+#      define ATL2_ISR_TXF_UNDERRUN    0x00000008      /* TxF underrun */
+#      define ATL2_ISR_TXS_OVERFLOW    0x00000010      /* TxS overflow */
+#      define ATL2_ISR_RXS_OVERFLOW    0x00000020      /* RxS overflow */
+#      define ATL2_ISR_TXD_UNDERRUN    0x00000080      /* TxD underrun */
+#      define ATL2_ISR_RXD_OVERFLOW    0x00000100      /* RxD overflow */
+#      define ATL2_ISR_DMAR_TIMEOUT    0x00000200      /* DMA read timeout */
+#      define ATL2_ISR_DMAW_TIMEOUT    0x00000400      /* DMA write timeout */
+#      define ATL2_ISR_TXS_UPDATED     0x00010000      /* Tx status updated */
+#      define ATL2_ISR_RXD_UPDATED     0x00020000      /* Rx status updated */
+#      define ATL2_ISR_TX_EARLY        0x00040000      /* Tx started xmit */
+#      define ATL2_ISR_PHY_LINKDOWN    0x10000000      /* PHY link down */
+#      define ATL2_ISR_DISABLE         0x80000000      /* disable intrs */
+#      define ATL2_ISR_TX_EVENT        (ATL2_ISR_TXF_UNDERRUN | \
+                                        ATL2_ISR_TXS_OVERFLOW | \
+                                        ATL2_ISR_TXD_UNDERRUN | \
+                                        ATL2_ISR_TXS_UPDATED | \
+                                        ATL2_ISR_TX_EARLY)
+#      define ATL2_ISR_RX_EVENT        (ATL2_ISR_RXF_OVERFLOW | \
+                                        ATL2_ISR_RXS_OVERFLOW | \
+                                        ATL2_ISR_RXD_OVERFLOW | \
+                                        ATL2_ISR_RXD_UPDATED)
+
+#define ATL2_IMR_REG                   0x1604  /* interrupt mask register */
+#      define ATL2_IMR_DEFAULT         (ATL2_ISR_DMAR_TIMEOUT | \
+                                        ATL2_ISR_DMAW_TIMEOUT | \
+                                        ATL2_ISR_TXS_UPDATED | \
+                                        ATL2_ISR_RXD_UPDATED | \
+                                        ATL2_ISR_PHY_LINKDOWN)
+
+#define ATL2_MAC_REG                   0x1480  /* MAC config register */
+#      define ATL2_MAC_TX_EN           0x00000001      /* enable transmit */
+#      define ATL2_MAC_RX_EN           0x00000002      /* enable receive */
+#      define ATL2_MAC_PROMISC_EN      0x00008000      /* promiscuous */
+#      define ATL2_MAC_MCAST_EN        0x02000000      /* multicast */
+#      define ATL2_MAC_BCAST_EN        0x04000000      /* broadcast */
+#      define ATL2_MAC_DEFAULT         0x28001cec      /* (magic) */
+
+#define ATL2_MHT0_REG                  0x1490  /* multicast hash table bits */
+#define ATL2_MHT1_REG                  0x1494  /* 64 slots in total */
+
+#define ATL2_DMAREAD_REG               0x1580  /* read DMA config register */
+#      define ATL2_DMAREAD_EN          1               /* read DMA enabled */
+#define ATL2_DMAWRITE_REG              0x15a0  /* write DMA config register */
+#      define ATL2_DMAWRITE_EN         1               /* write DMA enabled */
+
+#define ATL2_DESC_ADDR_HI_REG          0x1540  /* high 32 bits of addresses */
+#define ATL2_TXD_ADDR_LO_REG           0x1544  /* low 32 bits of TxD base */
+#define ATL2_TXD_BUFSIZE_REG           0x1548  /* size of TxD ring buffer */
+#define ATL2_TXS_ADDR_LO_REG           0x154c  /* low 32 bits of TxS base */
+#define ATL2_TXS_COUNT_REG             0x1550  /* number of TxS descriptors */
+#define ATL2_RXD_ADDR_LO_REG           0x1554  /* low 32 bits of RxD base */
+#define ATL2_RXD_COUNT_REG             0x1558  /* number of RxD descriptors */
+
+#define ATL2_IFG_REG                   0x1484  /* inter-frame gap config */
+#      define ATL2_IFG_DEFAULT         0x60405060      /* (magic) */
+
+#define ATL2_HDPX_REG                  0x1498  /* half-duplex mode config */
+#      define ATL2_HDPX_DEFAULT        0x07a1f037      /* (magic) */
+
+#define ATL2_IMT_REG                   0x1408  /* intr moderation timer */
+#      define ATL2_IMT_DEFAULT         100             /* 200 us */
+
+#define ATL2_ICT_REG                   0x140e  /* intr clearing timer */
+#      define ATL2_ICT_DEFAULT         50000           /* 100 ms */
+
+#define ATL2_MTU_REG                   0x149c  /* MTU config */
+#      define ATL2_MTU_DEFAULT         ETH_MAX_PACK_SIZE_TAGGED
+
+#define ATL2_CUT_THRESH_REG            0x1590  /* cut-through config */
+#      define ATL2_CUT_THRESH_DEFAULT  0x177           /* (magic) */
+
+#define ATL2_FLOW_THRESH_HI_REG                0x15a8  /* RxD overflow hi watermark */
+#define ATL2_FLOW_THRESH_LO_REG                0x15aa  /* RxD overflow lo watermark */
+
+#define ATL2_TXD_IDX_REG               0x15f0  /* TxD read index */
+#define ATL2_RXD_IDX_REG               0x15f4  /* RxD write index */
+
+#define ATL2_LTSSM_TESTMODE_REG                0x12fc  /* PCIE configuration */
+#define ATL2_LTSSM_TESTMODE_DEFAULT    0x6500          /* (magic) */
+#define ATL2_DLL_TX_CTRL_REG           0x1104  /* PCIE configuration */
+#define ATL2_DLL_TX_CTRL_DEFAULT       0x0568          /* (magic) */
+
+#define ATL2_VPD_CAP_REG               0x6c    /* VPD command register */
+#      define ATL2_VPD_CAP_ADDR_SHIFT  16
+#      define ATL2_VPD_CAP_ADDR_MASK   0x7fff0000
+#      define ATL2_VPD_CAP_DONE        0x80000000
+#define ATL2_VPD_DATA_REG              0x70    /* VPD data register */
+
+#define ATL2_SPICTL_REG                        0x200   /* SPI control register */
+#      define ATL2_SPICTL_VPD_EN       0x2000          /* enable VPD */
+
+#define ATL2_VPD_REGBASE               0x100   /* VPD register base */
+#define ATL2_VPD_NREGS                 64      /* number of VPD registers */
+#define ATL2_VPD_SIG_MASK              0xff    /* signature mask */
+#define ATL2_VPD_SIG                   0x5a    /* VPD entry signature */
+#define ATL2_VPD_REG_SHIFT             16      /* key shift */
+
+#define ATL2_VPD_NTRIES                        10      /* #tries to read from VPD */
+#define ATL2_VPD_DELAY                 2000    /* delay (us) between tries */
+
+#define ATL2_MDIO_REG                  0x1414  /* Management Data I/O reg */
+#      define ATL2_MDIO_ADDR_SHIFT     16              /* register address */
+#      define ATL2_MDIO_ADDR_MASK      0x001f0000      /* (shift and mask) */
+#      define ATL2_MDIO_READ           0x00200000      /* read operation */
+#      define ATL2_MDIO_SUP_PREAMBLE   0x00400000      /* suppress preamble */
+#      define ATL2_MDIO_START          0x00800000      /* initiate xfer */
+#      define ATL2_MDIO_CLK_25_4       0x00000000      /* 25MHz, 4bit */
+#      define ATL2_MDIO_BUSY           0x08000000      /* in progress */
+#      define ATL2_MDIO_DATA_MASK      0x0000ffff      /* result data mask */
+
+#define ATL2_MDIO_NTRIES               10      /* #tries to access MDIO */
+#define ATL2_MDIO_DELAY                        2       /* delay (us) between tries */
+
+#define ATL2_MII_BMSR                  1       /* basic mode status reg */
+#      define ATL2_MII_BMSR_LSTATUS    0x0004          /* link status */
+#define ATL2_MII_PSSR                  17      /* PHY specific status reg */
+#      define ATL2_MII_PSSR_RESOLVED   0x0800          /* speed/duplex OK */
+#      define ATL2_MII_PSSR_DUPLEX     0x2000          /* full duplex */
+#      define ATL2_MII_PSSR_SPEED      0xc000          /* link speed */
+#      define ATL2_MII_PSSR_10         0x0000                  /* 10Mbps */
+#      define ATL2_MII_PSSR_100        0x4000                  /* 100Mbps */
+#      define ATL2_MII_PSSR_1000       0x8000                  /* 1000Mbps */
+
+#define ATL2_RXD_SIZE_MASK             0x000007ff      /* packet size mask */
+#define ATL2_RXD_SUCCESS               0x00010000      /* successful receipt */
+#define ATL2_RXD_BCAST                 0x00020000      /* broadcast frame */
+#define ATL2_RXD_MCAST                 0x00040000      /* multicast frame */
+#define ATL2_RXD_PAUSE                 0x00080000      /* pause frame */
+#define ATL2_RXD_CTRL                  0x00100000      /* control frame */
+#define ATL2_RXD_CRCERR                        0x00200000      /* invalid frame CRC */
+#define ATL2_RXD_CODEERR               0x00400000      /* invalid opcode */
+#define ATL2_RXD_RUNT                  0x00800000      /* short frame */
+#define ATL2_RXD_FRAG                  0x01000000      /* collision fragment */
+#define ATL2_RXD_TRUNC                 0x02000000      /* frame truncated */
+#define ATL2_RXD_ALIGN                 0x04000000      /* frame align error */
+#define ATL2_RXD_UPDATE                        0x80000000      /* updated by device */
+
+#define ATL2_TXS_SIZE_MASK             0x000007ff      /* packet size mask */
+#define ATL2_TXS_SUCCESS               0x00010000      /* successful xmit */
+#define ATL2_TXS_BCAST                 0x00020000      /* broadcast frame */
+#define ATL2_TXS_MCAST                 0x00040000      /* multicast frame */
+#define ATL2_TXS_PAUSE                 0x00080000      /* pause frame */
+#define ATL2_TXS_CTRL                  0x00100000      /* control frame */
+#define ATL2_TXS_DEFER                 0x00200000      /* deferred transmit */
+#define ATL2_TXS_EXCDEFER              0x00400000      /* excess defer */
+#define ATL2_TXS_SINGLECOL             0x00800000      /* single collision */
+#define ATL2_TXS_MULTICOL              0x01000000      /* multi collisions */
+#define ATL2_TXS_LATECOL               0x02000000      /* late collision */
+#define ATL2_TXS_ABORTCOL              0x04000000      /* collision abort */
+#define ATL2_TXS_UNDERRUN              0x08000000      /* buffer underrun */
+#define ATL2_TXS_UPDATE                        0x80000000      /* updated by device */
+
+
index a49f16f6f04c8a3bd241d76564438504ce93b744..3f8702d5a96a5068c8920eb96c8fcee9d32d8065 100644 (file)
@@ -44,6 +44,7 @@ struct pci_vendor pci_vendor_table[]=
        { 0x1385, "Netgear" },
        { 0x16ec, "US Robotics" },
        { 0x1737, "Linksys" },
+       { 0x1969, "Atheros Communications" },
        { 0x5333, "S3" },
        { 0x8086, "Intel" },
        { 0x9004, "Adaptec" },
@@ -124,6 +125,7 @@ struct pci_device pci_device_table[]=
        { 0x1385, 0x8169, "Netgear Gigabit Ethernet Adapter" },
        { 0x16ec, 0x0116, "US Robotics Realtek 8169S chip" },
        { 0x1737, 0x1032, "Linksys Instant Gigabit Desktop Network Interface" },
+       { 0x1969, 0x2048, "Atheros L2 Fast Ethernet Controller" },
        { 0x5333, 0x8811, "S3 86c764/765 [Trio32/64/64V+]" },
        { 0x5333, 0x883d, "S3 Virge/VX" },
        { 0x5333, 0x88d0, "S3 Vision 964 vers 0" },
index 055831a37b727ad23d91ff90bfbfd6d8b03f4e90..eef96fce9265956ef17ce671cfea4d7a9a3ca9c6 100644 (file)
@@ -534,3 +534,22 @@ service e1000
                SYSTEM PM RS LOG TTY DS VM
                pci inet ;
 };
+
+service atl2
+{
+       system
+               UMAP            # 14
+               IRQCTL          # 19
+               TIMES           # 25
+               GETINFO         # 26
+               SAFECOPYFROM    # 31
+               SAFECOPYTO      # 32
+               SETGRANT        # 34
+               SYSCTL          # 44
+       ;
+       pci device      1969/2048;
+       ipc
+               SYSTEM PM RS TTY DS VM
+               pci inet
+       ;
+};
index 295fe7ef013a718cada1a7f5bd2819f47a33d199..da71e108d754b91f842e53039ac6cdbdb4e9974c 100644 (file)
@@ -91,7 +91,7 @@ start)
     fi
 
     # start only network drivers that are in use
-    for driver in lance rtl8139 rtl8169 fxp e1000 dpeth dp8390 orinoco
+    for driver in lance rtl8139 rtl8169 fxp e1000 dpeth dp8390 orinoco atl2
     do
         if grep " $driver " /etc/inet.conf > /dev/null  2>&1
         then