summaryrefslogtreecommitdiffstats
path: root/sys/dev/en
diff options
context:
space:
mode:
authorharti <harti@FreeBSD.org>2003-04-25 16:14:03 +0000
committerharti <harti@FreeBSD.org>2003-04-25 16:14:03 +0000
commit0162ac550edaf85d0029a69bc6b8328b3cfba95a (patch)
tree7150b3dd2f2fd105382b3b515c753a1785f5a48b /sys/dev/en
parentdcf45ed6250467e1a126ef79ea7ecbabbb76bbd5 (diff)
downloadFreeBSD-src-0162ac550edaf85d0029a69bc6b8328b3cfba95a.zip
FreeBSD-src-0162ac550edaf85d0029a69bc6b8328b3cfba95a.tar.gz
Convert the midway driver to use busdma. Except for this conversion the
following changes have been done: - stylify. The original code was too hard to read. - get rid of a number of compilation options (Adaptec-only, Eni-only, no-DMA). - more debugging features. - locking. This is not correct yet in the absence of interface layer locking, but is correct enough to not to cause lock order reversals. - remove RAW mode. There are no users of this in the tree and I doubt that there are any. - remove NetBSD compatibility code. There was no way to keep NetBSD non-busdma and FreeBSD busdma code together. - if_en now buildable as a module. This has been actively tested on sparc64 and i386 with ENI server and client cards and an Adaptec card (thanks to kjc). Reviewed by: mdodd, arr
Diffstat (limited to 'sys/dev/en')
-rw-r--r--sys/dev/en/if_en_pci.c573
-rw-r--r--sys/dev/en/midway.c5605
-rw-r--r--sys/dev/en/midwayreg.h116
-rw-r--r--sys/dev/en/midwayvar.h278
4 files changed, 3191 insertions, 3381 deletions
diff --git a/sys/dev/en/if_en_pci.c b/sys/dev/en/if_en_pci.c
index 68d0d65..45e5e3a 100644
--- a/sys/dev/en/if_en_pci.c
+++ b/sys/dev/en/if_en_pci.c
@@ -44,18 +44,21 @@
* FreeBSD PCI glue for the eni155p card.
* thanks to Matt Thomas for figuring out FreeBSD vs NetBSD vs etc.. diffs.
*/
-
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/socket.h>
+#include <sys/sysctl.h>
#include <sys/bus.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
+#include <vm/uma.h>
+
#include <net/if.h>
+#include <net/if_atm.h>
#include <pci/pcivar.h>
#include <pci/pcireg.h>
@@ -64,60 +67,43 @@
#include <dev/en/midwayvar.h>
/*
- * prototypes
- */
-
-static int en_pci_probe(device_t);
-static int en_pci_attach(device_t);
-static int en_pci_detach(device_t);
-static int en_pci_shutdown(device_t);
-
-/*
* local structures
*/
-
struct en_pci_softc {
- /* bus independent stuff */
- struct en_softc esc; /* includes "device" structure */
+ /* bus independent stuff */
+ struct en_softc esc; /* includes "device" structure */
- /* freebsd newbus glue */
- struct resource *res; /* resource descriptor for registers */
- struct resource *irq; /* resource descriptor for interrupt */
- void *ih; /* interrupt handle */
+ /* freebsd newbus glue */
+ struct resource *res; /* resource descriptor for registers */
+ struct resource *irq; /* resource descriptor for interrupt */
+ void *ih; /* interrupt handle */
};
-#if !defined(MIDWAY_ENIONLY)
static void eni_get_macaddr(device_t, struct en_pci_softc *);
-#endif
-#if !defined(MIDWAY_ADPONLY)
static void adp_get_macaddr(struct en_pci_softc *);
-#endif
-
-/*
- * local defines (PCI specific stuff)
- */
/*
* address of config base memory address register in PCI config space
* (this is card specific)
*/
-
#define PCI_CBMA 0x10
/*
* tonga (pci bridge). ENI cards only!
*/
-
#define EN_TONGA 0x60 /* PCI config addr of tonga reg */
#define TONGA_SWAP_DMA 0x80 /* endian swap control */
#define TONGA_SWAP_BYTE 0x40
#define TONGA_SWAP_WORD 0x20
+#define TONGA_READ_MULT 0x00
+#define TONGA_READ_MEM 0x04
+#define TONGA_READ_IVAN 0x08
+#define TONGA_READ_KEN 0x0C
/*
* adaptec pci bridge. ADP cards only!
*/
-
#define ADP_PCIREG 0x050040 /* PCI control register */
#define ADP_PCIREG_RESET 0x1 /* reset card */
@@ -134,187 +120,184 @@ static void adp_get_macaddr(struct en_pci_softc *);
#define PCI_VENDOR(x) ((x) & 0xFFFF)
#define PCI_CHIPID(x) (((x) >> 16) & 0xFFFF)
-#if !defined(MIDWAY_ENIONLY)
-
-static void adp_busreset(void *);
-
/*
* bus specific reset function [ADP only!]
*/
-
-static void adp_busreset(v)
-
-void *v;
-
+static void
+adp_busreset(void *v)
{
- struct en_softc *sc = (struct en_softc *) v;
- u_int32_t dummy;
-
- bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, ADP_PCIREG_RESET);
- DELAY(1000); /* let it reset */
- dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
- bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG,
- (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA|ADP_PCIREG_IENABLE));
- dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
- if ((dummy & (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA)) !=
- (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA))
- printf("adp_busreset: Adaptec ATM did NOT reset!\n");
+ struct en_softc *sc = (struct en_softc *)v;
+ uint32_t dummy;
+
+ bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG,
+ ADP_PCIREG_RESET);
+ DELAY(1000); /* let it reset */
+ dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
+ bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG,
+ (ADP_PCIREG_SWAP_DMA | ADP_PCIREG_IENABLE));
+ dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
+ if ((dummy & (ADP_PCIREG_SWAP_WORD | ADP_PCIREG_SWAP_DMA)) !=
+ ADP_PCIREG_SWAP_DMA)
+ if_printf(&sc->enif, "adp_busreset: Adaptec ATM did "
+ "NOT reset!\n");
}
-#endif
/***********************************************************************/
/*
* autoconfig stuff
*/
-
static int
en_pci_probe(device_t dev)
{
- switch (pci_get_vendor(dev)) {
-#if !defined(MIDWAY_ADPONLY)
- case PCI_VENDOR_EFFICIENTNETS:
- switch (pci_get_device(dev)) {
- case PCI_PRODUCT_EFFICIENTNETS_ENI155PF:
- case PCI_PRODUCT_EFFICIENTNETS_ENI155PA:
- device_set_desc(dev, "Efficient Networks ENI-155p");
- return 0;
- }
- break;
-#endif
-#if !defined(MIDWAY_ENIONLY)
- case PCI_VENDOR_ADP:
- switch (pci_get_device(dev)) {
- case PCI_PRODUCT_ADP_AIC5900:
- case PCI_PRODUCT_ADP_AIC5905:
- device_set_desc(dev, "Adaptec 155 ATM");
- return 0;
- }
- break;
-#endif
- }
- return ENXIO;
+ switch (pci_get_vendor(dev)) {
+
+ case PCI_VENDOR_EFFICIENTNETS:
+ switch (pci_get_device(dev)) {
+
+ case PCI_PRODUCT_EFFICIENTNETS_ENI155PF:
+ case PCI_PRODUCT_EFFICIENTNETS_ENI155PA:
+ device_set_desc(dev, "Efficient Networks ENI-155p");
+ return (0);
+ }
+ break;
+
+ case PCI_VENDOR_ADP:
+ switch (pci_get_device(dev)) {
+
+ case PCI_PRODUCT_ADP_AIC5900:
+ case PCI_PRODUCT_ADP_AIC5905:
+ device_set_desc(dev, "Adaptec 155 ATM");
+ return (0);
+ }
+ break;
+ }
+ return (ENXIO);
}
static int
en_pci_attach(device_t dev)
{
- struct en_softc *sc;
- struct en_pci_softc *scp;
- u_long val;
- int rid, s, unit, error = 0;
-
- sc = device_get_softc(dev);
- scp = (struct en_pci_softc *)sc;
- bzero(scp, sizeof(*scp)); /* zero */
-
- s = splimp();
-
- /*
- * Enable bus mastering.
- */
- val = pci_read_config(dev, PCIR_COMMAND, 2);
- val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
- pci_write_config(dev, PCIR_COMMAND, val, 2);
-
- /*
- * Map control/status registers.
- */
- rid = PCI_CBMA;
- scp->res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
- 0, ~0, 1, RF_ACTIVE);
- if (!scp->res) {
- device_printf(dev, "could not map memory\n");
- error = ENXIO;
- goto fail;
- }
-
- sc->en_memt = rman_get_bustag(scp->res);
- sc->en_base = rman_get_bushandle(scp->res);
-
- /*
- * Allocate our interrupt.
- */
- rid = 0;
- scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
- RF_SHAREABLE | RF_ACTIVE);
- if (scp->irq == NULL) {
- device_printf(dev, "could not map interrupt\n");
- bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
- error = ENXIO;
- goto fail;
- }
-
- error = bus_setup_intr(dev, scp->irq, INTR_TYPE_NET,
- en_intr, sc, &scp->ih);
- if (error) {
- device_printf(dev, "could not setup irq\n");
- bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq);
- bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
- goto fail;
- }
- sc->ipl = 1; /* XXX (required to enable interrupt on midway) */
-
- unit = device_get_unit(dev);
- snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname), "en%d", unit);
- sc->enif.if_unit = unit;
- sc->enif.if_name = "en";
-
- /* figure out if we are an adaptec card or not */
- sc->is_adaptec = (pci_get_vendor(dev) == PCI_VENDOR_ADP) ? 1 : 0;
-
- /*
- * set up pci bridge
- */
-#if !defined(MIDWAY_ENIONLY)
- if (sc->is_adaptec) {
- adp_get_macaddr(scp);
- sc->en_busreset = adp_busreset;
- adp_busreset(sc);
- }
-#endif
-
-#if !defined(MIDWAY_ADPONLY)
- if (!sc->is_adaptec) {
- eni_get_macaddr(dev, scp);
- sc->en_busreset = NULL;
- pci_write_config(dev, EN_TONGA, (TONGA_SWAP_DMA|TONGA_SWAP_WORD), 4);
- }
-#endif
-
- /*
- * done PCI specific stuff
- */
-
- en_attach(sc);
-
- splx(s);
-
- return 0;
-
- fail:
- splx(s);
- return error;
+ struct en_softc *sc;
+ struct en_pci_softc *scp;
+ u_long val;
+ int rid, unit, error = 0;
+
+ sc = device_get_softc(dev);
+ scp = (struct en_pci_softc *)sc;
+
+ unit = device_get_unit(dev);
+ sc->enif.if_unit = unit;
+ sc->enif.if_name = "en";
+
+ /*
+ * Enable bus mastering.
+ */
+ val = pci_read_config(dev, PCIR_COMMAND, 2);
+ val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, val, 2);
+
+ /*
+ * Map control/status registers.
+ */
+ rid = PCI_CBMA;
+ scp->res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (scp->res == NULL) {
+ device_printf(dev, "could not map memory\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->dev = dev;
+ sc->en_memt = rman_get_bustag(scp->res);
+ sc->en_base = rman_get_bushandle(scp->res);
+
+ /*
+ * Allocate our interrupt.
+ */
+ rid = 0;
+ scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (scp->irq == NULL) {
+ device_printf(dev, "could not map interrupt\n");
+ bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->ipl = 1; /* XXX (required to enable interrupt on midway) */
+
+ /* figure out if we are an adaptec card or not */
+ sc->is_adaptec = (pci_get_vendor(dev) == PCI_VENDOR_ADP) ? 1 : 0;
+
+ /*
+ * set up pci bridge
+ */
+ if (sc->is_adaptec) {
+ adp_get_macaddr(scp);
+ sc->en_busreset = adp_busreset;
+ adp_busreset(sc);
+ } else {
+ eni_get_macaddr(dev, scp);
+ sc->en_busreset = NULL;
+ pci_write_config(dev, EN_TONGA, TONGA_SWAP_DMA | TONGA_READ_IVAN, 4);
+ }
+
+ /*
+ * Common attach stuff
+ */
+ if ((error = en_attach(sc)) != 0) {
+ device_printf(dev, "attach failed\n");
+ bus_teardown_intr(dev, scp->irq, scp->ih);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq);
+ bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
+ goto fail;
+ }
+
+ /*
+ * Do the interrupt SETUP last just before returning
+ */
+ error = bus_setup_intr(dev, scp->irq, INTR_TYPE_NET,
+ en_intr, sc, &scp->ih);
+ if (error) {
+ en_reset(sc);
+ if_detach(&sc->enif);
+ device_printf(dev, "could not setup irq\n");
+ bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq);
+ bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
+ en_destroy(sc);
+ goto fail;
+ }
+
+ return (0);
+
+ fail:
+ return (error);
}
+/*
+ * Detach the adapter
+ */
static int
en_pci_detach(device_t dev)
{
struct en_softc *sc = device_get_softc(dev);
struct en_pci_softc *scp = (struct en_pci_softc *)sc;
- int s;
-
- s = splimp();
/*
- * Close down routes etc.
+ * Stop DMA and drop transmit queue.
*/
- if_detach(&sc->enif);
+ if ((sc->enif.if_flags & IFF_RUNNING)) {
+ if_printf(&sc->enif, "still running\n");
+ sc->enif.if_flags &= ~IFF_RUNNING;
+ }
/*
- * Stop DMA and drop transmit queue.
+ * Close down routes etc.
*/
en_reset(sc);
+ if_detach(&sc->enif);
/*
* Deallocate resources.
@@ -323,146 +306,157 @@ en_pci_detach(device_t dev)
bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq);
bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
-#ifdef notyet
/*
* Free all the driver internal resources
*/
-#endif
+ en_destroy(sc);
- splx(s);
-
- return 0;
+ return (0);
}
static int
en_pci_shutdown(device_t dev)
{
- struct en_pci_softc *psc = (struct en_pci_softc *)device_get_softc(dev);
-
- en_reset(&psc->esc);
- DELAY(10); /* is this necessary? */
- return (0);
-}
+ struct en_pci_softc *psc = device_get_softc(dev);
-#if !defined(MIDWAY_ENIONLY)
+ en_reset(&psc->esc);
+ DELAY(10); /* is this necessary? */
-#if defined(sparc)
-#define bus_space_read_1(t, h, o) \
- ((void)t, (*(volatile u_int8_t *)((h) + (o))))
-#endif
+ return (0);
+}
+/*
+ * Get the MAC address from an Adaptec board. No idea how to get
+ * serial number or other stuff, because I have no documentation for that
+ * card.
+ */
static void
-adp_get_macaddr(scp)
- struct en_pci_softc *scp;
+adp_get_macaddr(struct en_pci_softc *scp)
{
- struct en_softc * sc = (struct en_softc *)scp;
- int lcv;
+ struct en_softc * sc = (struct en_softc *)scp;
+ int lcv;
- for (lcv = 0; lcv < sizeof(sc->macaddr); lcv++)
- sc->macaddr[lcv] = bus_space_read_1(sc->en_memt, sc->en_base,
- MID_ADPMACOFF + lcv);
+ for (lcv = 0; lcv < sizeof(sc->macaddr); lcv++)
+ sc->macaddr[lcv] = bus_space_read_1(sc->en_memt, sc->en_base,
+ MID_ADPMACOFF + lcv);
}
-#endif /* MIDWAY_ENIONLY */
-
-#if !defined(MIDWAY_ADPONLY)
-
/*
* Read station (MAC) address from serial EEPROM.
* derived from linux drivers/atm/eni.c by Werner Almesberger, EPFL LRC.
*/
-#define EN_PROM_MAGIC 0x0c
-#define EN_PROM_DATA 0x02
-#define EN_PROM_CLK 0x01
-#define EN_ESI 64
+#define EN_PROM_MAGIC 0x0c
+#define EN_PROM_DATA 0x02
+#define EN_PROM_CLK 0x01
+#define EN_ESI 64
+#define EN_SERIAL 112
+/*
+ * Read a byte from the given address in the EEPROM
+ */
+static uint8_t
+eni_get_byte(device_t dev, uint32_t *data, u_int address)
+{
+ int j;
+ uint8_t tmp;
+
+ address = (address << 1) + 1;
+
+ /* start operation */
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data &= ~EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data &= ~EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ /* send address with serial line */
+ for ( j = 7 ; j >= 0 ; j --) {
+ *data = ((address >> j) & 1) ? (*data | EN_PROM_DATA) :
+ (*data & ~EN_PROM_DATA);
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data &= ~EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ }
+ /* get ack */
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data = pci_read_config(dev, EN_TONGA, 4);
+ *data &= ~EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+
+ tmp = 0;
+
+ for ( j = 7 ; j >= 0 ; j --) {
+ tmp <<= 1;
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data = pci_read_config(dev, EN_TONGA, 4);
+ if(*data & EN_PROM_DATA) tmp |= 1;
+ *data &= ~EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ }
+ /* get ack */
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data = pci_read_config(dev, EN_TONGA, 4);
+ *data &= ~EN_PROM_CLK ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+ *data |= EN_PROM_DATA ;
+ pci_write_config(dev, EN_TONGA, *data, 4);
+
+ return (tmp);
+}
+
+/*
+ * Get MAC address and other stuff from the EEPROM
+ */
static void
eni_get_macaddr(device_t dev, struct en_pci_softc *scp)
{
- struct en_softc * sc = (struct en_softc *)scp;
- int i, j, address, status;
- u_int32_t data, t_data;
- u_int8_t tmp;
-
- t_data = pci_read_config(dev, EN_TONGA, 4) & 0xffffff00;
-
- data = EN_PROM_MAGIC | EN_PROM_DATA | EN_PROM_CLK;
- pci_write_config(dev, EN_TONGA, data, 4);
-
- for (i = 0; i < sizeof(sc->macaddr); i ++){
- /* start operation */
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data &= ~EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data &= ~EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- /* send address with serial line */
- address = ((i + EN_ESI) << 1) + 1;
- for ( j = 7 ; j >= 0 ; j --){
- data = (address >> j) & 1 ? data | EN_PROM_DATA :
- data & ~EN_PROM_DATA;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data &= ~EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- }
- /* get ack */
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data = pci_read_config(dev, EN_TONGA, 4);
- status = data & EN_PROM_DATA;
- data &= ~EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
-
- tmp = 0;
-
- for ( j = 7 ; j >= 0 ; j --){
- tmp <<= 1;
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data = pci_read_config(dev, EN_TONGA, 4);
- if(data & EN_PROM_DATA) tmp |= 1;
- data &= ~EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
- }
- /* get ack */
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data = pci_read_config(dev, EN_TONGA, 4);
- status = data & EN_PROM_DATA;
- data &= ~EN_PROM_CLK ;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_DATA ;
- pci_write_config(dev, EN_TONGA, data, 4);
-
- sc->macaddr[i] = tmp;
- }
- /* stop operation */
- data &= ~EN_PROM_DATA;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_CLK;
- pci_write_config(dev, EN_TONGA, data, 4);
- data |= EN_PROM_DATA;
- pci_write_config(dev, EN_TONGA, data, 4);
- pci_write_config(dev, EN_TONGA, t_data, 4);
+ struct en_softc * sc = (struct en_softc *)scp;
+ int i;
+ uint32_t data, t_data;
+
+ t_data = pci_read_config(dev, EN_TONGA, 4) & 0xffffff00;
+
+ data = EN_PROM_MAGIC | EN_PROM_DATA | EN_PROM_CLK;
+ pci_write_config(dev, EN_TONGA, data, 4);
+
+ for (i = 0; i < sizeof(sc->macaddr); i ++)
+ sc->macaddr[i] = eni_get_byte(dev, &data, i + EN_ESI);
+
+ sc->serial = 0;
+ for (i = 0; i < 4; i++) {
+ sc->serial <<= 8;
+ sc->serial |= eni_get_byte(dev, &data, i + EN_SERIAL);
+ }
+ /* stop operation */
+ data &= ~EN_PROM_DATA;
+ pci_write_config(dev, EN_TONGA, data, 4);
+ data |= EN_PROM_CLK;
+ pci_write_config(dev, EN_TONGA, data, 4);
+ data |= EN_PROM_DATA;
+ pci_write_config(dev, EN_TONGA, data, 4);
+ pci_write_config(dev, EN_TONGA, t_data, 4);
}
-#endif /* !MIDWAY_ADPONLY */
-
+/*
+ * Driver infrastructure
+ */
static device_method_t en_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, en_pci_probe),
@@ -482,4 +476,3 @@ static driver_t en_driver = {
static devclass_t en_devclass;
DRIVER_MODULE(en, pci, en_driver, en_devclass, 0, 0);
-
diff --git a/sys/dev/en/midway.c b/sys/dev/en/midway.c
index a3aeca6..5ccb66a 100644
--- a/sys/dev/en/midway.c
+++ b/sys/dev/en/midway.c
@@ -49,25 +49,11 @@
* generally helpful.
*/
-#undef EN_DEBUG
-#undef EN_DEBUG_RANGE /* check ranges on en_read/en_write's? */
-#define EN_MBUF_OPT /* try and put more stuff in mbuf? */
#define EN_DIAG
-#define EN_STAT
-#ifndef EN_DMA
-#define EN_DMA 1 /* use dma? */
-#endif
-#define EN_NOTXDMA 0 /* hook to disable tx dma only */
-#define EN_NORXDMA 0 /* hook to disable rx dma only */
#define EN_DDBHOOK 1 /* compile in ddb functions */
-#if defined(MIDWAY_ADPONLY)
-#define EN_ENIDMAFIX 0 /* no ENI cards to worry about */
-#else
-#define EN_ENIDMAFIX 1 /* avoid byte DMA on the ENI card (see below) */
-#endif
/*
- * note on EN_ENIDMAFIX: the byte aligner on the ENI version of the card
+ * Note on EN_ENIDMAFIX: the byte aligner on the ENI version of the card
* appears to be broken. it works just fine if there is no load... however
* when the card is loaded the data get corrupted. to see this, one only
* has to use "telnet" over ATM. do the following command in "telnet":
@@ -91,48 +77,68 @@
#if defined(DIAGNOSTIC) && !defined(EN_DIAG)
#define EN_DIAG /* link in with master DIAG option */
#endif
-#ifdef EN_STAT
+
#define EN_COUNT(X) (X)++
-#else
-#define EN_COUNT(X) /* nothing */
-#endif
#ifdef EN_DEBUG
+
#undef EN_DDBHOOK
#define EN_DDBHOOK 1
-#define STATIC /* nothing */
-#define INLINE /* nothing */
+
+/*
+ * This macro removes almost all the EN_DEBUG conditionals in the code that make
+ * to code a good deal less readable.
+ */
+#define DBG(SC, FL, PRINT) do { \
+ if ((SC)->debug & DBG_##FL) { \
+ if_printf(&(SC)->enif, "%s: "#FL": ", __func__); \
+ printf PRINT; \
+ printf("\n"); \
+ } \
+ } while (0)
+
+enum {
+ DBG_INIT = 0x0001, /* debug attach/detach */
+ DBG_TX = 0x0002, /* debug transmitting */
+ DBG_SERV = 0x0004, /* debug service interrupts */
+ DBG_IOCTL = 0x0008, /* debug ioctls */
+ DBG_VC = 0x0010, /* debug VC handling */
+ DBG_INTR = 0x0020, /* debug interrupts */
+ DBG_DMA = 0x0040, /* debug DMA probing */
+ DBG_IPACKETS = 0x0080, /* print input packets */
+ DBG_REG = 0x0100, /* print all register access */
+ DBG_LOCK = 0x0200, /* debug locking */
+};
+
#else /* EN_DEBUG */
-#define STATIC static
-#define INLINE __inline
+
+#define DBG(SC, FL, PRINT) do { } while (0)
+
#endif /* EN_DEBUG */
-#ifdef __FreeBSD__
#include "opt_inet.h"
#include "opt_natm.h"
#include "opt_ddb.h"
-/* enable DDBHOOK when DDB is available */
-#undef EN_DDBHOOK
+
#ifdef DDB
+#undef EN_DDBHOOK
#define EN_DDBHOOK 1
#endif
-#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>
-#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
-#include <sys/device.h>
-#endif
#include <sys/sockio.h>
-#include <sys/mbuf.h>
#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <sys/endian.h>
+#include <sys/sbuf.h>
+#include <sys/stdint.h>
+#include <vm/uma.h>
#include <net/if.h>
#include <net/if_atm.h>
-#include <vm/vm.h>
-
#if defined(INET) || defined(INET6)
#include <netinet/in.h>
#include <netinet/if_atm.h>
@@ -142,115 +148,45 @@
#include <netnatm/natm.h>
#endif
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-#include <machine/bus.h>
-#include <dev/ic/midwayreg.h>
-#include <dev/ic/midwayvar.h>
-#elif defined(__FreeBSD__)
#include <sys/bus.h>
#include <machine/bus.h>
#include <sys/rman.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
#include <machine/resource.h>
#include <dev/en/midwayreg.h>
#include <dev/en/midwayvar.h>
-#include <vm/pmap.h> /* for vtophys proto */
-#ifndef IFF_NOTRAILERS
-#define IFF_NOTRAILERS 0
-#endif
-
-#endif /* __FreeBSD__ */
-
-#if defined(__alpha__)
-/* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */
-#undef vtophys
-#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va))
-#endif
-
-#ifdef __FreeBSD__
-#define NBPF 1
-#else
-#include "bpf.h"
-#endif
-#if NBPF > 0
#include <net/bpf.h>
-#ifdef __FreeBSD__
-#define BPFATTACH(ifp, dlt, hlen) bpfattach((ifp), (dlt), (hlen))
-#else
-#define BPFATTACH(ifp, dlt, hlen) bpfattach(&(ifp)->if_bpf, (ifp), (dlt), (hlen))
-#define BPF_MTAP(ifp, m) bpf_mtap((ifp)->if_bpf, (m))
-#endif
-#endif /* NBPF > 0 */
/*
* params
*/
-
#ifndef EN_TXHIWAT
-#define EN_TXHIWAT (64*1024) /* max 64 KB waiting to be DMAd out */
-#endif
-
-#ifndef EN_MINDMA
-#define EN_MINDMA 32 /* don't DMA anything less than this (bytes) */
+#define EN_TXHIWAT (64 * 1024) /* max 64 KB waiting to be DMAd out */
#endif
-#define RX_NONE 0xffff /* recv VC not in use */
-
-#define EN_OBHDR ATM_PH_DRIVER7 /* TBD in first mbuf ! */
-#define EN_OBTRL ATM_PH_DRIVER8 /* PDU trailier in last mbuf ! */
+#define RX_NONE 0xffff /* recv VC not in use */
#define ENOTHER_FREE 0x01 /* free rxslot */
#define ENOTHER_DRAIN 0x02 /* almost free (drain DRQ dma) */
-#define ENOTHER_RAW 0x04 /* 'raw' access (aka boodi mode) */
#define ENOTHER_SWSL 0x08 /* in software service list */
-static int en_dma = EN_DMA; /* use DMA (switch off for dbg) */
-
-#ifndef __FreeBSD__
-/*
- * autoconfig attachments
- */
-
-struct cfdriver en_cd = {
- 0, "en", DV_IFNET,
-};
-#endif
-
-/*
- * local structures
- */
-
-/*
- * params to en_txlaunch() function
- */
-
-struct en_launch {
- u_int32_t tbd1; /* TBD 1 */
- u_int32_t tbd2; /* TBD 2 */
- u_int32_t pdu1; /* PDU 1 (aal5) */
- int nodma; /* don't use DMA */
- int need; /* total space we need (pad out if less data) */
- int mlen; /* length of mbuf (for dtq) */
- struct mbuf *t; /* data */
- u_int32_t aal; /* aal code */
- u_int32_t atm_vci; /* vci */
- u_int8_t atm_flags; /* flags */
-};
-
+SYSCTL_NODE(_hw, OID_AUTO, en, CTLFLAG_RW, 0, "ENI 155p");
/*
- * dma table (index by # of words)
+ * dma tables
*
- * plan A: use WMAYBE (obsolete)
- * plan B: avoid WMAYBE
+ * The plan is indexed by the number of words to transfer.
+ * The maximum index is 15 for 60 words.
*/
-
struct en_dmatab {
- u_int8_t bcode; /* code */
- u_int8_t divshift; /* byte divisor */
+ uint8_t bcode; /* code */
+ uint8_t divshift; /* byte divisor */
};
-static struct en_dmatab en_dma_planB[] = {
+static const struct en_dmatab en_dmaplan[] = {
{ 0, 0 }, /* 0 */ { MIDDMA_WORD, 2}, /* 1 */
{ MIDDMA_2WORD, 3}, /* 2 */ { MIDDMA_WORD, 2}, /* 3 */
{ MIDDMA_4WORD, 4}, /* 4 */ { MIDDMA_WORD, 2}, /* 5 */
@@ -259,775 +195,1264 @@ static struct en_dmatab en_dma_planB[] = {
{ MIDDMA_2WORD, 3}, /* 10 */ { MIDDMA_WORD, 2}, /* 11 */
{ MIDDMA_4WORD, 4}, /* 12 */ { MIDDMA_WORD, 2}, /* 13 */
{ MIDDMA_2WORD, 3}, /* 14 */ { MIDDMA_WORD, 2}, /* 15 */
- { MIDDMA_16WORD, 6}, /* 16 */
+ { MIDDMA_16WORD,6}, /* 16 */
};
-static struct en_dmatab *en_dmaplan = en_dma_planB;
-
/*
* prototypes
*/
-
-STATIC INLINE int en_b2sz(int) __attribute__ ((unused));
#ifdef EN_DDBHOOK
- int en_dump(int,int);
- int en_dumpmem(int,int,int);
+int en_dump(int unit, int level);
+int en_dumpmem(int,int,int);
#endif
-STATIC void en_dmaprobe(struct en_softc *);
-STATIC int en_dmaprobe_doit(struct en_softc *, u_int8_t *,
- u_int8_t *, int);
-STATIC INLINE int en_dqneed(struct en_softc *, caddr_t, u_int,
- u_int) __attribute__ ((unused));
-STATIC void en_init(struct en_softc *);
-STATIC int en_ioctl(struct ifnet *, EN_IOCTL_CMDT, caddr_t);
-STATIC INLINE int en_k2sz(int) __attribute__ ((unused));
-STATIC void en_loadvc(struct en_softc *, int);
-STATIC int en_mfix(struct en_softc *, struct mbuf **, struct mbuf *);
-STATIC INLINE struct mbuf *en_mget(struct en_softc *, u_int,
- u_int *) __attribute__ ((unused));
-STATIC INLINE u_int32_t en_read(struct en_softc *,
- u_int32_t) __attribute__ ((unused));
-STATIC int en_rxctl(struct en_softc *, struct atm_pseudoioctl *, int);
-STATIC void en_txdma(struct en_softc *, int);
-STATIC void en_txlaunch(struct en_softc *, int,
- struct en_launch *);
-STATIC void en_service(struct en_softc *);
-STATIC void en_start(struct ifnet *);
-STATIC INLINE int en_sz2b(int) __attribute__ ((unused));
-STATIC INLINE void en_write(struct en_softc *, u_int32_t,
- u_int32_t) __attribute__ ((unused));
+
+#define EN_LOCK(SC) do { \
+ DBG(SC, LOCK, ("ENLOCK %d\n", __LINE__)); \
+ mtx_lock(&sc->en_mtx); \
+ } while (0)
+#define EN_UNLOCK(SC) do { \
+ DBG(SC, LOCK, ("ENUNLOCK %d\n", __LINE__)); \
+ mtx_unlock(&sc->en_mtx); \
+ } while (0)
/*
- * macros/inline
+ * While a transmit mbuf is waiting to get transmit DMA resources we
+ * need to keep some information with it. We don't want to allocate
+ * additional memory for this so we stuff it into free fields in the
+ * mbuf packet header. Neither the checksum fields nor the rcvif field are used
+ * so use these.
*/
+#define TX_AAL5 0x1 /* transmit AAL5 PDU */
+#define TX_HAS_TBD 0x2 /* TBD did fit into mbuf */
+#define TX_HAS_PAD 0x4 /* padding did fit into mbuf */
+#define TX_HAS_PDU 0x8 /* PDU trailer did fit into mbuf */
+
+#define MBUF_SET_TX(M, VCI, FLAGS, DATALEN, PAD, MAP) do { \
+ (M)->m_pkthdr.csum_data = (VCI) | ((FLAGS) << MID_VCI_BITS); \
+ (M)->m_pkthdr.csum_flags = ((DATALEN) & 0xffff) | \
+ ((PAD & 0x3f) << 16); \
+ (M)->m_pkthdr.rcvif = (void *)(MAP); \
+ } while (0)
+
+#define MBUF_GET_TX(M, VCI, FLAGS, DATALEN, PAD, MAP) do { \
+ (VCI) = (M)->m_pkthdr.csum_data & ((1 << MID_VCI_BITS) - 1); \
+ (FLAGS) = ((M)->m_pkthdr.csum_data >> MID_VCI_BITS) & 0xf; \
+ (DATALEN) = (M)->m_pkthdr.csum_flags & 0xffff; \
+ (PAD) = ((M)->m_pkthdr.csum_flags >> 16) & 0x3f; \
+ (MAP) = (void *)((M)->m_pkthdr.rcvif); \
+ } while (0)
+
+
+#define EN_WRAPADD(START, STOP, CUR, VAL) do { \
+ (CUR) = (CUR) + (VAL); \
+ if ((CUR) >= (STOP)) \
+ (CUR) = (START) + ((CUR) - (STOP)); \
+ } while (0)
+
+#define WORD_IDX(START, X) (((X) - (START)) / sizeof(uint32_t))
+
+#define SETQ_END(SC, VAL) ((SC)->is_adaptec ? \
+ ((VAL) | (MID_DMA_END >> 4)) : \
+ ((VAL) | (MID_DMA_END)))
/*
- * raw read/write macros
+ * The dtq and drq members are set for each END entry in the corresponding
+ * card queue entry. It is used to find out, when a buffer has been
+ * finished DMAing and can be freed.
+ *
+ * We store sc->dtq and sc->drq data in the following format...
+ * the 0x80000 ensures we != 0
*/
+#define EN_DQ_MK(SLOT, LEN) (((SLOT) << 20) | (LEN) | (0x80000))
+#define EN_DQ_SLOT(X) ((X) >> 20)
+#define EN_DQ_LEN(X) ((X) & 0x3ffff)
-#define EN_READDAT(SC,R) en_read(SC,R)
-#define EN_WRITEDAT(SC,R,V) en_write(SC,R,V)
+/***********************************************************************/
/*
- * cooked read/write macros
+ * en_read{x}: read a word from the card. These are the only functions
+ * that read from the card.
*/
+static __inline uint32_t
+en_readx(struct en_softc *sc, uint32_t r)
+{
+ uint32_t v;
-#define EN_READ(SC,R) (u_int32_t)ntohl(en_read(SC,R))
-#define EN_WRITE(SC,R,V) en_write(SC,R, htonl(V))
-
-#define EN_WRAPADD(START,STOP,CUR,VAL) { \
- (CUR) = (CUR) + (VAL); \
- if ((CUR) >= (STOP)) \
- (CUR) = (START) + ((CUR) - (STOP)); \
- }
-
-#define WORD_IDX(START, X) (((X) - (START)) / sizeof(u_int32_t))
-
-/* we store sc->dtq and sc->drq data in the following format... */
-#define EN_DQ_MK(SLOT,LEN) (((SLOT) << 20)|(LEN)|(0x80000))
- /* the 0x80000 ensures we != 0 */
-#define EN_DQ_SLOT(X) ((X) >> 20)
-#define EN_DQ_LEN(X) ((X) & 0x3ffff)
-
-/* format of DTQ/DRQ word 1 differs between ENI and ADP */
-#if defined(MIDWAY_ENIONLY)
-
-#define MID_MK_TXQ(SC,CNT,CHAN,END,BCODE) \
- EN_WRITE((SC), (SC)->dtq_us, \
- MID_MK_TXQ_ENI((CNT), (CHAN), (END), (BCODE)));
-
-#define MID_MK_RXQ(SC,CNT,VCI,END,BCODE) \
- EN_WRITE((SC), (SC)->drq_us, \
- MID_MK_RXQ_ENI((CNT), (VCI), (END), (BCODE)));
+#ifdef EN_DIAG
+ if (r > MID_MAXOFF || (r % 4))
+ panic("en_read out of range, r=0x%x", r);
+#endif
+ v = bus_space_read_4(sc->en_memt, sc->en_base, r);
+ return (v);
+}
-#elif defined(MIDWAY_ADPONLY)
+static __inline uint32_t
+en_read(struct en_softc *sc, uint32_t r)
+{
+ uint32_t v;
-#define MID_MK_TXQ(SC,CNT,CHAN,END,JK) \
- EN_WRITE((SC), (SC)->dtq_us, \
- MID_MK_TXQ_ADP((CNT), (CHAN), (END), (JK)));
+#ifdef EN_DIAG
+ if (r > MID_MAXOFF || (r % 4))
+ panic("en_read out of range, r=0x%x", r);
+#endif
+ v = bus_space_read_4(sc->en_memt, sc->en_base, r);
+ DBG(sc, REG, ("en_read(%#x) -> %08x", r, v));
+ return (v);
+}
-#define MID_MK_RXQ(SC,CNT,VCI,END,JK) \
- EN_WRITE((SC), (SC)->drq_us, \
- MID_MK_RXQ_ADP((CNT), (VCI), (END), (JK)));
+/*
+ * en_write: write a word to the card. This is the only function that
+ * writes to the card.
+ */
+static __inline void
+en_write(struct en_softc *sc, uint32_t r, uint32_t v)
+{
+#ifdef EN_DIAG
+ if (r > MID_MAXOFF || (r % 4))
+ panic("en_write out of range, r=0x%x", r);
+#endif
+ DBG(sc, REG, ("en_write(%#x) <- %08x", r, v));
+ bus_space_write_4(sc->en_memt, sc->en_base, r, v);
+}
-#else
+/*
+ * en_k2sz: convert KBytes to a size parameter (a log2)
+ */
+static __inline int
+en_k2sz(int k)
+{
+ switch(k) {
+ case 1: return (0);
+ case 2: return (1);
+ case 4: return (2);
+ case 8: return (3);
+ case 16: return (4);
+ case 32: return (5);
+ case 64: return (6);
+ case 128: return (7);
+ default:
+ panic("en_k2sz");
+ }
+ return (0);
+}
+#define en_log2(X) en_k2sz(X)
-#define MID_MK_TXQ(SC,CNT,CHAN,END,JK_OR_BCODE) { \
- if ((SC)->is_adaptec) \
- EN_WRITE((SC), (SC)->dtq_us, \
- MID_MK_TXQ_ADP((CNT), (CHAN), (END), (JK_OR_BCODE))); \
- else \
- EN_WRITE((SC), (SC)->dtq_us, \
- MID_MK_TXQ_ENI((CNT), (CHAN), (END), (JK_OR_BCODE))); \
+/*
+ * en_b2sz: convert a DMA burst code to its byte size
+ */
+static __inline int
+en_b2sz(int b)
+{
+ switch (b) {
+ case MIDDMA_WORD: return (1*4);
+ case MIDDMA_2WMAYBE:
+ case MIDDMA_2WORD: return (2*4);
+ case MIDDMA_4WMAYBE:
+ case MIDDMA_4WORD: return (4*4);
+ case MIDDMA_8WMAYBE:
+ case MIDDMA_8WORD: return (8*4);
+ case MIDDMA_16WMAYBE:
+ case MIDDMA_16WORD: return (16*4);
+ default:
+ panic("en_b2sz");
}
+ return (0);
+}
-#define MID_MK_RXQ(SC,CNT,VCI,END,JK_OR_BCODE) { \
- if ((SC)->is_adaptec) \
- EN_WRITE((SC), (SC)->drq_us, \
- MID_MK_RXQ_ADP((CNT), (VCI), (END), (JK_OR_BCODE))); \
- else \
- EN_WRITE((SC), (SC)->drq_us, \
- MID_MK_RXQ_ENI((CNT), (VCI), (END), (JK_OR_BCODE))); \
+/*
+ * en_sz2b: convert a burst size (bytes) to DMA burst code
+ */
+static __inline int
+en_sz2b(int sz)
+{
+ switch (sz) {
+ case 1*4: return (MIDDMA_WORD);
+ case 2*4: return (MIDDMA_2WORD);
+ case 4*4: return (MIDDMA_4WORD);
+ case 8*4: return (MIDDMA_8WORD);
+ case 16*4: return (MIDDMA_16WORD);
+ default:
+ panic("en_sz2b");
}
+ return(0);
+}
+#ifdef EN_DEBUG
+/*
+ * Dump a packet
+ */
+static void
+en_dump_packet(struct en_softc *sc, struct mbuf *m)
+{
+ int plen = m->m_pkthdr.len;
+ u_int pos = 0;
+ u_int totlen = 0;
+ int len;
+ u_char *ptr;
+
+ if_printf(&sc->enif, "packet len=%d", plen);
+ while (m != NULL) {
+ totlen += m->m_len;
+ ptr = mtod(m, u_char *);
+ for (len = 0; len < m->m_len; len++, pos++, ptr++) {
+ if (pos % 16 == 8)
+ printf(" ");
+ if (pos % 16 == 0)
+ printf("\n");
+ printf(" %02x", *ptr);
+ }
+ m = m->m_next;
+ }
+ printf("\n");
+ if (totlen != plen);
+ printf("sum of m_len=%u\n", totlen);
+}
#endif
-/* add an item to the DTQ */
-#define EN_DTQADD(SC,CNT,CHAN,JK_OR_BCODE,ADDR,LEN,END) { \
- if (END) \
- (SC)->dtq[MID_DTQ_A2REG((SC)->dtq_us)] = EN_DQ_MK(CHAN,LEN); \
- MID_MK_TXQ(SC,CNT,CHAN,END,JK_OR_BCODE); \
- (SC)->dtq_us += 4; \
- EN_WRITE((SC), (SC)->dtq_us, (ADDR)); \
- EN_WRAPADD(MID_DTQOFF, MID_DTQEND, (SC)->dtq_us, 4); \
- (SC)->dtq_free--; \
- if (END) \
- EN_WRITE((SC), MID_DMA_WRTX, MID_DTQ_A2REG((SC)->dtq_us)); \
-}
+/*********************************************************************/
+/*
+ * DMA maps
+ */
-/* DRQ add macro */
-#define EN_DRQADD(SC,CNT,VCI,JK_OR_BCODE,ADDR,LEN,SLOT,END) { \
- if (END) \
- (SC)->drq[MID_DRQ_A2REG((SC)->drq_us)] = EN_DQ_MK(SLOT,LEN); \
- MID_MK_RXQ(SC,CNT,VCI,END,JK_OR_BCODE); \
- (SC)->drq_us += 4; \
- EN_WRITE((SC), (SC)->drq_us, (ADDR)); \
- EN_WRAPADD(MID_DRQOFF, MID_DRQEND, (SC)->drq_us, 4); \
- (SC)->drq_free--; \
- if (END) \
- EN_WRITE((SC), MID_DMA_WRRX, MID_DRQ_A2REG((SC)->drq_us)); \
+/*
+ * Map constructor for a MAP.
+ *
+ * This is called each time when a map is allocated
+ * from the pool and about to be returned to the user. Here we actually
+ * allocate the map if there isn't one. The problem is that we may fail
+ * to allocate the DMA map yet have no means to signal this error. Therefor
+ * when allocating a map, the call must check that there is a map. An
+ * additional problem is, that i386 maps will be NULL, yet are ok and must
+ * be freed so let's use a flag to signal allocation.
+ *
+ * Caveat: we have no way to know that we are called from an interrupt context
+ * here. We rely on the fact, that bus_dmamap_create uses M_NOWAIT in all
+ * its allocations.
+ *
+ * LOCK: any, not needed
+ */
+static void
+en_map_ctor(void *mem, int size, void *arg)
+{
+ struct en_softc *sc = arg;
+ struct en_map *map = mem;
+ int err;
+
+ if (map->sc == NULL)
+ map->sc = sc;
+
+ if (!(map->flags & ENMAP_ALLOC)) {
+ err = bus_dmamap_create(sc->txtag, 0, &map->map);
+ if (err != 0)
+ if_printf(&sc->enif, "cannot create DMA map %d\n", err);
+ else
+ map->flags |= ENMAP_ALLOC;
+ }
+ map->flags &= ~ENMAP_LOADED;
}
/*
- * the driver code
+ * Map destructor.
*
- * the code is arranged in a specific way:
- * [1] short/inline functions
- * [2] autoconfig stuff
- * [3] ioctl stuff
- * [4] reset -> init -> trasmit -> intr -> receive functions
+ * Called when a map is disposed into the zone. If the map is loaded, unload
+ * it.
*
+ * LOCK: any, not needed
*/
+static void
+en_map_dtor(void *mem, int size, void *arg)
+{
+ struct en_map *map = mem;
-/***********************************************************************/
+ if (map->flags & ENMAP_LOADED) {
+ bus_dmamap_unload(map->sc->txtag, map->map);
+ map->flags &= ~ENMAP_LOADED;
+ }
+}
/*
- * en_read: read a word from the card. this is the only function
- * that reads from the card.
+ * Map finializer.
+ *
+ * This is called each time a map is returned from the zone to the system.
+ * Get rid of the dmamap here.
+ *
+ * LOCK: any, not needed
*/
+static void
+en_map_fini(void *mem, int size)
+{
+ struct en_map *map = mem;
-STATIC INLINE u_int32_t en_read(sc, r)
+ if (map->flags & ENMAP_ALLOC)
+ bus_dmamap_destroy(map->sc->txtag, map->map);
+}
-struct en_softc *sc;
-u_int32_t r;
+/*********************************************************************/
+/*
+ * Transmission
+ */
+/*
+ * Argument structure to load a transmit DMA map
+ */
+struct txarg {
+ struct en_softc *sc;
+ struct mbuf *m;
+ u_int vci;
+ u_int chan; /* transmit channel */
+ u_int datalen; /* length of user data */
+ u_int flags;
+ u_int wait; /* return: out of resources */
+};
+
+/*
+ * TX DMA map loader helper. This function is the callback when the map
+ * is loaded. It should fill the DMA segment descriptors into the hardware.
+ *
+ * LOCK: locked, needed
+ */
+static void
+en_txdma_load(void *uarg, bus_dma_segment_t *segs, int nseg, bus_size_t mapsize,
+ int error)
{
+ struct txarg *tx = uarg;
+ struct en_softc *sc = tx->sc;
+ struct en_txslot *slot = &sc->txslot[tx->chan];
+ uint32_t cur; /* on-card buffer position (bytes offset) */
+ uint32_t dtq; /* on-card queue position (byte offset) */
+ uint32_t last_dtq; /* last DTQ we have written */
+ uint32_t tmp;
+ u_int free; /* free queue entries on card */
+ u_int needalign, cnt;
+ bus_size_t rest; /* remaining bytes in current segment */
+ bus_addr_t addr;
+ bus_dma_segment_t *s;
+ uint32_t count, bcode;
+ int i;
+
+ if (error != 0)
+ return;
+
+ cur = slot->cur;
+ dtq = sc->dtq_us;
+ free = sc->dtq_free;
+
+ last_dtq = 0; /* make gcc happy */
+
+ /*
+ * Local macro to add an entry to the transmit DMA area. If there
+ * are no entries left, return. Save the byte offset of the entry
+ * in last_dtq for later use.
+ */
+#define PUT_DTQ_ENTRY(ENI, BCODE, COUNT, ADDR) \
+ if (free == 0) { \
+ EN_COUNT(sc->stats.txdtqout); \
+ tx->wait = 1; \
+ return; \
+ } \
+ last_dtq = dtq; \
+ en_write(sc, dtq + 0, (ENI || !sc->is_adaptec) ? \
+ MID_MK_TXQ_ENI(COUNT, tx->chan, 0, BCODE) : \
+ MID_MK_TXQ_ADP(COUNT, tx->chan, 0, BCODE)); \
+ en_write(sc, dtq + 4, ADDR); \
+ \
+ EN_WRAPADD(MID_DTQOFF, MID_DTQEND, dtq, 8); \
+ free--;
+
+ /*
+ * Local macro to generate a DMA entry to DMA cnt bytes. Updates
+ * the current buffer byte offset accordingly.
+ */
+#define DO_DTQ(TYPE) do { \
+ rest -= cnt; \
+ EN_WRAPADD(slot->start, slot->stop, cur, cnt); \
+ DBG(sc, TX, ("tx%d: "TYPE" %u bytes, %ju left, cur %#x", \
+ tx->chan, cnt, (uintmax_t)rest, cur)); \
+ \
+ PUT_DTQ_ENTRY(1, bcode, count, addr); \
+ \
+ addr += cnt; \
+ } while (0)
+
+ if (!(tx->flags & TX_HAS_TBD)) {
+ /*
+ * Prepend the TBD - it did not fit into the first mbuf
+ */
+ tmp = MID_TBD_MK1((tx->flags & TX_AAL5) ?
+ MID_TBD_AAL5 : MID_TBD_NOAAL5,
+ sc->txspeed[tx->vci],
+ tx->m->m_pkthdr.len / MID_ATMDATASZ);
+ en_write(sc, cur, tmp);
+ EN_WRAPADD(slot->start, slot->stop, cur, 4);
+
+ tmp = MID_TBD_MK2(tx->vci, 0, 0);
+ en_write(sc, cur, tmp);
+ EN_WRAPADD(slot->start, slot->stop, cur, 4);
+
+ /* update DMA address */
+ PUT_DTQ_ENTRY(0, MIDDMA_JK, WORD_IDX(slot->start, cur), 0);
+ }
-#ifdef EN_DEBUG_RANGE
- if (r > MID_MAXOFF || (r % 4))
- panic("en_read out of range, r=0x%x", r);
-#endif
+ for (i = 0, s = segs; i < nseg; i++, s++) {
+ rest = s->ds_len;
+ addr = s->ds_addr;
+
+ if (sc->is_adaptec) {
+ /* adaptec card - simple */
+
+ /* advance the on-card buffer pointer */
+ EN_WRAPADD(slot->start, slot->stop, cur, rest);
+ DBG(sc, TX, ("tx%d: adp %ju bytes %#jx (cur now 0x%x)",
+ tx->chan, (uintmax_t)rest, (uintmax_t)addr, cur));
+
+ PUT_DTQ_ENTRY(0, 0, rest, addr);
+
+ continue;
+ }
- return(bus_space_read_4(sc->en_memt, sc->en_base, r));
+ /*
+ * do we need to do a DMA op to align to the maximum
+ * burst? Note, that we are alway 32-bit aligned.
+ */
+ if (sc->alburst &&
+ (needalign = (addr & sc->bestburstmask)) != 0) {
+ /* compute number of bytes, words and code */
+ cnt = sc->bestburstlen - needalign;
+ if (cnt > rest)
+ cnt = rest;
+ count = cnt / sizeof(uint32_t);
+ if (sc->noalbursts) {
+ bcode = MIDDMA_WORD;
+ } else {
+ bcode = en_dmaplan[count].bcode;
+ count = cnt >> en_dmaplan[count].divshift;
+ }
+ DO_DTQ("al_dma");
+ }
+
+ /* do we need to do a max-sized burst? */
+ if (rest >= sc->bestburstlen) {
+ count = rest >> sc->bestburstshift;
+ cnt = count << sc->bestburstshift;
+ bcode = sc->bestburstcode;
+ DO_DTQ("best_dma");
+ }
+
+ /* do we need to do a cleanup burst? */
+ if (rest != 0) {
+ cnt = rest;
+ count = rest / sizeof(uint32_t);
+ if (sc->noalbursts) {
+ bcode = MIDDMA_WORD;
+ } else {
+ bcode = en_dmaplan[count].bcode;
+ count = cnt >> en_dmaplan[count].divshift;
+ }
+ DO_DTQ("clean_dma");
+ }
+ }
+
+ KASSERT (tx->flags & TX_HAS_PAD, ("PDU not padded"));
+
+ if ((tx->flags & TX_AAL5) && !(tx->flags & TX_HAS_PDU)) {
+ /*
+ * Append the AAL5 PDU trailer
+ */
+ tmp = MID_PDU_MK1(0, 0, tx->datalen);
+ en_write(sc, cur, tmp);
+ EN_WRAPADD(slot->start, slot->stop, cur, 4);
+
+ en_write(sc, cur, 0);
+ EN_WRAPADD(slot->start, slot->stop, cur, 4);
+
+ /* update DMA address */
+ PUT_DTQ_ENTRY(0, MIDDMA_JK, WORD_IDX(slot->start, cur), 0);
+ }
+
+ /* record the end for the interrupt routine */
+ sc->dtq[MID_DTQ_A2REG(last_dtq)] =
+ EN_DQ_MK(tx->chan, tx->m->m_pkthdr.len);
+
+ /* set the end flag in the last descriptor */
+ en_write(sc, last_dtq + 0, SETQ_END(sc, en_read(sc, last_dtq + 0)));
+
+#undef PUT_DTQ_ENTRY
+#undef DO_DTQ
+
+ /* commit */
+ slot->cur = cur;
+ sc->dtq_free = free;
+ sc->dtq_us = dtq;
+
+ /* tell card */
+ en_write(sc, MID_DMA_WRTX, MID_DTQ_A2REG(sc->dtq_us));
}
/*
- * en_write: write a word to the card. this is the only function that
- * writes to the card.
+ * en_txdma: start transmit DMA on the given channel, if possible
+ *
+ * This is called from two places: when we got new packets from the upper
+ * layer or when we found that buffer space has freed up during interrupt
+ * processing.
+ *
+ * LOCK: locked, needed
*/
+static void
+en_txdma(struct en_softc *sc, struct en_txslot *slot)
+{
+ struct en_map *map;
+ struct mbuf *lastm;
+ struct txarg tx;
+ u_int pad;
+ int error;
+
+ DBG(sc, TX, ("tx%td: starting ...", slot - sc->txslot));
+ again:
+ bzero(&tx, sizeof(tx));
+ tx.chan = slot - sc->txslot;
+ tx.sc = sc;
+
+ /*
+ * get an mbuf waiting for DMA
+ */
+ _IF_DEQUEUE(&slot->q, tx.m);
+ if (tx.m == NULL) {
+ DBG(sc, TX, ("tx%td: ...done!", slot - sc->txslot));
+ return;
+ }
+ MBUF_GET_TX(tx.m, tx.vci, tx.flags, tx.datalen, pad, map);
+
+ /*
+ * note: don't use the entire buffer space. if WRTX becomes equal
+ * to RDTX, the transmitter stops assuming the buffer is empty! --kjc
+ */
+ if (tx.m->m_pkthdr.len >= slot->bfree) {
+ EN_COUNT(sc->stats.txoutspace);
+ DBG(sc, TX, ("tx%td: out of transmit space", slot - sc->txslot));
+ goto waitres;
+ }
+
+ lastm = NULL;
+ if (!(tx.flags & TX_HAS_PAD)) {
+ if (pad != 0) {
+ /* Append the padding buffer */
+ (void)m_length(tx.m, &lastm);
+ lastm->m_next = sc->padbuf;
+ sc->padbuf->m_len = pad;
+ }
+ tx.flags |= TX_HAS_PAD;
+ }
-STATIC INLINE void en_write(sc, r, v)
+ /*
+ * Try to load that map
+ */
+ error = bus_dmamap_load_mbuf(sc->txtag, map->map, tx.m,
+ en_txdma_load, &tx, 0);
-struct en_softc *sc;
-u_int32_t r, v;
+ if (lastm != NULL)
+ lastm->m_next = NULL;
-{
-#ifdef EN_DEBUG_RANGE
- if (r > MID_MAXOFF || (r % 4))
- panic("en_write out of range, r=0x%x", r);
+ if (error != 0) {
+ if_printf(&sc->enif, "loading TX map failed %d\n", error);
+ goto dequeue_drop;
+ }
+ map->flags |= ENMAP_LOADED;
+ if (tx.wait) {
+ /* probably not enough space */
+ bus_dmamap_unload(map->sc->txtag, map->map);
+ map->flags &= ~ENMAP_LOADED;
+
+ sc->need_dtqs = 1;
+ DBG(sc, TX, ("tx%td: out of transmit DTQs", slot - sc->txslot));
+ goto waitres;
+ }
+
+ EN_COUNT(sc->stats.launch);
+ sc->enif.if_opackets++;
+
+#ifdef ENABLE_BPF
+ if (sc->enif.if_bpf != NULL) {
+ /*
+ * adjust the top of the mbuf to skip the TBD if present
+ * before passing the packet to bpf.
+ * Also remove padding and the PDU trailer. Assume both of
+ * them to be in the same mbuf. pktlen, m_len and m_data
+ * are not needed anymore so we can change them.
+ */
+ if (tx.flags & TX_HAS_TBD) {
+ tx.m->m_data += MID_TBD_SIZE;
+ tx.m->m_len -= MID_TBD_SIZE;
+ }
+ tx.m->m_pkthdr.len = m_length(tx.m, &lastm);
+ if (tx.m->m_pkthdr.len > tx.datalen) {
+ lastm->m_len -= tx.m->m_pkthdr.len - tx.datalen;
+ tx.m->m_pkthdr.len = tx.datalen;
+ }
+
+ BPF_MTAP(&sc->enif, tx.m);
+ }
#endif
- bus_space_write_4(sc->en_memt, sc->en_base, r, v);
+ /*
+ * do some housekeeping and get the next packet
+ */
+ slot->bfree -= tx.m->m_pkthdr.len;
+ _IF_ENQUEUE(&slot->indma, tx.m);
+
+ goto again;
+
+ /*
+ * error handling. This is jumped to when we just want to drop
+ * the packet. Must be unlocked here.
+ */
+ dequeue_drop:
+ if (map != NULL)
+ uma_zfree(sc->map_zone, map);
+
+ slot->mbsize -= tx.m->m_pkthdr.len;
+
+ m_freem(tx.m);
+
+ goto again;
+
+ waitres:
+ _IF_PREPEND(&slot->q, tx.m);
}
/*
- * en_k2sz: convert KBytes to a size parameter (a log2)
+ * Create a copy of a single mbuf. It can have either internal or
+ * external data, it may have a packet header. External data is really
+ * copied, so the new buffer is writeable.
+ *
+ * LOCK: any, not needed
*/
+static struct mbuf *
+copy_mbuf(struct mbuf *m)
+{
+ struct mbuf *new;
-STATIC INLINE int en_k2sz(k)
+ MGET(new, M_TRYWAIT, MT_DATA);
+ if (new == NULL)
+ return (NULL);
-int k;
+ if (m->m_flags & M_PKTHDR) {
+ M_MOVE_PKTHDR(new, m);
+ if (m->m_len > MHLEN) {
+ MCLGET(new, M_TRYWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(new);
+ return (NULL);
+ }
+ }
+ } else {
+ if (m->m_len > MLEN) {
+ MCLGET(new, M_TRYWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(new);
+ return (NULL);
+ }
+ }
+ }
-{
- switch(k) {
- case 1: return(0);
- case 2: return(1);
- case 4: return(2);
- case 8: return(3);
- case 16: return(4);
- case 32: return(5);
- case 64: return(6);
- case 128: return(7);
- default: panic("en_k2sz");
- }
- return(0);
-}
-#define en_log2(X) en_k2sz(X)
+ bcopy(m->m_data, new->m_data, m->m_len);
+ new->m_len = m->m_len;
+ new->m_flags &= ~M_RDONLY;
+ return (new);
+}
/*
- * en_b2sz: convert a DMA burst code to its byte size
+ * This function is called when we have an ENI adapter. It fixes the
+ * mbuf chain, so that all addresses and lengths are 4 byte aligned.
+ * The overall length is already padded to multiple of cells plus the
+ * TBD so this must always succeed. The routine can fail, when it
+ * needs to copy an mbuf (this may happen if an mbuf is readonly).
+ *
+ * We assume here, that aligning the virtual addresses to 4 bytes also
+ * aligns the physical addresses.
+ *
+ * LOCK: locked, needed
*/
+static struct mbuf *
+en_fix_mchain(struct en_softc *sc, struct mbuf *m0, u_int *pad)
+{
+ struct mbuf **prev = &m0;
+ struct mbuf *m = m0;
+ struct mbuf *new;
+ u_char *d;
+ int off;
+
+ while (m != NULL) {
+ d = mtod(m, u_char *);
+ if ((off = (uintptr_t)d % sizeof(uint32_t)) != 0) {
+ EN_COUNT(sc->stats.mfixaddr);
+ if (M_WRITABLE(m)) {
+ bcopy(d, d - off, m->m_len);
+ m->m_data -= off;
+ } else {
+ if ((new = copy_mbuf(m)) == NULL) {
+ EN_COUNT(sc->stats.mfixfail);
+ m_freem(m0);
+ return (NULL);
+ }
+ new->m_next = m_free(m);
+ *prev = m = new;
+ }
+ }
-STATIC INLINE int en_b2sz(b)
+ if ((off = m->m_len % sizeof(uint32_t)) != 0) {
+ EN_COUNT(sc->stats.mfixlen);
+ if (!M_WRITABLE(m)) {
+ if ((new = copy_mbuf(m)) == NULL) {
+ EN_COUNT(sc->stats.mfixfail);
+ m_freem(m0);
+ return (NULL);
+ }
+ new->m_next = m_free(m);
+ *prev = m = new;
+ }
+ d = mtod(m, u_char *) + m->m_len;
+ off = 4 - off;
+ while (off) {
+ while (m->m_next && m->m_next->m_len == 0)
+ m->m_next = m_free(m->m_next);
+
+ if (m->m_next == NULL) {
+ *d++ = 0;
+ KASSERT(*pad > 0, ("no padding space"));
+ (*pad)--;
+ } else {
+ *d++ = *mtod(m->m_next, u_char *);
+ m->m_next->m_len--;
+ m->m_next->m_data++;
+ }
+ m->m_len++;
+ off--;
+ }
+ }
-int b;
+ prev = &m->m_next;
+ m = m->m_next;
+ }
-{
- switch (b) {
- case MIDDMA_WORD: return(1*4);
- case MIDDMA_2WMAYBE:
- case MIDDMA_2WORD: return(2*4);
- case MIDDMA_4WMAYBE:
- case MIDDMA_4WORD: return(4*4);
- case MIDDMA_8WMAYBE:
- case MIDDMA_8WORD: return(8*4);
- case MIDDMA_16WMAYBE:
- case MIDDMA_16WORD: return(16*4);
- default: panic("en_b2sz");
- }
- return(0);
+ return (m0);
}
-
/*
- * en_sz2b: convert a burst size (bytes) to DMA burst code
+ * en_start: start transmitting the next packet that needs to go out
+ * if there is one. We take off all packets from the interface's queue and
+ * put them into the channels queue.
+ *
+ * Here we also prepend the transmit packet descriptor and append the padding
+ * and (for aal5) the PDU trailer. This is different from the original driver:
+ * we assume, that allocating one or two additional mbufs is actually cheaper
+ * than all this algorithmic fiddling we would need otherwise.
+ *
+ * While the packet is on the channels wait queue we use the csum_* fields
+ * in the packet header to hold the original datalen, the AAL5 flag and the
+ * VCI. The packet length field in the header holds the needed buffer space.
+ * This may actually be more than the length of the current mbuf chain (when
+ * one or more of TBD, padding and PDU do not fit).
+ *
+ * LOCK: unlocked, needed
*/
+static void
+en_start(struct ifnet *ifp)
+{
+ struct en_softc *sc = (struct en_softc *)ifp->if_softc;
+ struct mbuf *m, *lastm;
+ struct atm_pseudohdr *ap;
+ u_int pad; /* 0-bytes to pad at PDU end */
+ u_int datalen; /* length of user data */
+ u_int vci; /* the VCI we are transmitting on */
+ u_int chan; /* the transmit channel */
+ u_int flags;
+ uint32_t tbd[2];
+ uint32_t pdu[2];
+ struct en_map *map;
+
+ while (1) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ return;
+
+ flags = 0;
+
+ ap = mtod(m, struct atm_pseudohdr *);
+ vci = ATM_PH_VCI(ap);
+ if (ATM_PH_FLAGS(ap) & ATM_PH_AAL5)
+ flags |= TX_AAL5;
+
+ if (ATM_PH_VPI(ap) != 0 || vci > MID_N_VC) {
+ DBG(sc, TX, ("output vpi=%u, vci=%u -- drop",
+ ATM_PH_VPI(ap), vci));
+ m_freem(m);
+ continue;
+ }
+ m_adj(m, sizeof(struct atm_pseudohdr));
+
+ /*
+ * (re-)calculate size of packet (in bytes)
+ */
+ m->m_pkthdr.len = datalen = m_length(m, &lastm);
+
+ /*
+ * computing how much padding we need on the end of the mbuf,
+ * then see if we can put the TBD at the front of the mbuf
+ * where the link header goes (well behaved protocols will
+ * reserve room for us). Last, check if room for PDU tail.
+ */
+ if (flags & TX_AAL5)
+ m->m_pkthdr.len += MID_PDU_SIZE;
+ m->m_pkthdr.len = roundup(m->m_pkthdr.len, MID_ATMDATASZ);
+ pad = m->m_pkthdr.len - datalen;
+ if (flags & TX_AAL5)
+ pad -= MID_PDU_SIZE;
+ m->m_pkthdr.len += MID_TBD_SIZE;
+
+ DBG(sc, TX, ("txvci%d: buflen=%u datalen=%u lead=%d trail=%d",
+ vci, m->m_pkthdr.len, datalen, (int)M_LEADINGSPACE(m),
+ (int)M_TRAILINGSPACE(lastm)));
+
+ /*
+ * Allocate a map. We do this here rather then in en_txdma,
+ * because en_txdma is also called from the interrupt handler
+ * and we are going to have a locking problem then. We must
+ * use NOWAIT here, because the ip_output path holds various
+ * locks.
+ */
+ map = uma_zalloc_arg(sc->map_zone, sc, M_NOWAIT);
+ if (map == NULL || !(map->flags & ENMAP_ALLOC)) {
+ /* drop that packet */
+ EN_COUNT(sc->stats.txnomap);
+ if (map != NULL)
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+ continue;
+ }
-STATIC INLINE int en_sz2b(sz)
+ /*
+ * From here on we need access to sc
+ */
+ EN_LOCK(sc);
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ EN_UNLOCK(sc);
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+ continue;
+ }
-int sz;
+ /*
+ * Look, whether we can prepend the TBD (8 byte)
+ */
+ if (M_WRITABLE(m) && M_LEADINGSPACE(m) >= MID_TBD_SIZE) {
+ tbd[0] = htobe32(MID_TBD_MK1((flags & TX_AAL5) ?
+ MID_TBD_AAL5 : MID_TBD_NOAAL5,
+ sc->txspeed[vci],
+ m->m_pkthdr.len / MID_ATMDATASZ));
+ tbd[1] = htobe32(MID_TBD_MK2(vci, 0, 0));
+
+ m->m_data -= MID_TBD_SIZE;
+ bcopy(tbd, m->m_data, MID_TBD_SIZE);
+ m->m_len += MID_TBD_SIZE;
+ flags |= TX_HAS_TBD;
+ }
-{
- switch (sz) {
- case 1*4: return(MIDDMA_WORD);
- case 2*4: return(MIDDMA_2WORD);
- case 4*4: return(MIDDMA_4WORD);
- case 8*4: return(MIDDMA_8WORD);
- case 16*4: return(MIDDMA_16WORD);
- default: panic("en_sz2b");
- }
- return(0);
-}
+ /*
+ * Check whether the padding fits (must be writeable -
+ * we pad with zero).
+ */
+ if (M_WRITABLE(lastm) && M_TRAILINGSPACE(lastm) >= pad) {
+ bzero(lastm->m_data + lastm->m_len, pad);
+ lastm->m_len += pad;
+ flags |= TX_HAS_PAD;
+
+ if ((flags & TX_AAL5) &&
+ M_TRAILINGSPACE(lastm) > MID_PDU_SIZE) {
+ pdu[0] = htobe32(MID_PDU_MK1(0, 0, datalen));
+ pdu[1] = 0;
+ bcopy(pdu, lastm->m_data + lastm->m_len,
+ MID_PDU_SIZE);
+ lastm->m_len += MID_PDU_SIZE;
+ flags |= TX_HAS_PDU;
+ }
+ }
+ if (!sc->is_adaptec &&
+ (m = en_fix_mchain(sc, m, &pad)) == NULL) {
+ EN_UNLOCK(sc);
+ uma_zfree(sc->map_zone, map);
+ continue;
+ }
-/*
- * en_dqneed: calculate number of DTQ/DRQ's needed for a buffer
- */
+ /*
+ * get assigned channel (will be zero unless
+ * txspeed[atm_vci] is set)
+ */
+ chan = sc->txvc2slot[vci];
+
+ if (m->m_pkthdr.len > EN_TXSZ * 1024) {
+ DBG(sc, TX, ("tx%d: packet larger than xmit buffer "
+ "(%d > %d)\n", chan, m->m_pkthdr.len,
+ EN_TXSZ * 1024));
+ EN_UNLOCK(sc);
+ m_freem(m);
+ uma_zfree(sc->map_zone, map);
+ continue;
+ }
-STATIC INLINE int en_dqneed(sc, data, len, tx)
+ if (sc->txslot[chan].mbsize > EN_TXHIWAT) {
+ EN_COUNT(sc->stats.txmbovr);
+ DBG(sc, TX, ("tx%d: buffer space shortage", chan));
+ EN_UNLOCK(sc);
+ m_freem(m);
+ uma_zfree(sc->map_zone, map);
+ continue;
+ }
-struct en_softc *sc;
-caddr_t data;
-u_int len, tx;
+ /* commit */
+ sc->txslot[chan].mbsize += m->m_pkthdr.len;
-{
- int result, needalign, sz;
+ DBG(sc, TX, ("tx%d: VCI=%d, speed=0x%x, buflen=%d, mbsize=%d",
+ chan, vci, sc->txspeed[vci], m->m_pkthdr.len,
+ sc->txslot[chan].mbsize));
-#if !defined(MIDWAY_ENIONLY)
-#if !defined(MIDWAY_ADPONLY)
- if (sc->is_adaptec)
-#endif /* !MIDWAY_ADPONLY */
- return(1); /* adaptec can DMA anything in one go */
-#endif
-
-#if !defined(MIDWAY_ADPONLY)
- result = 0;
- if (len < EN_MINDMA) {
- if (!tx) /* XXX: conservative */
- return(1); /* will copy/DMA_JK */
- }
-
- if (tx) { /* byte burst? */
- needalign = (((uintptr_t) (void *) data) % sizeof(u_int32_t));
- if (needalign) {
- result++;
- sz = min(len, sizeof(u_int32_t) - needalign);
- len -= sz;
- data += sz;
- }
- }
-
- if (sc->alburst && len) {
- needalign = (((uintptr_t) (void *) data) & sc->bestburstmask);
- if (needalign) {
- result++; /* alburst */
- sz = min(len, sc->bestburstlen - needalign);
- len -= sz;
- }
- }
-
- if (len >= sc->bestburstlen) {
- sz = len / sc->bestburstlen;
- sz = sz * sc->bestburstlen;
- len -= sz;
- result++; /* best shot */
- }
-
- if (len) {
- result++; /* clean up */
- if (tx && (len % sizeof(u_int32_t)) != 0)
- result++; /* byte cleanup */
- }
-
- return(result);
-#endif /* !MIDWAY_ADPONLY */
+ MBUF_SET_TX(m, vci, flags, datalen, pad, map);
+
+ _IF_ENQUEUE(&sc->txslot[chan].q, m);
+
+ en_txdma(sc, &sc->txslot[chan]);
+
+ EN_UNLOCK(sc);
+ }
}
+/*********************************************************************/
+/*
+ * VCs
+ */
/*
- * en_mget: get an mbuf chain that can hold totlen bytes and return it
- * (for recv) [based on am7990_get from if_le and ieget from if_ie]
- * after this call the sum of all the m_len's in the chain will be totlen.
+ * en_loadvc: load a vc tab entry from a slot
+ *
+ * LOCK: locked, needed
*/
+static void
+en_loadvc(struct en_softc *sc, int vc)
+{
+ int slot;
+ uint32_t reg = en_read(sc, MID_VC(vc));
-STATIC INLINE struct mbuf *en_mget(sc, totlen, drqneed)
+ reg = MIDV_SETMODE(reg, MIDV_TRASH);
+ en_write(sc, MID_VC(vc), reg);
+ DELAY(27);
-struct en_softc *sc;
-u_int totlen, *drqneed;
+ if ((slot = sc->rxvc2slot[vc]) == RX_NONE)
+ return;
-{
- struct mbuf *m;
- struct mbuf *top, **mp;
- *drqneed = 0;
-
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return(NULL);
- m->m_pkthdr.rcvif = &sc->enif;
- m->m_pkthdr.len = totlen;
- m->m_len = MHLEN;
- top = NULL;
- mp = &top;
-
- /* if (top != NULL) then we've already got 1 mbuf on the chain */
- while (totlen > 0) {
- if (top) {
- MGET(m, M_DONTWAIT, MT_DATA);
- if (!m) {
- m_freem(top);
- return(NULL); /* out of mbufs */
- }
- m->m_len = MLEN;
- }
- if (totlen >= MINCLSIZE) {
- MCLGET(m, M_DONTWAIT);
- if ((m->m_flags & M_EXT) == 0) {
- m_free(m);
- m_freem(top);
- return(NULL); /* out of mbuf clusters */
- }
- m->m_len = MCLBYTES;
- }
- m->m_len = min(totlen, m->m_len);
- totlen -= m->m_len;
- *mp = m;
- mp = &m->m_next;
-
- *drqneed += en_dqneed(sc, m->m_data, m->m_len, 0);
-
- }
- return(top);
-}
+ /* no need to set CRC */
-/***********************************************************************/
+ /* read pointer = 0, desc. start = 0 */
+ en_write(sc, MID_DST_RP(vc), 0);
+ /* write pointer = 0 */
+ en_write(sc, MID_WP_ST_CNT(vc), 0);
+ /* set mode, size, loc */
+ en_write(sc, MID_VC(vc), sc->rxslot[slot].mode);
+
+ sc->rxslot[slot].cur = sc->rxslot[slot].start;
+
+ DBG(sc, VC, ("rx%d: assigned to VCI %d", slot, vc));
+}
/*
- * autoconfig stuff
+ * en_rxctl: turn on and off VCs for recv.
+ *
+ * LOCK: unlocked, needed
*/
+static int
+en_rxctl(struct en_softc *sc, struct atm_pseudoioctl *pi, int on)
+{
+ u_int vci, flags, slot;
+ uint32_t oldmode, newmode;
+
+ vci = ATM_PH_VCI(&pi->aph);
+ flags = ATM_PH_FLAGS(&pi->aph);
+
+ DBG(sc, IOCTL, ("%s vpi=%d, vci=%d, flags=%#x",
+ (on) ? "enable" : "disable", ATM_PH_VPI(&pi->aph), vci, flags));
+
+ if (ATM_PH_VPI(&pi->aph) || vci >= MID_N_VC)
+ return (EINVAL);
+
+ EN_LOCK(sc);
+
+ if (on) {
+ /*
+ * turn on VCI!
+ */
+ if (sc->rxvc2slot[vci] != RX_NONE)
+ return (EINVAL);
+ for (slot = 0; slot < sc->en_nrx; slot++)
+ if (sc->rxslot[slot].oth_flags & ENOTHER_FREE)
+ break;
+ if (slot == sc->en_nrx) {
+ EN_UNLOCK(sc);
+ return (ENOSPC);
+ }
-void en_attach(sc)
-
-struct en_softc *sc;
+ sc->rxvc2slot[vci] = slot;
+ sc->rxslot[slot].rxhand = NULL;
+ oldmode = sc->rxslot[slot].mode;
+ newmode = (flags & ATM_PH_AAL5) ? MIDV_AAL5 : MIDV_NOAAL;
+ sc->rxslot[slot].mode = MIDV_SETMODE(oldmode, newmode);
+ sc->rxslot[slot].atm_vci = vci;
+ sc->rxslot[slot].atm_flags = flags;
+ sc->rxslot[slot].oth_flags = 0;
+ sc->rxslot[slot].rxhand = pi->rxhand;
+
+ if (_IF_QLEN(&sc->rxslot[slot].indma) != 0 ||
+ _IF_QLEN(&sc->rxslot[slot].q) != 0)
+ panic("en_rxctl: left over mbufs on enable");
+ sc->txspeed[vci] = 0; /* full speed to start */
+ sc->txvc2slot[vci] = 0; /* init value */
+ sc->txslot[0].nref++; /* bump reference count */
+ en_loadvc(sc, vci); /* does debug printf for us */
+
+ EN_UNLOCK(sc);
+ return (0);
+ }
-{
- struct ifnet *ifp = &sc->enif;
- int sz;
- u_int32_t reg, lcv, check, ptr, sav, midvloc;
-
- /*
- * probe card to determine memory size. the stupid ENI card always
- * reports to PCI that it needs 4MB of space (2MB regs and 2MB RAM).
- * if it has less than 2MB RAM the addresses wrap in the RAM address space.
- * (i.e. on a 512KB card addresses 0x3ffffc, 0x37fffc, and 0x2ffffc
- * are aliases for 0x27fffc [note that RAM starts at offset 0x200000]).
- */
-
- if (sc->en_busreset)
- sc->en_busreset(sc);
- EN_WRITE(sc, MID_RESID, 0x0); /* reset card before touching RAM */
- for (lcv = MID_PROBEOFF; lcv <= MID_MAXOFF ; lcv += MID_PROBSIZE) {
- EN_WRITE(sc, lcv, lcv); /* data[address] = address */
- for (check = MID_PROBEOFF ; check < lcv ; check += MID_PROBSIZE) {
- reg = EN_READ(sc, check);
- if (reg != check) { /* found an alias! */
- goto done_probe; /* and quit */
- }
- }
- }
-done_probe:
- lcv -= MID_PROBSIZE; /* take one step back */
- sc->en_obmemsz = (lcv + 4) - MID_RAMOFF;
-
- /*
- * determine the largest DMA burst supported
- */
-
- en_dmaprobe(sc);
-
- /*
- * "hello world"
- */
-
- if (sc->en_busreset)
- sc->en_busreset(sc);
- EN_WRITE(sc, MID_RESID, 0x0); /* reset */
- for (lcv = MID_RAMOFF ; lcv < MID_RAMOFF + sc->en_obmemsz ; lcv += 4)
- EN_WRITE(sc, lcv, 0); /* zero memory */
-
- reg = EN_READ(sc, MID_RESID);
-
- printf("%s: ATM midway v%d, board IDs %d.%d, %s%s%s, %ldKB on-board RAM\n",
- sc->sc_dev.dv_xname, MID_VER(reg), MID_MID(reg), MID_DID(reg),
- (MID_IS_SABRE(reg)) ? "sabre controller, " : "",
- (MID_IS_SUNI(reg)) ? "SUNI" : "Utopia",
- (!MID_IS_SUNI(reg) && MID_IS_UPIPE(reg)) ? " (pipelined)" : "",
- (long)sc->en_obmemsz / 1024);
-
- if (sc->is_adaptec) {
- if (sc->bestburstlen == 64 && sc->alburst == 0)
- printf("%s: passed 64 byte DMA test\n", sc->sc_dev.dv_xname);
- else
- printf("%s: FAILED DMA TEST: burst=%d, alburst=%d\n",
- sc->sc_dev.dv_xname, sc->bestburstlen, sc->alburst);
- } else {
- printf("%s: maximum DMA burst length = %d bytes%s\n", sc->sc_dev.dv_xname,
- sc->bestburstlen, (sc->alburst) ? " (must align)" : "");
- }
-
- /*
- * link into network subsystem and prepare card
- */
-
-#if defined(__NetBSD__) || defined(__OpenBSD__)
- bcopy(sc->sc_dev.dv_xname, sc->enif.if_xname, IFNAMSIZ);
-#endif
- sc->enif.if_softc = sc;
- ifp->if_flags = IFF_SIMPLEX|IFF_NOTRAILERS;
- ifp->if_ioctl = en_ioctl;
- ifp->if_output = atm_output;
- ifp->if_start = en_start;
-
- /*
- * init softc
- */
-
- for (lcv = 0 ; lcv < MID_N_VC ; lcv++) {
- sc->rxvc2slot[lcv] = RX_NONE;
- sc->txspeed[lcv] = 0; /* full */
- sc->txvc2slot[lcv] = 0; /* full speed == slot 0 */
- }
-
- sz = sc->en_obmemsz - (MID_BUFOFF - MID_RAMOFF);
- ptr = sav = MID_BUFOFF;
- ptr = roundup(ptr, EN_TXSZ * 1024); /* align */
- sz = sz - (ptr - sav);
- if (EN_TXSZ*1024 * EN_NTX > sz) {
- printf("%s: EN_NTX/EN_TXSZ too big\n", sc->sc_dev.dv_xname);
- return;
- }
- for (lcv = 0 ; lcv < EN_NTX ; lcv++) {
- sc->txslot[lcv].mbsize = 0;
- sc->txslot[lcv].start = ptr;
- ptr += (EN_TXSZ * 1024);
- sz -= (EN_TXSZ * 1024);
- sc->txslot[lcv].stop = ptr;
- sc->txslot[lcv].nref = 0;
- bzero(&sc->txslot[lcv].indma, sizeof(sc->txslot[lcv].indma));
- bzero(&sc->txslot[lcv].q, sizeof(sc->txslot[lcv].q));
-#ifdef EN_DEBUG
- printf("%s: tx%d: start 0x%x, stop 0x%x\n", sc->sc_dev.dv_xname, lcv,
- sc->txslot[lcv].start, sc->txslot[lcv].stop);
-#endif
- }
-
- sav = ptr;
- ptr = roundup(ptr, EN_RXSZ * 1024); /* align */
- sz = sz - (ptr - sav);
- sc->en_nrx = sz / (EN_RXSZ * 1024);
- if (sc->en_nrx <= 0) {
- printf("%s: EN_NTX/EN_TXSZ/EN_RXSZ too big\n", sc->sc_dev.dv_xname);
- return;
- }
-
- /*
- * ensure that there is always one VC slot on the service list free
- * so that we can tell the difference between a full and empty list.
- */
- if (sc->en_nrx >= MID_N_VC)
- sc->en_nrx = MID_N_VC - 1;
-
- for (lcv = 0 ; lcv < sc->en_nrx ; lcv++) {
- sc->rxslot[lcv].rxhand = NULL;
- sc->rxslot[lcv].oth_flags = ENOTHER_FREE;
- bzero(&sc->rxslot[lcv].indma, sizeof(sc->rxslot[lcv].indma));
- bzero(&sc->rxslot[lcv].q, sizeof(sc->rxslot[lcv].q));
- midvloc = sc->rxslot[lcv].start = ptr;
- ptr += (EN_RXSZ * 1024);
- sz -= (EN_RXSZ * 1024);
- sc->rxslot[lcv].stop = ptr;
- midvloc = midvloc - MID_RAMOFF;
- midvloc = (midvloc & ~((EN_RXSZ*1024) - 1)) >> 2; /* mask, cvt to words */
- midvloc = midvloc >> MIDV_LOCTOPSHFT; /* we only want the top 11 bits */
- midvloc = (midvloc & MIDV_LOCMASK) << MIDV_LOCSHIFT;
- sc->rxslot[lcv].mode = midvloc |
- (en_k2sz(EN_RXSZ) << MIDV_SZSHIFT) | MIDV_TRASH;
+ /*
+ * turn off VCI
+ */
+ if (sc->rxvc2slot[vci] == RX_NONE) {
+ EN_UNLOCK(sc);
+ return (EINVAL);
+ }
+ slot = sc->rxvc2slot[vci];
+ if ((sc->rxslot[slot].oth_flags & (ENOTHER_FREE|ENOTHER_DRAIN)) != 0) {
+ EN_UNLOCK(sc);
+ return (EINVAL);
+ }
-#ifdef EN_DEBUG
- printf("%s: rx%d: start 0x%x, stop 0x%x, mode 0x%x\n", sc->sc_dev.dv_xname,
- lcv, sc->rxslot[lcv].start, sc->rxslot[lcv].stop, sc->rxslot[lcv].mode);
-#endif
- }
-
-#ifdef EN_STAT
- sc->vtrash = sc->otrash = sc->mfix = sc->txmbovr = sc->dmaovr = 0;
- sc->txoutspace = sc->txdtqout = sc->launch = sc->lheader = sc->ltail = 0;
- sc->hwpull = sc->swadd = sc->rxqnotus = sc->rxqus = sc->rxoutboth = 0;
- sc->rxdrqout = sc->ttrash = sc->rxmbufout = sc->mfixfail = 0;
- sc->headbyte = sc->tailbyte = sc->tailflush = 0;
-#endif
- sc->need_drqs = sc->need_dtqs = 0;
+ oldmode = en_read(sc, MID_VC(vci));
+ newmode = MIDV_SETMODE(oldmode, MIDV_TRASH) & ~MIDV_INSERVICE;
+ en_write(sc, MID_VC(vci), (newmode | (oldmode & MIDV_INSERVICE)));
- printf("%s: %d %dKB receive buffers, %d %dKB transmit buffers allocated\n",
- sc->sc_dev.dv_xname, sc->en_nrx, EN_RXSZ, EN_NTX, EN_TXSZ);
+ /* halt in tracks, be careful to preserve inservice bit */
+ DELAY(27);
+ sc->rxslot[slot].rxhand = NULL;
+ sc->rxslot[slot].mode = newmode;
- printf("%s: End Station Identifier (mac address) %6D\n",
- sc->sc_dev.dv_xname, sc->macaddr, ":");
+ sc->txslot[sc->txvc2slot[vci]].nref--;
+ sc->txspeed[vci] = 0;
+ sc->txvc2slot[vci] = 0;
- /*
- * final commit
- */
+ /* if stuff is still going on we are going to have to drain it out */
+ if (_IF_QLEN(&sc->rxslot[slot].indma) != 0 ||
+ _IF_QLEN(&sc->rxslot[slot].q) != 0 ||
+ (sc->rxslot[slot].oth_flags & ENOTHER_SWSL) != 0) {
+ sc->rxslot[slot].oth_flags |= ENOTHER_DRAIN;
+ } else {
+ sc->rxslot[slot].oth_flags = ENOTHER_FREE;
+ sc->rxslot[slot].atm_vci = RX_NONE;
+ sc->rxvc2slot[vci] = RX_NONE;
+ }
+ EN_UNLOCK(sc);
- if_attach(ifp);
- atm_ifattach(ifp);
+ DBG(sc, IOCTL, ("rx%d: VCI %d is now %s", slot, vci,
+ (sc->rxslot[slot].oth_flags & ENOTHER_DRAIN) ? "draining" : "free"));
-#if NBPF > 0
- BPFATTACH(ifp, DLT_ATM_RFC1483, sizeof(struct atmllc));
-#endif
+ return (0);
}
+/*********************************************************************/
+/*
+ * starting/stopping the card
+ */
/*
- * en_dmaprobe: helper function for en_attach.
- *
- * see how the card handles DMA by running a few DMA tests. we need
- * to figure out the largest number of bytes we can DMA in one burst
- * ("bestburstlen"), and if the starting address for a burst needs to
- * be aligned on any sort of boundary or not ("alburst").
+ * en_reset_ul: reset the board, throw away work in progress.
+ * must en_init to recover.
*
- * typical findings:
- * sparc1: bestburstlen=4, alburst=0 (ick, broken DMA!)
- * sparc2: bestburstlen=64, alburst=1
- * p166: bestburstlen=64, alburst=0
+ * LOCK: locked, needed
*/
+static void
+en_reset_ul(struct en_softc *sc)
+{
+ struct en_map *map;
+ struct mbuf *m;
+ int lcv, slot;
+
+ if_printf(&sc->enif, "reset\n");
+
+ if (sc->en_busreset)
+ sc->en_busreset(sc);
+ en_write(sc, MID_RESID, 0x0); /* reset hardware */
+
+ /*
+ * recv: dump any mbufs we are dma'ing into, if DRAINing, then a reset
+ * will free us!
+ */
+ for (lcv = 0 ; lcv < MID_N_VC ; lcv++) {
+ if (sc->rxvc2slot[lcv] == RX_NONE)
+ continue;
+ slot = sc->rxvc2slot[lcv];
+
+ for (;;) {
+ _IF_DEQUEUE(&sc->rxslot[slot].indma, m);
+ if (m == NULL)
+ break;
+ map = (void *)m->m_pkthdr.rcvif;
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+ }
+ for (;;) {
+ _IF_DEQUEUE(&sc->rxslot[slot].q, m);
+ if (m == NULL)
+ break;
+ m_freem(m);
+ }
+ sc->rxslot[slot].oth_flags &= ~ENOTHER_SWSL;
+ if (sc->rxslot[slot].oth_flags & ENOTHER_DRAIN) {
+ sc->rxslot[slot].oth_flags = ENOTHER_FREE;
+ sc->rxvc2slot[lcv] = RX_NONE;
+ DBG(sc, INIT, ("rx%d: VCI %d is now free", slot, lcv));
+ }
+ }
-STATIC void en_dmaprobe(sc)
-
-struct en_softc *sc;
+ /*
+ * xmit: dump everything
+ */
+ for (lcv = 0 ; lcv < EN_NTX ; lcv++) {
+ for (;;) {
+ _IF_DEQUEUE(&sc->txslot[lcv].indma, m);
+ if (m == NULL)
+ break;
+ map = (void *)m->m_pkthdr.rcvif;
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+ }
+ for (;;) {
+ _IF_DEQUEUE(&sc->txslot[lcv].q, m);
+ if (m == NULL)
+ break;
+ map = (void *)m->m_pkthdr.rcvif;
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+ }
+ sc->txslot[lcv].mbsize = 0;
+ }
+}
+/*
+ * en_reset: reset the board, throw away work in progress.
+ * must en_init to recover.
+ *
+ * LOCK: unlocked, needed
+ *
+ * Use en_reset_ul if you alreay have the lock
+ */
+void
+en_reset(struct en_softc *sc)
{
- u_int32_t srcbuf[64], dstbuf[64];
- u_int8_t *sp, *dp;
- int bestalgn, bestnotalgn, lcv, try;
-
- sc->alburst = 0;
-
- sp = (u_int8_t *) srcbuf;
- while ((((unsigned long) sp) % MIDDMA_MAXBURST) != 0)
- sp += 4;
- dp = (u_int8_t *) dstbuf;
- while ((((unsigned long) dp) % MIDDMA_MAXBURST) != 0)
- dp += 4;
-
- bestalgn = bestnotalgn = en_dmaprobe_doit(sc, sp, dp, 0);
-
- for (lcv = 4 ; lcv < MIDDMA_MAXBURST ; lcv += 4) {
- try = en_dmaprobe_doit(sc, sp+lcv, dp+lcv, 0);
- if (try < bestnotalgn)
- bestnotalgn = try;
- }
-
- if (bestalgn != bestnotalgn) /* need bursts aligned */
- sc->alburst = 1;
-
- sc->bestburstlen = bestalgn;
- sc->bestburstshift = en_log2(bestalgn);
- sc->bestburstmask = sc->bestburstlen - 1; /* must be power of 2 */
- sc->bestburstcode = en_sz2b(bestalgn);
-
-#if 1 /* __FreeBSD__ */
- /*
- * correct pci chipsets should be able to handle misaligned-64-byte DMA.
- * but there are too many broken chipsets around. we try to work around
- * by finding the best workable dma size, but still some broken machines
- * exhibit the problem later. so warn it here.
- */
- if (bestalgn != 64 || sc->alburst != 0) {
- printf("%s: WARNING: DMA test detects a broken PCI chipset!\n",
- sc->sc_dev.dv_xname);
- printf(" trying to work around the problem... but if this doesn't\n");
- printf(" work for you, you'd better switch to a newer motherboard.\n");
- }
-#endif /* 1 */
- return;
+ EN_LOCK(sc);
+ en_reset_ul(sc);
+ EN_UNLOCK(sc);
}
/*
- * en_dmaprobe_doit: do actual testing
+ * en_init: init board and sync the card with the data in the softc.
+ *
+ * LOCK: locked, needed
*/
-
-STATIC int
-en_dmaprobe_doit(sc, sp, dp, wmtry)
-
-struct en_softc *sc;
-u_int8_t *sp, *dp;
-int wmtry;
-
+static void
+en_init(struct en_softc *sc)
{
- int lcv, retval = 4, cnt, count;
- u_int32_t reg, bcode, midvloc;
-
- /*
- * set up a 1k buffer at MID_BUFOFF
- */
-
- if (sc->en_busreset)
- sc->en_busreset(sc);
- EN_WRITE(sc, MID_RESID, 0x0); /* reset card before touching RAM */
-
- midvloc = ((MID_BUFOFF - MID_RAMOFF) / sizeof(u_int32_t)) >> MIDV_LOCTOPSHFT;
- EN_WRITE(sc, MIDX_PLACE(0), MIDX_MKPLACE(en_k2sz(1), midvloc));
- EN_WRITE(sc, MID_VC(0), (midvloc << MIDV_LOCSHIFT)
- | (en_k2sz(1) << MIDV_SZSHIFT) | MIDV_TRASH);
- EN_WRITE(sc, MID_DST_RP(0), 0);
- EN_WRITE(sc, MID_WP_ST_CNT(0), 0);
-
- for (lcv = 0 ; lcv < 68 ; lcv++) /* set up sample data */
- sp[lcv] = lcv+1;
- EN_WRITE(sc, MID_MAST_CSR, MID_MCSR_ENDMA); /* enable DMA (only) */
-
- sc->drq_chip = MID_DRQ_REG2A(EN_READ(sc, MID_DMA_RDRX));
- sc->dtq_chip = MID_DTQ_REG2A(EN_READ(sc, MID_DMA_RDTX));
-
- /*
- * try it now . . . DMA it out, then DMA it back in and compare
- *
- * note: in order to get the dma stuff to reverse directions it wants
- * the "end" flag set! since we are not dma'ing valid data we may
- * get an ident mismatch interrupt (which we will ignore).
- *
- * note: we've got two different tests rolled up in the same loop
- * if (wmtry)
- * then we are doing a wmaybe test and wmtry is a byte count
- * else we are doing a burst test
- */
-
- for (lcv = 8 ; lcv <= MIDDMA_MAXBURST ; lcv = lcv * 2) {
-
-#ifdef EN_DEBUG
- printf("DMA test lcv=%d, sp=0x%lx, dp=0x%lx, wmtry=%d\n",
- lcv, (unsigned long)sp, (unsigned long)dp, wmtry);
-#endif
-
- /* zero SRAM and dest buffer */
- for (cnt = 0 ; cnt < 1024; cnt += 4)
- EN_WRITE(sc, MID_BUFOFF+cnt, 0); /* zero memory */
- for (cnt = 0 ; cnt < 68 ; cnt++)
- dp[cnt] = 0;
-
- if (wmtry) {
- count = (sc->bestburstlen - sizeof(u_int32_t)) / sizeof(u_int32_t);
- bcode = en_dmaplan[count].bcode;
- count = wmtry >> en_dmaplan[count].divshift;
- } else {
- bcode = en_sz2b(lcv);
- count = 1;
- }
- if (sc->is_adaptec)
- EN_WRITE(sc, sc->dtq_chip, MID_MK_TXQ_ADP(lcv, 0, MID_DMA_END, 0));
- else
- EN_WRITE(sc, sc->dtq_chip, MID_MK_TXQ_ENI(count, 0, MID_DMA_END, bcode));
- EN_WRITE(sc, sc->dtq_chip+4, vtophys(sp));
- EN_WRITE(sc, MID_DMA_WRTX, MID_DTQ_A2REG(sc->dtq_chip+8));
- cnt = 1000;
- while (EN_READ(sc, MID_DMA_RDTX) == MID_DTQ_A2REG(sc->dtq_chip)) {
- DELAY(1);
- cnt--;
- if (cnt == 0) {
- printf("%s: unexpected timeout in tx DMA test\n", sc->sc_dev.dv_xname);
- return(retval); /* timeout, give up */
- }
- }
- EN_WRAPADD(MID_DTQOFF, MID_DTQEND, sc->dtq_chip, 8);
- reg = EN_READ(sc, MID_INTACK);
- if ((reg & MID_INT_DMA_TX) != MID_INT_DMA_TX) {
- printf("%s: unexpected status in tx DMA test: 0x%x\n",
- sc->sc_dev.dv_xname, reg);
- return(retval);
- }
- EN_WRITE(sc, MID_MAST_CSR, MID_MCSR_ENDMA); /* re-enable DMA (only) */
-
- /* "return to sender..." address is known ... */
-
- if (sc->is_adaptec)
- EN_WRITE(sc, sc->drq_chip, MID_MK_RXQ_ADP(lcv, 0, MID_DMA_END, 0));
- else
- EN_WRITE(sc, sc->drq_chip, MID_MK_RXQ_ENI(count, 0, MID_DMA_END, bcode));
- EN_WRITE(sc, sc->drq_chip+4, vtophys(dp));
- EN_WRITE(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_chip+8));
- cnt = 1000;
- while (EN_READ(sc, MID_DMA_RDRX) == MID_DRQ_A2REG(sc->drq_chip)) {
- DELAY(1);
- cnt--;
- if (cnt == 0) {
- printf("%s: unexpected timeout in rx DMA test\n", sc->sc_dev.dv_xname);
- return(retval); /* timeout, give up */
- }
- }
- EN_WRAPADD(MID_DRQOFF, MID_DRQEND, sc->drq_chip, 8);
- reg = EN_READ(sc, MID_INTACK);
- if ((reg & MID_INT_DMA_RX) != MID_INT_DMA_RX) {
- printf("%s: unexpected status in rx DMA test: 0x%x\n",
- sc->sc_dev.dv_xname, reg);
- return(retval);
- }
- EN_WRITE(sc, MID_MAST_CSR, MID_MCSR_ENDMA); /* re-enable DMA (only) */
-
- if (wmtry) {
- return(bcmp(sp, dp, wmtry)); /* wmtry always exits here, no looping */
- }
-
- if (bcmp(sp, dp, lcv))
- return(retval); /* failed, use last value */
+ int vc, slot;
+ uint32_t loc;
+
+ if ((sc->enif.if_flags & IFF_UP) == 0) {
+ DBG(sc, INIT, ("going down"));
+ en_reset(sc); /* to be safe */
+ sc->enif.if_flags &= ~IFF_RUNNING; /* disable */
+ return;
+ }
- retval = lcv;
+ DBG(sc, INIT, ("going up"));
+ sc->enif.if_flags |= IFF_RUNNING; /* enable */
+
+ if (sc->en_busreset)
+ sc->en_busreset(sc);
+ en_write(sc, MID_RESID, 0x0); /* reset */
+
+ /*
+ * init obmem data structures: vc tab, dma q's, slist.
+ *
+ * note that we set drq_free/dtq_free to one less than the total number
+ * of DTQ/DRQs present. we do this because the card uses the condition
+ * (drq_chip == drq_us) to mean "list is empty"... but if you allow the
+ * circular list to be completely full then (drq_chip == drq_us) [i.e.
+ * the drq_us pointer will wrap all the way around]. by restricting
+ * the number of active requests to (N - 1) we prevent the list from
+ * becoming completely full. note that the card will sometimes give
+ * us an interrupt for a DTQ/DRQ we have already processes... this helps
+ * keep that interrupt from messing us up.
+ */
+
+ for (vc = 0; vc < MID_N_VC; vc++)
+ en_loadvc(sc, vc);
+
+ bzero(&sc->drq, sizeof(sc->drq));
+ sc->drq_free = MID_DRQ_N - 1;
+ sc->drq_chip = MID_DRQ_REG2A(en_read(sc, MID_DMA_RDRX));
+ en_write(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_chip));
+ sc->drq_us = sc->drq_chip;
+
+ bzero(&sc->dtq, sizeof(sc->dtq));
+ sc->dtq_free = MID_DTQ_N - 1;
+ sc->dtq_chip = MID_DTQ_REG2A(en_read(sc, MID_DMA_RDTX));
+ en_write(sc, MID_DMA_WRTX, MID_DRQ_A2REG(sc->dtq_chip));
+ sc->dtq_us = sc->dtq_chip;
+
+ sc->hwslistp = MID_SL_REG2A(en_read(sc, MID_SERV_WRITE));
+ sc->swsl_size = sc->swsl_head = sc->swsl_tail = 0;
+
+ DBG(sc, INIT, ("drq free/chip: %d/0x%x, dtq free/chip: %d/0x%x, "
+ "hwslist: 0x%x", sc->drq_free, sc->drq_chip, sc->dtq_free,
+ sc->dtq_chip, sc->hwslistp));
+
+ for (slot = 0 ; slot < EN_NTX ; slot++) {
+ sc->txslot[slot].bfree = EN_TXSZ * 1024;
+ en_write(sc, MIDX_READPTR(slot), 0);
+ en_write(sc, MIDX_DESCSTART(slot), 0);
+ loc = sc->txslot[slot].cur = sc->txslot[slot].start;
+ loc = loc - MID_RAMOFF;
+ /* mask, cvt to words */
+ loc = (loc & ~((EN_TXSZ * 1024) - 1)) >> 2;
+ /* top 11 bits */
+ loc = loc >> MIDV_LOCTOPSHFT;
+ en_write(sc, MIDX_PLACE(slot), MIDX_MKPLACE(en_k2sz(EN_TXSZ),
+ loc));
+ DBG(sc, INIT, ("tx%d: place 0x%x", slot,
+ (u_int)en_read(sc, MIDX_PLACE(slot))));
+ }
- }
- return(retval); /* studly 64 byte DMA present! oh baby!! */
+ /*
+ * enable!
+ */
+ en_write(sc, MID_INTENA, MID_INT_TX | MID_INT_DMA_OVR | MID_INT_IDENT |
+ MID_INT_LERR | MID_INT_DMA_ERR | MID_INT_DMA_RX | MID_INT_DMA_TX |
+ MID_INT_SERVICE | /* MID_INT_SUNI | */ MID_INT_STATS);
+ en_write(sc, MID_MAST_CSR, MID_SETIPL(sc->ipl) | MID_MCSR_ENDMA |
+ MID_MCSR_ENTX | MID_MCSR_ENRX);
}
-/***********************************************************************/
+/*********************************************************************/
+/*
+ * Ioctls
+ */
/*
* en_ioctl: handle ioctl requests
@@ -1037,2104 +1462,1466 @@ int wmtry;
* value, subtract one from sc->txslot[0].nref, add one to the
* sc->txslot[slot].nref, set sc->txvc2slot[vci] = slot, and then set
* txspeed[vci].
+ *
+ * LOCK: unlocked, needed
*/
-
-STATIC int en_ioctl(ifp, cmd, data)
-
-struct ifnet *ifp;
-EN_IOCTL_CMDT cmd;
-caddr_t data;
-
+static int
+en_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
- struct en_softc *sc = (struct en_softc *) ifp->if_softc;
- struct ifaddr *ifa = (struct ifaddr *) data;
- struct ifreq *ifr = (struct ifreq *) data;
- struct atm_pseudoioctl *api = (struct atm_pseudoioctl *)data;
-#ifdef NATM
- struct atm_rawioctl *ario = (struct atm_rawioctl *)data;
- int slot;
-#endif
- int s, error = 0;
+ struct en_softc *sc = (struct en_softc *)ifp->if_softc;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct atm_pseudoioctl *api = (struct atm_pseudoioctl *)data;
+ int error = 0;
- s = splnet();
+ switch (cmd) {
- switch (cmd) {
- case SIOCATMENA: /* enable circuit for recv */
+ case SIOCATMENA: /* enable circuit for recv */
error = en_rxctl(sc, api, 1);
break;
- case SIOCATMDIS: /* disable circuit for recv */
+ case SIOCATMDIS: /* disable circuit for recv */
error = en_rxctl(sc, api, 0);
break;
-#ifdef NATM
- case SIOCXRAWATM:
- if ((slot = sc->rxvc2slot[ario->npcb->npcb_vci]) == RX_NONE) {
- error = EINVAL;
- break;
- }
- if (ario->rawvalue > EN_RXSZ*1024)
- ario->rawvalue = EN_RXSZ*1024;
- if (ario->rawvalue) {
- sc->rxslot[slot].oth_flags |= ENOTHER_RAW;
- sc->rxslot[slot].raw_threshold = ario->rawvalue;
- } else {
- sc->rxslot[slot].oth_flags &= (~ENOTHER_RAW);
- sc->rxslot[slot].raw_threshold = 0;
- }
-#ifdef EN_DEBUG
- printf("%s: rxvci%d: turn %s raw (boodi) mode\n",
- sc->sc_dev.dv_xname, ario->npcb->npcb_vci,
- (ario->rawvalue) ? "on" : "off");
-#endif
- break;
-#endif
- case SIOCSIFADDR:
+ case SIOCSIFADDR:
+ EN_LOCK(sc);
ifp->if_flags |= IFF_UP;
#if defined(INET) || defined(INET6)
if (ifa->ifa_addr->sa_family == AF_INET
|| ifa->ifa_addr->sa_family == AF_INET6) {
- en_reset(sc);
- en_init(sc);
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ en_reset_ul(sc);
+ en_init(sc);
+ }
ifa->ifa_rtrequest = atm_rtrequest; /* ??? */
+ EN_UNLOCK(sc);
break;
}
#endif /* INET */
- /* what to do if not INET? */
- en_reset(sc);
- en_init(sc);
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ en_reset_ul(sc);
+ en_init(sc);
+ }
+ EN_UNLOCK(sc);
break;
- case SIOCGIFADDR:
- error = EINVAL;
+ case SIOCSIFFLAGS:
+ EN_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_flags & IFF_RUNNING))
+ en_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ en_reset_ul(sc);
+ }
+ EN_UNLOCK(sc);
break;
- case SIOCSIFFLAGS:
- error = EINVAL;
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > ATMMTU) {
+ error = EINVAL;
+ break;
+ }
+ ifp->if_mtu = ifr->ifr_mtu;
break;
-#if defined(SIOCSIFMTU) /* ??? copied from if_de */
-#if !defined(ifr_mtu)
-#define ifr_mtu ifr_metric
-#endif
- case SIOCSIFMTU:
- /*
- * Set the interface MTU.
- */
-#ifdef notsure
- if (ifr->ifr_mtu > ATMMTU) {
+ default:
error = EINVAL;
break;
- }
-#endif
- ifp->if_mtu = ifr->ifr_mtu;
- /* XXXCDC: do we really need to reset on MTU size change? */
- en_reset(sc);
- en_init(sc);
- break;
-#endif /* SIOCSIFMTU */
-
- default:
- error = EINVAL;
- break;
- }
- splx(s);
- return error;
+ }
+ return (error);
}
-
+/*********************************************************************/
/*
- * en_rxctl: turn on and off VCs for recv.
+ * Sysctl's
*/
-STATIC int en_rxctl(sc, pi, on)
-
-struct en_softc *sc;
-struct atm_pseudoioctl *pi;
-int on;
-
+/*
+ * Sysctl handler for internal statistics
+ *
+ * LOCK: unlocked, needed
+ */
+static int
+en_sysctl_istats(SYSCTL_HANDLER_ARGS)
{
- u_int s, vci, flags, slot;
- u_int32_t oldmode, newmode;
-
- vci = ATM_PH_VCI(&pi->aph);
- flags = ATM_PH_FLAGS(&pi->aph);
-
-#ifdef EN_DEBUG
- printf("%s: %s vpi=%d, vci=%d, flags=%d\n", sc->sc_dev.dv_xname,
- (on) ? "enable" : "disable", ATM_PH_VPI(&pi->aph), vci, flags);
-#endif
-
- if (ATM_PH_VPI(&pi->aph) || vci >= MID_N_VC)
- return(EINVAL);
-
- /*
- * turn on VCI!
- */
-
- if (on) {
- if (sc->rxvc2slot[vci] != RX_NONE)
- return(EINVAL);
- for (slot = 0 ; slot < sc->en_nrx ; slot++)
- if (sc->rxslot[slot].oth_flags & ENOTHER_FREE)
- break;
- if (slot == sc->en_nrx)
- return(ENOSPC);
- sc->rxvc2slot[vci] = slot;
- sc->rxslot[slot].rxhand = NULL;
- oldmode = sc->rxslot[slot].mode;
- newmode = (flags & ATM_PH_AAL5) ? MIDV_AAL5 : MIDV_NOAAL;
- sc->rxslot[slot].mode = MIDV_SETMODE(oldmode, newmode);
- sc->rxslot[slot].atm_vci = vci;
- sc->rxslot[slot].atm_flags = flags;
- sc->rxslot[slot].oth_flags = 0;
- sc->rxslot[slot].rxhand = pi->rxhand;
- if (sc->rxslot[slot].indma.ifq_head || sc->rxslot[slot].q.ifq_head)
- panic("en_rxctl: left over mbufs on enable");
- sc->txspeed[vci] = 0; /* full speed to start */
- sc->txvc2slot[vci] = 0; /* init value */
- sc->txslot[0].nref++; /* bump reference count */
- en_loadvc(sc, vci); /* does debug printf for us */
- return(0);
- }
-
- /*
- * turn off VCI
- */
-
- if (sc->rxvc2slot[vci] == RX_NONE)
- return(EINVAL);
- slot = sc->rxvc2slot[vci];
- if ((sc->rxslot[slot].oth_flags & (ENOTHER_FREE|ENOTHER_DRAIN)) != 0)
- return(EINVAL);
- s = splimp(); /* block out enintr() */
- oldmode = EN_READ(sc, MID_VC(vci));
- newmode = MIDV_SETMODE(oldmode, MIDV_TRASH) & ~MIDV_INSERVICE;
- EN_WRITE(sc, MID_VC(vci), (newmode | (oldmode & MIDV_INSERVICE)));
- /* halt in tracks, be careful to preserve inserivce bit */
- DELAY(27);
- sc->rxslot[slot].rxhand = NULL;
- sc->rxslot[slot].mode = newmode;
-
- sc->txslot[sc->txvc2slot[vci]].nref--;
- sc->txspeed[vci] = 0;
- sc->txvc2slot[vci] = 0;
-
- /* if stuff is still going on we are going to have to drain it out */
- if (sc->rxslot[slot].indma.ifq_head ||
- sc->rxslot[slot].q.ifq_head ||
- (sc->rxslot[slot].oth_flags & ENOTHER_SWSL) != 0) {
- sc->rxslot[slot].oth_flags |= ENOTHER_DRAIN;
- } else {
- sc->rxslot[slot].oth_flags = ENOTHER_FREE;
- sc->rxslot[slot].atm_vci = RX_NONE;
- sc->rxvc2slot[vci] = RX_NONE;
- }
- splx(s); /* enable enintr() */
-#ifdef EN_DEBUG
- printf("%s: rx%d: VCI %d is now %s\n", sc->sc_dev.dv_xname, slot, vci,
- (sc->rxslot[slot].oth_flags & ENOTHER_DRAIN) ? "draining" : "free");
-#endif
- return(0);
+ struct en_softc *sc = arg1;
+ struct sbuf *sb;
+ int error;
+
+ sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
+ sbuf_clear(sb);
+
+ EN_LOCK(sc);
+
+#define DO(NAME) sbuf_printf(sb, #NAME": %u\n", sc->stats.NAME)
+ DO(vtrash);
+ DO(otrash);
+ DO(ttrash);
+ DO(mfixaddr);
+ DO(mfixlen);
+ DO(mfixfail);
+ DO(txmbovr);
+ DO(dmaovr);
+ DO(txoutspace);
+ DO(txdtqout);
+ DO(launch);
+ DO(hwpull);
+ DO(swadd);
+ DO(rxqnotus);
+ DO(rxqus);
+ DO(rxdrqout);
+ DO(rxmbufout);
+ DO(txnomap);
+#undef DO
+
+ EN_UNLOCK(sc);
+
+ sbuf_finish(sb);
+ error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
+ sbuf_delete(sb);
+ return (error);
}
-/***********************************************************************/
-
+/*********************************************************************/
/*
- * en_reset: reset the board, throw away work in progress.
- * must en_init to recover.
+ * Interrupts
*/
-void en_reset(sc)
-
-struct en_softc *sc;
-
+/*
+ * Transmit interrupt handler
+ *
+ * check for tx complete, if detected then this means that some space
+ * has come free on the card. we must account for it and arrange to
+ * kick the channel to life (in case it is stalled waiting on the card).
+ *
+ * LOCK: locked, needed
+ */
+static uint32_t
+en_intr_tx(struct en_softc *sc, uint32_t reg)
{
- struct mbuf *m;
- int lcv, slot;
-
-#ifdef EN_DEBUG
- printf("%s: reset\n", sc->sc_dev.dv_xname);
-#endif
-
- if (sc->en_busreset)
- sc->en_busreset(sc);
- EN_WRITE(sc, MID_RESID, 0x0); /* reset hardware */
-
- /*
- * recv: dump any mbufs we are dma'ing into, if DRAINing, then a reset
- * will free us!
- */
-
- for (lcv = 0 ; lcv < MID_N_VC ; lcv++) {
- if (sc->rxvc2slot[lcv] == RX_NONE)
- continue;
- slot = sc->rxvc2slot[lcv];
- while (1) {
- _IF_DEQUEUE(&sc->rxslot[slot].indma, m);
- if (m == NULL)
- break; /* >>> exit 'while(1)' here <<< */
- m_freem(m);
- }
- while (1) {
- _IF_DEQUEUE(&sc->rxslot[slot].q, m);
- if (m == NULL)
- break; /* >>> exit 'while(1)' here <<< */
- m_freem(m);
- }
- sc->rxslot[slot].oth_flags &= ~ENOTHER_SWSL;
- if (sc->rxslot[slot].oth_flags & ENOTHER_DRAIN) {
- sc->rxslot[slot].oth_flags = ENOTHER_FREE;
- sc->rxvc2slot[lcv] = RX_NONE;
-#ifdef EN_DEBUG
- printf("%s: rx%d: VCI %d is now free\n", sc->sc_dev.dv_xname, slot, lcv);
-#endif
- }
- }
-
- /*
- * xmit: dump everything
- */
-
- for (lcv = 0 ; lcv < EN_NTX ; lcv++) {
- while (1) {
- _IF_DEQUEUE(&sc->txslot[lcv].indma, m);
- if (m == NULL)
- break; /* >>> exit 'while(1)' here <<< */
- m_freem(m);
- }
- while (1) {
- _IF_DEQUEUE(&sc->txslot[lcv].q, m);
- if (m == NULL)
- break; /* >>> exit 'while(1)' here <<< */
- m_freem(m);
- }
-
- sc->txslot[lcv].mbsize = 0;
- }
-
- return;
+ uint32_t kick;
+ uint32_t mask;
+ uint32_t val;
+ int chan;
+
+ kick = 0; /* bitmask of channels to kick */
+
+ for (mask = 1, chan = 0; chan < EN_NTX; chan++, mask *= 2) {
+ if (!(reg & MID_TXCHAN(chan)))
+ continue;
+
+ kick = kick | mask;
+
+ /* current read pointer */
+ val = en_read(sc, MIDX_READPTR(chan));
+ /* as offset */
+ val = (val * sizeof(uint32_t)) + sc->txslot[chan].start;
+ if (val > sc->txslot[chan].cur)
+ sc->txslot[chan].bfree = val - sc->txslot[chan].cur;
+ else
+ sc->txslot[chan].bfree = (val + (EN_TXSZ * 1024)) -
+ sc->txslot[chan].cur;
+ DBG(sc, INTR, ("tx%d: transmit done. %d bytes now free in "
+ "buffer", chan, sc->txslot[chan].bfree));
+ }
+ return (kick);
}
-
/*
- * en_init: init board and sync the card with the data in the softc.
+ * TX DMA interrupt
+ *
+ * check for TX DMA complete, if detected then this means
+ * that some DTQs are now free. it also means some indma
+ * mbufs can be freed. if we needed DTQs, kick all channels.
+ *
+ * LOCK: locked, needed
*/
-
-STATIC void en_init(sc)
-
-struct en_softc *sc;
-
+static uint32_t
+en_intr_tx_dma(struct en_softc *sc)
{
- int vc, slot;
- u_int32_t loc;
-
- if ((sc->enif.if_flags & IFF_UP) == 0) {
-#ifdef EN_DEBUG
- printf("%s: going down\n", sc->sc_dev.dv_xname);
-#endif
- en_reset(sc); /* to be safe */
- sc->enif.if_flags &= ~IFF_RUNNING; /* disable */
- return;
- }
-
-#ifdef EN_DEBUG
- printf("%s: going up\n", sc->sc_dev.dv_xname);
-#endif
- sc->enif.if_flags |= IFF_RUNNING; /* enable */
-
- if (sc->en_busreset)
- sc->en_busreset(sc);
- EN_WRITE(sc, MID_RESID, 0x0); /* reset */
-
- /*
- * init obmem data structures: vc tab, dma q's, slist.
- *
- * note that we set drq_free/dtq_free to one less than the total number
- * of DTQ/DRQs present. we do this because the card uses the condition
- * (drq_chip == drq_us) to mean "list is empty"... but if you allow the
- * circular list to be completely full then (drq_chip == drq_us) [i.e.
- * the drq_us pointer will wrap all the way around]. by restricting
- * the number of active requests to (N - 1) we prevent the list from
- * becoming completely full. note that the card will sometimes give
- * us an interrupt for a DTQ/DRQ we have already processes... this helps
- * keep that interrupt from messing us up.
- */
-
- for (vc = 0 ; vc < MID_N_VC ; vc++)
- en_loadvc(sc, vc);
-
- bzero(&sc->drq, sizeof(sc->drq));
- sc->drq_free = MID_DRQ_N - 1; /* N - 1 */
- sc->drq_chip = MID_DRQ_REG2A(EN_READ(sc, MID_DMA_RDRX));
- EN_WRITE(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_chip));
- /* ensure zero queue */
- sc->drq_us = sc->drq_chip;
-
- bzero(&sc->dtq, sizeof(sc->dtq));
- sc->dtq_free = MID_DTQ_N - 1; /* N - 1 */
- sc->dtq_chip = MID_DTQ_REG2A(EN_READ(sc, MID_DMA_RDTX));
- EN_WRITE(sc, MID_DMA_WRTX, MID_DRQ_A2REG(sc->dtq_chip));
- /* ensure zero queue */
- sc->dtq_us = sc->dtq_chip;
-
- sc->hwslistp = MID_SL_REG2A(EN_READ(sc, MID_SERV_WRITE));
- sc->swsl_size = sc->swsl_head = sc->swsl_tail = 0;
-
-#ifdef EN_DEBUG
- printf("%s: drq free/chip: %d/0x%x, dtq free/chip: %d/0x%x, hwslist: 0x%x\n",
- sc->sc_dev.dv_xname, sc->drq_free, sc->drq_chip,
- sc->dtq_free, sc->dtq_chip, sc->hwslistp);
-#endif
-
- for (slot = 0 ; slot < EN_NTX ; slot++) {
- sc->txslot[slot].bfree = EN_TXSZ * 1024;
- EN_WRITE(sc, MIDX_READPTR(slot), 0);
- EN_WRITE(sc, MIDX_DESCSTART(slot), 0);
- loc = sc->txslot[slot].cur = sc->txslot[slot].start;
- loc = loc - MID_RAMOFF;
- loc = (loc & ~((EN_TXSZ*1024) - 1)) >> 2; /* mask, cvt to words */
- loc = loc >> MIDV_LOCTOPSHFT; /* top 11 bits */
- EN_WRITE(sc, MIDX_PLACE(slot), MIDX_MKPLACE(en_k2sz(EN_TXSZ), loc));
-#ifdef EN_DEBUG
- printf("%s: tx%d: place 0x%x\n", sc->sc_dev.dv_xname, slot,
- (u_int)EN_READ(sc, MIDX_PLACE(slot)));
-#endif
- }
-
- /*
- * enable!
- */
+ uint32_t kick = 0;
+ uint32_t val;
+ uint32_t idx;
+ uint32_t slot;
+ uint32_t dtq;
+ struct en_map *map;
+ struct mbuf *m;
+
+ val = en_read(sc, MID_DMA_RDTX); /* chip's current location */
+ idx = MID_DTQ_A2REG(sc->dtq_chip); /* where we last saw chip */
+
+ if (sc->need_dtqs) {
+ kick = MID_NTX_CH - 1; /* assume power of 2, kick all! */
+ sc->need_dtqs = 0; /* recalculated in "kick" loop below */
+ DBG(sc, INTR, ("cleared need DTQ condition"));
+ }
- EN_WRITE(sc, MID_INTENA, MID_INT_TX|MID_INT_DMA_OVR|MID_INT_IDENT|
- MID_INT_LERR|MID_INT_DMA_ERR|MID_INT_DMA_RX|MID_INT_DMA_TX|
- MID_INT_SERVICE| /* >>> MID_INT_SUNI| XXXCDC<<< */ MID_INT_STATS);
- EN_WRITE(sc, MID_MAST_CSR, MID_SETIPL(sc->ipl)|MID_MCSR_ENDMA|
- MID_MCSR_ENTX|MID_MCSR_ENRX);
+ while (idx != val) {
+ sc->dtq_free++;
+ if ((dtq = sc->dtq[idx]) != 0) {
+ /* don't forget to zero it out when done */
+ sc->dtq[idx] = 0;
+ slot = EN_DQ_SLOT(dtq);
+
+ _IF_DEQUEUE(&sc->txslot[slot].indma, m);
+ if (m == NULL)
+ panic("enintr: dtqsync");
+ map = (void *)m->m_pkthdr.rcvif;
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+
+ sc->txslot[slot].mbsize -= EN_DQ_LEN(dtq);
+ DBG(sc, INTR, ("tx%d: free %d dma bytes, mbsize now "
+ "%d", slot, EN_DQ_LEN(dtq),
+ sc->txslot[slot].mbsize));
+ }
+ EN_WRAPADD(0, MID_DTQ_N, idx, 1);
+ }
+ sc->dtq_chip = MID_DTQ_REG2A(val); /* sync softc */
+ return (kick);
}
-
/*
- * en_loadvc: load a vc tab entry from a slot
+ * Service interrupt
+ *
+ * LOCK: locked, needed
*/
-
-STATIC void en_loadvc(sc, vc)
-
-struct en_softc *sc;
-int vc;
-
+static int
+en_intr_service(struct en_softc *sc)
{
- int slot;
- u_int32_t reg = EN_READ(sc, MID_VC(vc));
-
- reg = MIDV_SETMODE(reg, MIDV_TRASH);
- EN_WRITE(sc, MID_VC(vc), reg);
- DELAY(27);
-
- if ((slot = sc->rxvc2slot[vc]) == RX_NONE)
- return;
-
- /* no need to set CRC */
- EN_WRITE(sc, MID_DST_RP(vc), 0); /* read pointer = 0, desc. start = 0 */
- EN_WRITE(sc, MID_WP_ST_CNT(vc), 0); /* write pointer = 0 */
- EN_WRITE(sc, MID_VC(vc), sc->rxslot[slot].mode); /* set mode, size, loc */
- sc->rxslot[slot].cur = sc->rxslot[slot].start;
+ uint32_t chip;
+ uint32_t slot;
+ uint32_t vci;
+ int need_softserv = 0;
+
+ chip = MID_SL_REG2A(en_read(sc, MID_SERV_WRITE));
+
+ while (sc->hwslistp != chip) {
+ /* fetch and remove it from hardware service list */
+ vci = en_read(sc, sc->hwslistp);
+ EN_WRAPADD(MID_SLOFF, MID_SLEND, sc->hwslistp, 4);
+
+ slot = sc->rxvc2slot[vci];
+ if (slot == RX_NONE) {
+ DBG(sc, INTR, ("unexpected rx interrupt on VCI %d",
+ vci));
+ en_write(sc, MID_VC(vci), MIDV_TRASH); /* rx off */
+ continue;
+ }
-#ifdef EN_DEBUG
- printf("%s: rx%d: assigned to VCI %d\n", sc->sc_dev.dv_xname, slot, vc);
-#endif
+ /* remove from hwsl */
+ en_write(sc, MID_VC(vci), sc->rxslot[slot].mode);
+ EN_COUNT(sc->stats.hwpull);
+
+ DBG(sc, INTR, ("pulled VCI %d off hwslist", vci));
+
+ /* add it to the software service list (if needed) */
+ if ((sc->rxslot[slot].oth_flags & ENOTHER_SWSL) == 0) {
+ EN_COUNT(sc->stats.swadd);
+ need_softserv = 1;
+ sc->rxslot[slot].oth_flags |= ENOTHER_SWSL;
+ sc->swslist[sc->swsl_tail] = slot;
+ EN_WRAPADD(0, MID_SL_N, sc->swsl_tail, 1);
+ sc->swsl_size++;
+ DBG(sc, INTR, ("added VCI %d to swslist", vci));
+ }
+ }
+ return (need_softserv);
}
-
/*
- * en_start: start transmitting the next packet that needs to go out
- * if there is one. note that atm_output() has already splimp()'d us.
+ * check for RX DMA complete, and pass the data "upstairs"
+ *
+ * LOCK: locked, needed
*/
-
-STATIC void en_start(ifp)
-
-struct ifnet *ifp;
-
+static int
+en_intr_rx_dma(struct en_softc *sc)
{
- struct en_softc *sc = (struct en_softc *) ifp->if_softc;
- struct ifqueue *ifq = &ifp->if_snd; /* if INPUT QUEUE */
- struct mbuf *m, *lastm, *prev;
- struct atm_pseudohdr *ap, *new_ap;
- int txchan, mlen, got, need, toadd, cellcnt, first;
- u_int32_t atm_vpi, atm_vci, atm_flags, *dat, aal;
- u_int8_t *cp;
-
- if ((ifp->if_flags & IFF_RUNNING) == 0)
- return;
-
- /*
- * remove everything from interface queue since we handle all queueing
- * locally ...
- */
-
- while (1) {
-
- IF_DEQUEUE(ifq, m);
- if (m == NULL)
- return; /* EMPTY: >>> exit here <<< */
-
- /*
- * calculate size of packet (in bytes)
- * also, if we are not doing transmit DMA we eliminate all stupid
- * (non-word) alignments here using en_mfix(). calls to en_mfix()
- * seem to be due to tcp retransmits for the most part.
- *
- * after this loop mlen total length of mbuf chain (including atm_ph),
- * and lastm is a pointer to the last mbuf on the chain.
- */
-
- lastm = m;
- mlen = 0;
- prev = NULL;
- while (1) {
- /* no DMA? */
- if ((!sc->is_adaptec && EN_ENIDMAFIX) || EN_NOTXDMA || !en_dma) {
- if ( ((uintptr_t)mtod(lastm, void *) % sizeof(u_int32_t)) != 0 ||
- ((lastm->m_len % sizeof(u_int32_t)) != 0 && lastm->m_next)) {
- first = (lastm == m);
- if (en_mfix(sc, &lastm, prev) == 0) { /* failed? */
- m_freem(m);
- m = NULL;
- break;
- }
- if (first)
- m = lastm; /* update */
- }
- prev = lastm;
- }
-
- mlen += lastm->m_len;
- if (lastm->m_next == NULL)
- break;
- lastm = lastm->m_next;
- }
-
- if (m == NULL) /* happens only if mfix fails */
- continue;
-
- ap = mtod(m, struct atm_pseudohdr *);
-
- atm_vpi = ATM_PH_VPI(ap);
- atm_vci = ATM_PH_VCI(ap);
- atm_flags = ATM_PH_FLAGS(ap) & ~(EN_OBHDR|EN_OBTRL);
- aal = ((atm_flags & ATM_PH_AAL5) != 0)
- ? MID_TBD_AAL5 : MID_TBD_NOAAL5;
-
- /*
- * check that vpi/vci is one we can use
- */
-
- if (atm_vpi || atm_vci > MID_N_VC) {
- printf("%s: output vpi=%d, vci=%d out of card range, dropping...\n",
- sc->sc_dev.dv_xname, atm_vpi, atm_vci);
- m_freem(m);
- continue;
- }
-
- /*
- * computing how much padding we need on the end of the mbuf, then
- * see if we can put the TBD at the front of the mbuf where the
- * link header goes (well behaved protocols will reserve room for us).
- * last, check if room for PDU tail.
- *
- * got = number of bytes of data we have
- * cellcnt = number of cells in this mbuf
- * need = number of bytes of data + padding we need (excludes TBD)
- * toadd = number of bytes of data we need to add to end of mbuf,
- * [including AAL5 PDU, if AAL5]
- */
-
- got = mlen - sizeof(struct atm_pseudohdr);
- toadd = (aal == MID_TBD_AAL5) ? MID_PDU_SIZE : 0; /* PDU */
- cellcnt = (got + toadd + (MID_ATMDATASZ - 1)) / MID_ATMDATASZ;
- need = cellcnt * MID_ATMDATASZ;
- toadd = need - got; /* recompute, including zero padding */
-
-#ifdef EN_DEBUG
- printf("%s: txvci%d: mlen=%d, got=%d, need=%d, toadd=%d, cell#=%d\n",
- sc->sc_dev.dv_xname, atm_vci, mlen, got, need, toadd, cellcnt);
- printf(" leading_space=%d, trailing_space=%d\n",
- (int)M_LEADINGSPACE(m), (int)M_TRAILINGSPACE(lastm));
-#endif
-
-#ifdef EN_MBUF_OPT
-
- /*
- * note: external storage (M_EXT) can be shared between mbufs
- * to avoid copying (see m_copym()). this means that the same
- * data buffer could be shared by several mbufs, and thus it isn't
- * a good idea to try and write TBDs or PDUs to M_EXT data areas.
- */
-
- if (M_LEADINGSPACE(m) >= MID_TBD_SIZE && (m->m_flags & M_EXT) == 0) {
- m->m_data -= MID_TBD_SIZE;
- m->m_len += MID_TBD_SIZE;
- mlen += MID_TBD_SIZE;
- new_ap = mtod(m, struct atm_pseudohdr *);
- *new_ap = *ap; /* move it back */
- ap = new_ap;
- dat = ((u_int32_t *) ap) + 1;
- /* make sure the TBD is in proper byte order */
- *dat++ = htonl(MID_TBD_MK1(aal, sc->txspeed[atm_vci], cellcnt));
- *dat = htonl(MID_TBD_MK2(atm_vci, 0, 0));
- atm_flags |= EN_OBHDR;
- }
-
- if (toadd && (lastm->m_flags & M_EXT) == 0 &&
- M_TRAILINGSPACE(lastm) >= toadd) {
- cp = mtod(lastm, u_int8_t *) + lastm->m_len;
- lastm->m_len += toadd;
- mlen += toadd;
- if (aal == MID_TBD_AAL5) {
- bzero(cp, toadd - MID_PDU_SIZE);
- dat = (u_int32_t *)(cp + toadd - MID_PDU_SIZE);
- /* make sure the PDU is in proper byte order */
- *dat = htonl(MID_PDU_MK1(0, 0, got));
- } else {
- bzero(cp, toadd);
+ uint32_t val;
+ uint32_t idx;
+ uint32_t drq;
+ uint32_t slot;
+ uint32_t vci;
+ struct atm_pseudohdr ah;
+ struct mbuf *m;
+ struct en_map *map;
+
+ val = en_read(sc, MID_DMA_RDRX); /* chip's current location */
+ idx = MID_DRQ_A2REG(sc->drq_chip); /* where we last saw chip */
+
+ while (idx != val) {
+ sc->drq_free++;
+ if ((drq = sc->drq[idx]) != 0) {
+ /* don't forget to zero it out when done */
+ sc->drq[idx] = 0;
+ slot = EN_DQ_SLOT(drq);
+ if (EN_DQ_LEN(drq) == 0) { /* "JK" trash DMA? */
+ m = NULL;
+ map = NULL;
+ } else {
+ _IF_DEQUEUE(&sc->rxslot[slot].indma, m);
+ if (m == NULL)
+ panic("enintr: drqsync: %s%d: lost mbuf"
+ " in slot %d!", sc->enif.if_name,
+ sc->enif.if_unit, slot);
+ map = (void *)m->m_pkthdr.rcvif;
+ uma_zfree(sc->map_zone, map);
+ }
+ /* do something with this mbuf */
+ if (sc->rxslot[slot].oth_flags & ENOTHER_DRAIN) {
+ /* drain? */
+ if (m != NULL)
+ m_freem(m);
+ vci = sc->rxslot[slot].atm_vci;
+ if (!_IF_QLEN(&sc->rxslot[slot].indma) &&
+ !_IF_QLEN(&sc->rxslot[slot].q) &&
+ (en_read(sc, MID_VC(vci)) & MIDV_INSERVICE)
+ == 0 &&
+ (sc->rxslot[slot].oth_flags & ENOTHER_SWSL)
+ == 0) {
+ sc->rxslot[slot].oth_flags =
+ ENOTHER_FREE; /* done drain */
+ sc->rxslot[slot].atm_vci = RX_NONE;
+ sc->rxvc2slot[vci] = RX_NONE;
+ DBG(sc, INTR, ("rx%d: VCI %d now free",
+ slot, vci));
+ }
+
+ } else if (m != NULL) {
+ ATM_PH_FLAGS(&ah) = sc->rxslot[slot].atm_flags;
+ ATM_PH_VPI(&ah) = 0;
+ ATM_PH_SETVCI(&ah, sc->rxslot[slot].atm_vci);
+ DBG(sc, INTR, ("rx%d: rxvci%d: atm_input, "
+ "mbuf %p, len %d, hand %p", slot,
+ sc->rxslot[slot].atm_vci, m,
+ EN_DQ_LEN(drq), sc->rxslot[slot].rxhand));
+
+ m->m_pkthdr.rcvif = &sc->enif;
+ sc->enif.if_ipackets++;
+#ifdef EN_DEBUG
+ if (sc->debug & DBG_IPACKETS)
+ en_dump_packet(sc, m);
+#endif
+#ifdef ENABLE_BPF
+ BPF_MTAP(&sc->enif, m);
+#endif
+ atm_input(&sc->enif, &ah, m,
+ sc->rxslot[slot].rxhand);
+ }
+ }
+ EN_WRAPADD(0, MID_DRQ_N, idx, 1);
}
- atm_flags |= EN_OBTRL;
- }
- ATM_PH_FLAGS(ap) = atm_flags; /* update EN_OBHDR/EN_OBTRL bits */
-#endif /* EN_MBUF_OPT */
-
- /*
- * get assigned channel (will be zero unless txspeed[atm_vci] is set)
- */
-
- txchan = sc->txvc2slot[atm_vci];
-
- if (sc->txslot[txchan].mbsize > EN_TXHIWAT) {
- EN_COUNT(sc->txmbovr);
- m_freem(m);
-#ifdef EN_DEBUG
- printf("%s: tx%d: buffer space shortage\n", sc->sc_dev.dv_xname,
- txchan);
-#endif
- continue;
- }
-
- sc->txslot[txchan].mbsize += mlen;
+ sc->drq_chip = MID_DRQ_REG2A(val); /* sync softc */
+
+ if (sc->need_drqs) {
+ /* true if we had a DRQ shortage */
+ sc->need_drqs = 0;
+ DBG(sc, INTR, ("cleared need DRQ condition"));
+ return (1);
+ } else
+ return (0);
+}
-#ifdef EN_DEBUG
- printf("%s: tx%d: VPI=%d, VCI=%d, FLAGS=0x%x, speed=0x%x\n",
- sc->sc_dev.dv_xname, txchan, atm_vpi, atm_vci, atm_flags,
- sc->txspeed[atm_vci]);
- printf(" adjusted mlen=%d, mbsize=%d\n", mlen,
- sc->txslot[txchan].mbsize);
-#endif
+/*
+ * en_mget: get an mbuf chain that can hold totlen bytes and return it
+ * (for recv). For the actual allocation totlen is rounded up to a multiple
+ * of 4. We also ensure, that each mbuf has a multiple of 4 bytes.
+ *
+ * After this call the sum of all the m_len's in the chain will be totlen.
+ * This is called at interrupt time, so we can't wait here.
+ *
+ * LOCK: any, not needed
+ */
+static struct mbuf *
+en_mget(struct en_softc *sc, u_int pktlen)
+{
+ struct mbuf *m, *tmp;
+ u_int totlen, pad;
+
+ totlen = roundup(pktlen, sizeof(uint32_t));
+ pad = totlen - pktlen;
+
+ /*
+ * First get an mbuf with header. Keep space for a couple of
+ * words at the begin.
+ */
+ /* called from interrupt context */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (NULL);
+
+ m->m_pkthdr.rcvif = NULL;
+ m->m_pkthdr.len = pktlen;
+ m->m_len = EN_RX1BUF;
+ MH_ALIGN(m, EN_RX1BUF);
+ if (m->m_len >= totlen) {
+ m->m_len = totlen;
- _IF_ENQUEUE(&sc->txslot[txchan].q, m);
+ } else {
+ totlen -= m->m_len;
- en_txdma(sc, txchan);
+ /* called from interrupt context */
+ tmp = m_getm(m, totlen, M_DONTWAIT, MT_DATA);
+ if (tmp == NULL) {
+ m_free(m);
+ return (NULL);
+ }
+ tmp = m->m_next;
+ /* m_getm could do this for us */
+ while (tmp != NULL) {
+ tmp->m_len = min(MCLBYTES, totlen);
+ totlen -= tmp->m_len;
+ tmp = tmp->m_next;
+ }
+ }
- }
- /*NOTREACHED*/
+ return (m);
}
-
/*
- * en_mfix: fix a stupid mbuf
+ * Argument for RX DMAMAP loader.
*/
+struct rxarg {
+ struct en_softc *sc;
+ struct mbuf *m;
+ u_int pre_skip; /* number of bytes to skip at begin */
+ u_int post_skip; /* number of bytes to skip at end */
+ struct en_rxslot *slot; /* slot we are receiving on */
+ int wait; /* wait for DRQ entries */
+};
-#ifndef __FreeBSD__
+/*
+ * Copy the segment table to the buffer for later use. And compute the
+ * number of dma queue entries we need.
+ *
+ * LOCK: locked, needed
+ */
+static void
+en_rxdma_load(void *uarg, bus_dma_segment_t *segs, int nseg,
+ bus_size_t mapsize, int error)
+{
+ struct rxarg *rx = uarg;
+ struct en_softc *sc = rx->sc;
+ struct en_rxslot *slot = rx->slot;
+ u_int free; /* number of free DRQ entries */
+ uint32_t cur; /* current buffer offset */
+ uint32_t drq; /* DRQ entry pointer */
+ uint32_t last_drq; /* where we have written last */
+ u_int needalign, cnt, count, bcode;
+ bus_addr_t addr;
+ bus_size_t rest;
+ int i;
+
+ if (error != 0)
+ return;
+ if (nseg > EN_MAX_DMASEG)
+ panic("too many DMA segments");
+
+ rx->wait = 0;
+
+ free = sc->drq_free;
+ drq = sc->drq_us;
+ cur = slot->cur;
+
+ last_drq = 0;
+
+ /*
+ * Local macro to add an entry to the receive DMA area. If there
+ * are no entries left, return. Save the byte offset of the entry
+ * in last_drq for later use.
+ */
+#define PUT_DRQ_ENTRY(ENI, BCODE, COUNT, ADDR) \
+ if (free == 0) { \
+ EN_COUNT(sc->stats.rxdrqout); \
+ rx->wait = 1; \
+ return; \
+ } \
+ last_drq = drq; \
+ en_write(sc, drq + 0, (ENI || !sc->is_adaptec) ? \
+ MID_MK_RXQ_ENI(COUNT, slot->atm_vci, 0, BCODE) : \
+ MID_MK_RXQ_ADP(COUNT, slot->atm_vci, 0, BCODE)); \
+ en_write(sc, drq + 4, ADDR); \
+ \
+ EN_WRAPADD(MID_DRQOFF, MID_DRQEND, drq, 8); \
+ free--;
+
+ /*
+ * Local macro to generate a DMA entry to DMA cnt bytes. Updates
+ * the current buffer byte offset accordingly.
+ */
+#define DO_DRQ(TYPE) do { \
+ rest -= cnt; \
+ EN_WRAPADD(slot->start, slot->stop, cur, cnt); \
+ DBG(sc, SERV, ("rx%td: "TYPE" %u bytes, %ju left, cur %#x", \
+ slot - sc->rxslot, cnt, (uintmax_t)rest, cur)); \
+ \
+ PUT_DRQ_ENTRY(1, bcode, count, addr); \
+ \
+ addr += cnt; \
+ } while (0)
+
+ /*
+ * Skip the RBD at the beginning
+ */
+ if (rx->pre_skip > 0) {
+ /* update DMA address */
+ EN_WRAPADD(slot->start, slot->stop, cur, rx->pre_skip);
+
+ PUT_DRQ_ENTRY(0, MIDDMA_JK, WORD_IDX(slot->start, cur), 0);
+ }
-STATIC int en_mfix(sc, mm, prev)
+ for (i = 0; i < nseg; i++, segs++) {
+ addr = segs->ds_addr;
+ rest = segs->ds_len;
-struct en_softc *sc;
-struct mbuf **mm, *prev;
+ if (sc->is_adaptec) {
+ /* adaptec card - simple */
-{
- struct mbuf *m, *new;
- u_char *d, *cp;
- int off;
- struct mbuf *nxt;
+ /* advance the on-card buffer pointer */
+ EN_WRAPADD(slot->start, slot->stop, cur, rest);
+ DBG(sc, SERV, ("rx%td: adp %ju bytes %#jx "
+ "(cur now 0x%x)", slot - sc->rxslot,
+ (uintmax_t)rest, (uintmax_t)addr, cur));
- m = *mm;
+ PUT_DRQ_ENTRY(0, 0, rest, addr);
- EN_COUNT(sc->mfix); /* count # of calls */
-#ifdef EN_DEBUG
- printf("%s: mfix mbuf m_data=%p, m_len=%d\n", sc->sc_dev.dv_xname,
- m->m_data, m->m_len);
-#endif
+ continue;
+ }
- d = mtod(m, u_char *);
- off = ((unsigned long) d) % sizeof(u_int32_t);
-
- if (off) {
- if ((m->m_flags & M_EXT) == 0) {
- bcopy(d, d - off, m->m_len); /* ALIGN! (with costly data copy...) */
- d -= off;
- m->m_data = (caddr_t)d;
- } else {
- /* can't write to an M_EXT mbuf since it may be shared */
- MGET(new, M_DONTWAIT, MT_DATA);
- if (!new) {
- EN_COUNT(sc->mfixfail);
- return(0);
- }
- MCLGET(new, M_DONTWAIT);
- if ((new->m_flags & M_EXT) == 0) {
- m_free(new);
- EN_COUNT(sc->mfixfail);
- return(0);
- }
- bcopy(d, new->m_data, m->m_len); /* ALIGN! (with costly data copy...) */
- new->m_len = m->m_len;
- new->m_next = m->m_next;
- if (prev)
- prev->m_next = new;
- m_free(m);
- *mm = m = new; /* note: 'd' now invalid */
- }
- }
-
- off = m->m_len % sizeof(u_int32_t);
- if (off == 0)
- return(1);
-
- d = mtod(m, u_char *) + m->m_len;
- off = sizeof(u_int32_t) - off;
-
- nxt = m->m_next;
- while (off--) {
- for ( ; nxt != NULL && nxt->m_len == 0 ; nxt = nxt->m_next)
- /*null*/;
- if (nxt == NULL) { /* out of data, zero fill */
- *d++ = 0;
- continue; /* next "off" */
- }
- cp = mtod(nxt, u_char *);
- *d++ = *cp++;
- m->m_len++;
- nxt->m_len--;
- nxt->m_data = (caddr_t)cp;
- }
- return(1);
-}
+ /*
+ * do we need to do a DMA op to align to the maximum
+ * burst? Note, that we are alway 32-bit aligned.
+ */
+ if (sc->alburst &&
+ (needalign = (addr & sc->bestburstmask)) != 0) {
+ /* compute number of bytes, words and code */
+ cnt = sc->bestburstlen - needalign;
+ if (cnt > rest)
+ cnt = rest;
+ count = cnt / sizeof(uint32_t);
+ if (sc->noalbursts) {
+ bcode = MIDDMA_WORD;
+ } else {
+ bcode = en_dmaplan[count].bcode;
+ count = cnt >> en_dmaplan[count].divshift;
+ }
+ DO_DRQ("al_dma");
+ }
-#else /* __FreeBSD__ */
+ /* do we need to do a max-sized burst? */
+ if (rest >= sc->bestburstlen) {
+ count = rest >> sc->bestburstshift;
+ cnt = count << sc->bestburstshift;
+ bcode = sc->bestburstcode;
+ DO_DRQ("best_dma");
+ }
-STATIC int en_makeexclusive(struct en_softc *, struct mbuf **, struct mbuf *);
+ /* do we need to do a cleanup burst? */
+ if (rest != 0) {
+ cnt = rest;
+ count = rest / sizeof(uint32_t);
+ if (sc->noalbursts) {
+ bcode = MIDDMA_WORD;
+ } else {
+ bcode = en_dmaplan[count].bcode;
+ count = cnt >> en_dmaplan[count].divshift;
+ }
+ DO_DRQ("clean_dma");
+ }
+ }
-STATIC int en_makeexclusive(sc, mm, prev)
- struct en_softc *sc;
- struct mbuf **mm, *prev;
-{
- struct mbuf *m, *new;
-
- m = *mm;
-
- if (m->m_flags & M_EXT) {
- if (m->m_ext.ext_type != EXT_CLUSTER) {
- /* external buffer isn't an ordinary mbuf cluster! */
- printf("%s: mfix: special buffer! can't make a copy!\n",
- sc->sc_dev.dv_xname);
- return (0);
- }
-
- if (MEXT_IS_REF(m)) {
- /* make a real copy of the M_EXT mbuf since it is shared */
- MGET(new, M_DONTWAIT, MT_DATA);
- if (!new) {
- EN_COUNT(sc->mfixfail);
- return(0);
- }
- if (m->m_flags & M_PKTHDR)
- M_MOVE_PKTHDR(new, m);
- MCLGET(new, M_DONTWAIT);
- if ((new->m_flags & M_EXT) == 0) {
- m_free(new);
- EN_COUNT(sc->mfixfail);
- return(0);
- }
- bcopy(m->m_data, new->m_data, m->m_len);
- new->m_len = m->m_len;
- new->m_next = m->m_next;
- if (prev)
- prev->m_next = new;
- m_free(m);
- *mm = new;
- }
- else {
- /* the buffer is not shared, align the data offset using
- this buffer. */
- u_char *d = mtod(m, u_char *);
- int off = ((uintptr_t)(void *)d) % sizeof(u_int32_t);
-
- if (off > 0) {
- bcopy(d, d - off, m->m_len);
- m->m_data = (caddr_t)d - off;
- }
- }
- }
- return (1);
-}
+ /*
+ * Skip stuff at the end
+ */
+ if (rx->post_skip > 0) {
+ /* update DMA address */
+ EN_WRAPADD(slot->start, slot->stop, cur, rx->post_skip);
-STATIC int en_mfix(sc, mm, prev)
+ PUT_DRQ_ENTRY(0, MIDDMA_JK, WORD_IDX(slot->start, cur), 0);
+ }
-struct en_softc *sc;
-struct mbuf **mm, *prev;
+ /* record the end for the interrupt routine */
+ sc->drq[MID_DRQ_A2REG(last_drq)] =
+ EN_DQ_MK(slot - sc->rxslot, rx->m->m_pkthdr.len);
-{
- struct mbuf *m;
- u_char *d, *cp;
- int off;
- struct mbuf *nxt;
+ /* set the end flag in the last descriptor */
+ en_write(sc, last_drq + 0, SETQ_END(sc, en_read(sc, last_drq + 0)));
- m = *mm;
+#undef PUT_DRQ_ENTRY
+#undef DO_DRQ
- EN_COUNT(sc->mfix); /* count # of calls */
-#ifdef EN_DEBUG
- printf("%s: mfix mbuf m_data=%p, m_len=%d\n", sc->sc_dev.dv_xname,
- m->m_data, m->m_len);
-#endif
+ /* commit */
+ slot->cur = cur;
+ sc->drq_free = free;
+ sc->drq_us = drq;
- d = mtod(m, u_char *);
- off = ((uintptr_t) (void *) d) % sizeof(u_int32_t);
-
- if (off) {
- if ((m->m_flags & M_EXT) == 0) {
- bcopy(d, d - off, m->m_len); /* ALIGN! (with costly data copy...) */
- d -= off;
- m->m_data = (caddr_t)d;
- } else {
- /* can't write to an M_EXT mbuf since it may be shared */
- if (en_makeexclusive(sc, &m, prev) == 0)
- return (0);
- *mm = m; /* note: 'd' now invalid */
- }
- }
-
- off = m->m_len % sizeof(u_int32_t);
- if (off == 0)
- return(1);
-
- if (m->m_flags & M_EXT) {
- /* can't write to an M_EXT mbuf since it may be shared */
- if (en_makeexclusive(sc, &m, prev) == 0)
- return (0);
- *mm = m; /* note: 'd' now invalid */
- }
-
- d = mtod(m, u_char *) + m->m_len;
- off = sizeof(u_int32_t) - off;
-
- nxt = m->m_next;
- while (off--) {
- if (nxt != NULL && nxt->m_len == 0) {
- /* remove an empty mbuf. this avoids odd byte padding to an empty
- last mbuf. */
- m->m_next = nxt = m_free(nxt);
- }
- if (nxt == NULL) { /* out of data, zero fill */
- *d++ = 0;
- continue; /* next "off" */
- }
- cp = mtod(nxt, u_char *);
- *d++ = *cp++;
- m->m_len++;
- nxt->m_len--;
- nxt->m_data = (caddr_t)cp;
- }
- if (nxt != NULL && nxt->m_len == 0)
- m->m_next = m_free(nxt);
- return(1);
+ /* signal to card */
+ en_write(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_us));
}
-#endif /* __FreeBSD__ */
-
/*
- * en_txdma: start trasmit DMA, if possible
+ * en_service: handle a service interrupt
+ *
+ * Q: why do we need a software service list?
+ *
+ * A: if we remove a VCI from the hardware list and we find that we are
+ * out of DRQs we must defer processing until some DRQs become free.
+ * so we must remember to look at this RX VCI/slot later, but we can't
+ * put it back on the hardware service list (since that isn't allowed).
+ * so we instead save it on the software service list. it would be nice
+ * if we could peek at the VCI on top of the hwservice list without removing
+ * it, however this leads to a race condition: if we peek at it and
+ * decide we are done with it new data could come in before we have a
+ * chance to remove it from the hwslist. by the time we get it out of
+ * the list the interrupt for the new data will be lost. oops!
+ *
+ * LOCK: locked, needed
*/
-
-STATIC void en_txdma(sc, chan)
-
-struct en_softc *sc;
-int chan;
-
+static void
+en_service(struct en_softc *sc)
{
- struct mbuf *tmp;
- struct atm_pseudohdr *ap;
- struct en_launch launch;
- int datalen = 0, dtqneed, len, ncells;
- u_int8_t *cp;
- struct ifnet *ifp;
-
-#ifdef EN_DEBUG
- printf("%s: tx%d: starting...\n", sc->sc_dev.dv_xname, chan);
-#endif
-
- /*
- * note: now that txlaunch handles non-word aligned/sized requests
- * the only time you can safely set launch.nodma is if you've en_mfix()'d
- * the mbuf chain. this happens only if EN_NOTXDMA || !en_dma.
- */
-
- launch.nodma = (EN_NOTXDMA || !en_dma);
+ struct mbuf *m, *lastm;
+ struct en_map *map;
+ struct rxarg rx;
+ uint32_t cur;
+ uint32_t dstart; /* data start (as reported by card) */
+ uint32_t rbd; /* receive buffer descriptor */
+ uint32_t pdu; /* AAL5 trailer */
+ int mlen;
+ struct en_rxslot *slot;
+ int error;
+
+ rx.sc = sc;
+
+ next_vci:
+ if (sc->swsl_size == 0) {
+ DBG(sc, SERV, ("en_service done"));
+ return;
+ }
-again:
+ /*
+ * get slot to service
+ */
+ rx.slot = slot = &sc->rxslot[sc->swslist[sc->swsl_head]];
+
+ KASSERT (sc->rxvc2slot[slot->atm_vci] == slot - sc->rxslot,
+ ("en_service: rx slot/vci sync"));
+
+ /*
+ * determine our mode and if we've got any work to do
+ */
+ DBG(sc, SERV, ("rx%td: service vci=%d start/stop/cur=0x%x 0x%x "
+ "0x%x", slot - sc->rxslot, slot->atm_vci,
+ slot->start, slot->stop, slot->cur));
+
+ same_vci:
+ cur = slot->cur;
+
+ dstart = MIDV_DSTART(en_read(sc, MID_DST_RP(slot->atm_vci)));
+ dstart = (dstart * sizeof(uint32_t)) + slot->start;
+
+ /* check to see if there is any data at all */
+ if (dstart == cur) {
+ EN_WRAPADD(0, MID_SL_N, sc->swsl_head, 1);
+ /* remove from swslist */
+ slot->oth_flags &= ~ENOTHER_SWSL;
+ sc->swsl_size--;
+ DBG(sc, SERV, ("rx%td: remove vci %d from swslist",
+ slot - sc->rxslot, slot->atm_vci));
+ goto next_vci;
+ }
- /*
- * get an mbuf waiting for DMA
- */
+ /*
+ * figure out how many bytes we need
+ * [mlen = # bytes to go in mbufs]
+ */
+ rbd = en_read(sc, cur);
+ if (MID_RBD_ID(rbd) != MID_RBD_STDID)
+ panic("en_service: id mismatch");
+
+ if (rbd & MID_RBD_T) {
+ mlen = 0; /* we've got trash */
+ rx.pre_skip = MID_RBD_SIZE;
+ rx.post_skip = 0;
+ EN_COUNT(sc->stats.ttrash);
+ DBG(sc, SERV, ("RX overflow lost %d cells!", MID_RBD_CNT(rbd)));
+
+ } else if (!(slot->atm_flags & ATM_PH_AAL5)) {
+ /* 1 cell (ick!) */
+ mlen = MID_CHDR_SIZE + MID_ATMDATASZ;
+ rx.pre_skip = MID_RBD_SIZE;
+ rx.post_skip = 0;
- launch.t = sc->txslot[chan].q.ifq_head; /* peek at head of queue */
+ } else {
+ rx.pre_skip = MID_RBD_SIZE;
+
+ /* get PDU trailer in correct byte order */
+ pdu = cur + MID_RBD_CNT(rbd) * MID_ATMDATASZ +
+ MID_RBD_SIZE - MID_PDU_SIZE;
+ if (pdu >= slot->stop)
+ pdu -= EN_RXSZ * 1024;
+ pdu = en_read(sc, pdu);
+
+ if (MID_RBD_CNT(rbd) * MID_ATMDATASZ <
+ MID_PDU_LEN(pdu)) {
+ if_printf(&sc->enif, "invalid AAL5 length\n");
+ rx.post_skip = MID_RBD_CNT(rbd) * MID_ATMDATASZ;
+ mlen = 0;
+ sc->enif.if_ierrors++;
+
+ } else if (rbd & MID_RBD_CRCERR) {
+ if_printf(&sc->enif, "CRC error\n");
+ rx.post_skip = MID_RBD_CNT(rbd) * MID_ATMDATASZ;
+ mlen = 0;
+ sc->enif.if_ierrors++;
- if (launch.t == NULL) {
-#ifdef EN_DEBUG
- printf("%s: tx%d: ...done!\n", sc->sc_dev.dv_xname, chan);
-#endif
- return; /* >>> exit here if no data waiting for DMA <<< */
- }
-
- /*
- * get flags, vci
- *
- * note: launch.need = # bytes we need to get on the card
- * dtqneed = # of DTQs we need for this packet
- * launch.mlen = # of bytes in in mbuf chain (<= launch.need)
- */
-
- ap = mtod(launch.t, struct atm_pseudohdr *);
- launch.atm_vci = ATM_PH_VCI(ap);
- launch.atm_flags = ATM_PH_FLAGS(ap);
- launch.aal = ((launch.atm_flags & ATM_PH_AAL5) != 0) ?
- MID_TBD_AAL5 : MID_TBD_NOAAL5;
-
- /*
- * XXX: have to recompute the length again, even though we already did
- * it in en_start(). might as well compute dtqneed here as well, so
- * this isn't that bad.
- */
-
- if ((launch.atm_flags & EN_OBHDR) == 0) {
- dtqneed = 1; /* header still needs to be added */
- launch.need = MID_TBD_SIZE; /* not includeded with mbuf */
- } else {
- dtqneed = 0; /* header on-board, dma with mbuf */
- launch.need = 0;
- }
-
- launch.mlen = 0;
- for (tmp = launch.t ; tmp != NULL ; tmp = tmp->m_next) {
- len = tmp->m_len;
- launch.mlen += len;
- cp = mtod(tmp, u_int8_t *);
- if (tmp == launch.t) {
- len -= sizeof(struct atm_pseudohdr); /* don't count this! */
- cp += sizeof(struct atm_pseudohdr);
- }
- launch.need += len;
- if (len == 0)
- continue; /* atm_pseudohdr alone in first mbuf */
-
- dtqneed += en_dqneed(sc, (caddr_t) cp, len, 1);
- }
-
- if ((launch.need % sizeof(u_int32_t)) != 0)
- dtqneed++; /* need DTQ to FLUSH internal buffer */
-
- if ((launch.atm_flags & EN_OBTRL) == 0) {
- if (launch.aal == MID_TBD_AAL5) {
- datalen = launch.need - MID_TBD_SIZE;
- launch.need += MID_PDU_SIZE; /* AAL5: need PDU tail */
- }
- dtqneed++; /* need to work on the end a bit */
- }
-
- /*
- * finish calculation of launch.need (need to figure out how much padding
- * we will need). launch.need includes MID_TBD_SIZE, but we need to
- * remove that to so we can round off properly. we have to add
- * MID_TBD_SIZE back in after calculating ncells.
- */
-
- launch.need = roundup(launch.need - MID_TBD_SIZE, MID_ATMDATASZ);
- ncells = launch.need / MID_ATMDATASZ;
- launch.need += MID_TBD_SIZE;
-
- if (launch.need > EN_TXSZ * 1024) {
- printf("%s: tx%d: packet larger than xmit buffer (%d > %d)\n",
- sc->sc_dev.dv_xname, chan, launch.need, EN_TXSZ * 1024);
- goto dequeue_drop;
- }
-
- /*
- * note: don't use the entire buffer space. if WRTX becomes equal
- * to RDTX, the transmitter stops assuming the buffer is empty! --kjc
- */
- if (launch.need >= sc->txslot[chan].bfree) {
- EN_COUNT(sc->txoutspace);
-#ifdef EN_DEBUG
- printf("%s: tx%d: out of transmit space\n", sc->sc_dev.dv_xname, chan);
-#endif
- return; /* >>> exit here if out of obmem buffer space <<< */
- }
-
- /*
- * ensure we have enough dtqs to go, if not, wait for more.
- */
-
- if (launch.nodma) {
- dtqneed = 1;
- }
- if (dtqneed > sc->dtq_free) {
- sc->need_dtqs = 1;
- EN_COUNT(sc->txdtqout);
-#ifdef EN_DEBUG
- printf("%s: tx%d: out of transmit DTQs\n", sc->sc_dev.dv_xname, chan);
-#endif
- return; /* >>> exit here if out of dtqs <<< */
- }
+ } else {
+ mlen = MID_PDU_LEN(pdu);
+ rx.post_skip = MID_RBD_CNT(rbd) * MID_ATMDATASZ - mlen;
+ }
+ }
- /*
- * it is a go, commit! dequeue mbuf start working on the xfer.
- */
+ /*
+ * now allocate mbufs for mlen bytes of data, if out of mbufs, trash all
+ *
+ * notes:
+ * 1. it is possible that we've already allocated an mbuf for this pkt
+ * but ran out of DRQs, in which case we saved the allocated mbuf
+ * on "q".
+ * 2. if we save an buf in "q" we store the "cur" (pointer) in the
+ * buf as an identity (that we can check later).
+ * 3. after this block of code, if m is still NULL then we ran out of
+ * mbufs
+ */
+ _IF_DEQUEUE(&slot->q, m);
+ if (m != NULL) {
+ if (m->m_pkthdr.csum_data != cur) {
+ /* wasn't ours */
+ DBG(sc, SERV, ("rx%td: q'ed buf %p not ours",
+ slot - sc->rxslot, m));
+ _IF_PREPEND(&slot->q, m);
+ m = NULL;
+ EN_COUNT(sc->stats.rxqnotus);
+ } else {
+ EN_COUNT(sc->stats.rxqus);
+ DBG(sc, SERV, ("rx%td: recovered q'ed buf %p",
+ slot - sc->rxslot, m));
+ }
+ }
+ if (mlen == 0 && m != NULL) {
+ /* should not happen */
+ m_freem(m);
+ m = NULL;
+ }
- _IF_DEQUEUE(&sc->txslot[chan].q, tmp);
-#ifdef EN_DIAG
- if (launch.t != tmp)
- panic("en dequeue");
-#endif /* EN_DIAG */
+ if (mlen != 0 && m == NULL) {
+ m = en_mget(sc, mlen);
+ if (m == NULL) {
+ rx.post_skip += mlen;
+ mlen = 0;
+ EN_COUNT(sc->stats.rxmbufout);
+ DBG(sc, SERV, ("rx%td: out of mbufs",
+ slot - sc->rxslot));
+ } else
+ rx.post_skip -= roundup(mlen, sizeof(uint32_t)) - mlen;
+
+ DBG(sc, SERV, ("rx%td: allocate buf %p, mlen=%d",
+ slot - sc->rxslot, m, mlen));
+ }
- /*
- * launch!
- */
+ DBG(sc, SERV, ("rx%td: VCI %d, rbuf %p, mlen %d, skip %u/%u",
+ slot - sc->rxslot, slot->atm_vci, m, mlen, rx.pre_skip,
+ rx.post_skip));
+
+ if (m != NULL) {
+ /* M_NOWAIT - called from interrupt context */
+ map = uma_zalloc_arg(sc->map_zone, sc, M_NOWAIT);
+ if (map == NULL || !(map->flags & ENMAP_ALLOC)) {
+ rx.post_skip += mlen;
+ m_freem(m);
+ DBG(sc, SERV, ("rx%td: out of maps",
+ slot - sc->rxslot));
+ if (map->map != NULL)
+ uma_zfree(sc->map_zone, map);
+ goto skip;
+ }
+ rx.m = m;
+ error = bus_dmamap_load_mbuf(sc->txtag, map->map, m,
+ en_rxdma_load, &rx, 0);
+
+ if (error != 0) {
+ if_printf(&sc->enif, "loading RX map failed "
+ "%d\n", error);
+ uma_zfree(sc->map_zone, map);
+ m_freem(m);
+ rx.post_skip += mlen;
+ goto skip;
- EN_COUNT(sc->launch);
- ifp = &sc->enif;
- ifp->if_opackets++;
-
- if ((launch.atm_flags & EN_OBHDR) == 0) {
- EN_COUNT(sc->lheader);
- /* store tbd1/tbd2 in host byte order */
- launch.tbd1 = MID_TBD_MK1(launch.aal, sc->txspeed[launch.atm_vci], ncells);
- launch.tbd2 = MID_TBD_MK2(launch.atm_vci, 0, 0);
- }
- if ((launch.atm_flags & EN_OBTRL) == 0 && launch.aal == MID_TBD_AAL5) {
- EN_COUNT(sc->ltail);
- launch.pdu1 = MID_PDU_MK1(0, 0, datalen); /* host byte order */
- }
-
- en_txlaunch(sc, chan, &launch);
-
-#if NBPF > 0
- if (ifp->if_bpf) {
- /*
- * adjust the top of the mbuf to skip the pseudo atm header
- * (and TBD, if present) before passing the packet to bpf,
- * restore it afterwards.
- */
- int size = sizeof(struct atm_pseudohdr);
- if (launch.atm_flags & EN_OBHDR)
- size += MID_TBD_SIZE;
-
- launch.t->m_data += size;
- launch.t->m_len -= size;
-
- BPF_MTAP(ifp, launch.t);
-
- launch.t->m_data -= size;
- launch.t->m_len += size;
- }
-#endif /* NBPF > 0 */
- /*
- * do some housekeeping and get the next packet
- */
-
- sc->txslot[chan].bfree -= launch.need;
- _IF_ENQUEUE(&sc->txslot[chan].indma, launch.t);
- goto again;
-
- /*
- * END of txdma loop!
- */
-
- /*
- * error handles
- */
-
-dequeue_drop:
- _IF_DEQUEUE(&sc->txslot[chan].q, tmp);
- if (launch.t != tmp)
- panic("en dequeue drop");
- m_freem(launch.t);
- sc->txslot[chan].mbsize -= launch.mlen;
- goto again;
-}
+ }
+ map->flags |= ENMAP_LOADED;
+ if (rx.wait) {
+ /* out of DRQs - wait */
+ uma_zfree(sc->map_zone, map);
-/*
- * en_txlaunch: launch an mbuf into the dma pool!
- */
+ m->m_pkthdr.csum_data = cur;
+ _IF_ENQUEUE(&slot->q, m);
+ EN_COUNT(sc->stats.rxdrqout);
-STATIC void en_txlaunch(sc, chan, l)
+ sc->need_drqs = 1; /* flag condition */
+ return;
-struct en_softc *sc;
-int chan;
-struct en_launch *l;
+ }
+ (void)m_length(m, &lastm);
+ lastm->m_len -= roundup(mlen, sizeof(uint32_t)) - mlen;
-{
- struct mbuf *tmp;
- u_int32_t cur = sc->txslot[chan].cur,
- start = sc->txslot[chan].start,
- stop = sc->txslot[chan].stop,
- dma, *data, *datastop, count, bcode;
- int pad, addtail, need, len, needalign, cnt, end, mx;
-
-
- /*
- * vars:
- * need = # bytes card still needs (decr. to zero)
- * len = # of bytes left in current mbuf
- * cur = our current pointer
- * dma = last place we programmed into the DMA
- * data = pointer into data area of mbuf that needs to go next
- * cnt = # of bytes to transfer in this DTQ
- * bcode/count = DMA burst code, and chip's version of cnt
- *
- * a single buffer can require up to 5 DTQs depending on its size
- * and alignment requirements. the 5 possible requests are:
- * [1] 1, 2, or 3 byte DMA to align src data pointer to word boundary
- * [2] alburst DMA to align src data pointer to bestburstlen
- * [3] 1 or more bestburstlen DMAs
- * [4] clean up burst (to last word boundary)
- * [5] 1, 2, or 3 byte final clean up DMA
- */
-
- need = l->need;
- dma = cur;
- addtail = (l->atm_flags & EN_OBTRL) == 0; /* add a tail? */
+ m->m_pkthdr.rcvif = (void *)map;
+ _IF_ENQUEUE(&slot->indma, m);
-#ifdef EN_DIAG
- if ((need - MID_TBD_SIZE) % MID_ATMDATASZ)
- printf("%s: tx%d: bogus trasmit needs (%d)\n", sc->sc_dev.dv_xname, chan,
- need);
-#endif
-#ifdef EN_DEBUG
- printf("%s: tx%d: launch mbuf %p! cur=0x%x[%d], need=%d, addtail=%d\n",
- sc->sc_dev.dv_xname, chan, l->t, cur, (cur-start)/4, need, addtail);
- count = EN_READ(sc, MIDX_PLACE(chan));
- printf(" HW: base_address=0x%x, size=%d, read=%d, descstart=%d\n",
- (u_int)MIDX_BASE(count), MIDX_SZ(count),
- (int)EN_READ(sc, MIDX_READPTR(chan)),
- (int)EN_READ(sc, MIDX_DESCSTART(chan)));
-#endif
+ /* get next packet in this slot */
+ goto same_vci;
+ }
+ skip:
+ /*
+ * Here we end if we should drop the packet from the receive buffer.
+ * The number of bytes to drop is in fill. We can do this with on
+ * JK entry. If we don't even have that one - wait.
+ */
+ if (sc->drq_free == 0) {
+ sc->need_drqs = 1; /* flag condition */
+ return;
+ }
+ rx.post_skip += rx.pre_skip;
+ DBG(sc, SERV, ("rx%td: skipping %u", slot - sc->rxslot, rx.post_skip));
- /*
- * do we need to insert the TBD by hand?
- * note that tbd1/tbd2/pdu1 are in host byte order.
- */
+ /* advance buffer address */
+ EN_WRAPADD(slot->start, slot->stop, cur, rx.post_skip);
- if ((l->atm_flags & EN_OBHDR) == 0) {
-#ifdef EN_DEBUG
- printf("%s: tx%d: insert header 0x%x 0x%x\n", sc->sc_dev.dv_xname,
- chan, l->tbd1, l->tbd2);
-#endif
- EN_WRITE(sc, cur, l->tbd1);
- EN_WRAPADD(start, stop, cur, 4);
- EN_WRITE(sc, cur, l->tbd2);
- EN_WRAPADD(start, stop, cur, 4);
- need -= 8;
- }
-
- /*
- * now do the mbufs...
- */
-
- for (tmp = l->t ; tmp != NULL ; tmp = tmp->m_next) {
-
- /* get pointer to data and length */
- data = mtod(tmp, u_int32_t *);
- len = tmp->m_len;
- if (tmp == l->t) {
- data += sizeof(struct atm_pseudohdr)/sizeof(u_int32_t);
- len -= sizeof(struct atm_pseudohdr);
- }
-
- /* now, determine if we should copy it */
- if (l->nodma || (len < EN_MINDMA &&
- (len % 4) == 0 && ((uintptr_t) (void *) data % 4) == 0 &&
- (cur % 4) == 0)) {
-
- /*
- * roundup len: the only time this will change the value of len
- * is when l->nodma is true, tmp is the last mbuf, and there is
- * a non-word number of bytes to transmit. in this case it is
- * safe to round up because we've en_mfix'd the mbuf (so the first
- * byte is word aligned there must be enough free bytes at the end
- * to round off to the next word boundary)...
- */
- len = roundup(len, sizeof(u_int32_t));
- datastop = data + (len / sizeof(u_int32_t));
- /* copy loop: preserve byte order!!! use WRITEDAT */
- while (data != datastop) {
- EN_WRITEDAT(sc, cur, *data);
- data++;
- EN_WRAPADD(start, stop, cur, 4);
- }
- need -= len;
-#ifdef EN_DEBUG
- printf("%s: tx%d: copied %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, len, need, cur);
-#endif
- continue; /* continue on to next mbuf */
- }
+ /* write DRQ entry */
+ if (sc->is_adaptec)
+ en_write(sc, sc->drq_us,
+ MID_MK_RXQ_ADP(WORD_IDX(slot->start, cur),
+ slot->atm_vci, MID_DMA_END, MIDDMA_JK));
+ else
+ en_write(sc, sc->drq_us,
+ MID_MK_RXQ_ENI(WORD_IDX(slot->start, cur),
+ slot->atm_vci, MID_DMA_END, MIDDMA_JK));
+ en_write(sc, sc->drq_us + 4, 0);
+ EN_WRAPADD(MID_DRQOFF, MID_DRQEND, sc->drq_us, 8);
+ sc->drq_free--;
- /* going to do DMA, first make sure the dtq is in sync. */
- if (dma != cur) {
- EN_DTQADD(sc, WORD_IDX(start,cur), chan, MIDDMA_JK, 0, 0, 0);
-#ifdef EN_DEBUG
- printf("%s: tx%d: dtq_sync: advance pointer to %d\n",
- sc->sc_dev.dv_xname, chan, cur);
-#endif
- }
-
- /*
- * if this is the last buffer, and it looks like we are going to need to
- * flush the internal buffer, can we extend the length of this mbuf to
- * avoid the FLUSH?
- */
-
- if (tmp->m_next == NULL) {
- cnt = (need - len) % sizeof(u_int32_t);
- if (cnt && M_TRAILINGSPACE(tmp) >= cnt)
- len += cnt; /* pad for FLUSH */
- }
-
-#if !defined(MIDWAY_ENIONLY)
-
- /*
- * the adaptec DMA engine is smart and handles everything for us.
- */
-
- if (sc->is_adaptec) {
- /* need to DMA "len" bytes out to card */
- need -= len;
- EN_WRAPADD(start, stop, cur, len);
-#ifdef EN_DEBUG
- printf("%s: tx%d: adp_dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, len, need, cur);
-#endif
- end = (need == 0) ? MID_DMA_END : 0;
- EN_DTQADD(sc, len, chan, 0, vtophys(data), l->mlen, end);
- if (end)
- goto done;
- dma = cur; /* update dma pointer */
- continue;
- }
-#endif /* !MIDWAY_ENIONLY */
-
-#if !defined(MIDWAY_ADPONLY)
-
- /*
- * the ENI DMA engine is not so smart and need more help from us
- */
-
- /* do we need to do a DMA op to align to word boundary? */
- needalign = (uintptr_t) (void *) data % sizeof(u_int32_t);
- if (needalign) {
- EN_COUNT(sc->headbyte);
- cnt = sizeof(u_int32_t) - needalign;
- if (cnt == 2 && len >= cnt) {
- count = 1;
- bcode = MIDDMA_2BYTE;
- } else {
- cnt = min(cnt, len); /* prevent overflow */
- count = cnt;
- bcode = MIDDMA_BYTE;
- }
- need -= cnt;
- EN_WRAPADD(start, stop, cur, cnt);
-#ifdef EN_DEBUG
- printf("%s: tx%d: small al_dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, cnt, need, cur);
-#endif
- len -= cnt;
- end = (need == 0) ? MID_DMA_END : 0;
- EN_DTQADD(sc, count, chan, bcode, vtophys(data), l->mlen, end);
- if (end)
- goto done;
- data = (u_int32_t *) ((u_char *)data + cnt);
- }
-
- /* do we need to do a DMA op to align? */
- if (sc->alburst &&
- (needalign = (((uintptr_t) (void *) data) & sc->bestburstmask)) != 0
- && len >= sizeof(u_int32_t)) {
- cnt = sc->bestburstlen - needalign;
- mx = len & ~(sizeof(u_int32_t)-1); /* don't go past end */
- if (cnt > mx) {
- cnt = mx;
- count = cnt / sizeof(u_int32_t);
- bcode = MIDDMA_WORD;
- } else {
- count = cnt / sizeof(u_int32_t);
- bcode = en_dmaplan[count].bcode;
- count = cnt >> en_dmaplan[count].divshift;
- }
- need -= cnt;
- EN_WRAPADD(start, stop, cur, cnt);
-#ifdef EN_DEBUG
- printf("%s: tx%d: al_dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, cnt, need, cur);
-#endif
- len -= cnt;
- end = (need == 0) ? MID_DMA_END : 0;
- EN_DTQADD(sc, count, chan, bcode, vtophys(data), l->mlen, end);
- if (end)
- goto done;
- data = (u_int32_t *) ((u_char *)data + cnt);
- }
-
- /* do we need to do a max-sized burst? */
- if (len >= sc->bestburstlen) {
- count = len >> sc->bestburstshift;
- cnt = count << sc->bestburstshift;
- bcode = sc->bestburstcode;
- need -= cnt;
- EN_WRAPADD(start, stop, cur, cnt);
-#ifdef EN_DEBUG
- printf("%s: tx%d: best_dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, cnt, need, cur);
-#endif
- len -= cnt;
- end = (need == 0) ? MID_DMA_END : 0;
- EN_DTQADD(sc, count, chan, bcode, vtophys(data), l->mlen, end);
- if (end)
- goto done;
- data = (u_int32_t *) ((u_char *)data + cnt);
- }
-
- /* do we need to do a cleanup burst? */
- cnt = len & ~(sizeof(u_int32_t)-1);
- if (cnt) {
- count = cnt / sizeof(u_int32_t);
- bcode = en_dmaplan[count].bcode;
- count = cnt >> en_dmaplan[count].divshift;
- need -= cnt;
- EN_WRAPADD(start, stop, cur, cnt);
-#ifdef EN_DEBUG
- printf("%s: tx%d: cleanup_dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, cnt, need, cur);
-#endif
- len -= cnt;
- end = (need == 0) ? MID_DMA_END : 0;
- EN_DTQADD(sc, count, chan, bcode, vtophys(data), l->mlen, end);
- if (end)
- goto done;
- data = (u_int32_t *) ((u_char *)data + cnt);
- }
-
- /* any word fragments left? */
- if (len) {
- EN_COUNT(sc->tailbyte);
- if (len == 2) {
- count = 1;
- bcode = MIDDMA_2BYTE; /* use 2byte mode */
- } else {
- count = len;
- bcode = MIDDMA_BYTE; /* use 1 byte mode */
- }
- need -= len;
- EN_WRAPADD(start, stop, cur, len);
-#ifdef EN_DEBUG
- printf("%s: tx%d: byte cleanup_dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, len, need, cur);
-#endif
- end = (need == 0) ? MID_DMA_END : 0;
- EN_DTQADD(sc, count, chan, bcode, vtophys(data), l->mlen, end);
- if (end)
- goto done;
- }
-
- dma = cur; /* update dma pointer */
-#endif /* !MIDWAY_ADPONLY */
-
- } /* next mbuf, please */
-
- /*
- * all mbuf data has been copied out to the obmem (or set up to be DMAd).
- * if the trailer or padding needs to be put in, do it now.
- *
- * NOTE: experimental results reveal the following fact:
- * if you DMA "X" bytes to the card, where X is not a multiple of 4,
- * then the card will internally buffer the last (X % 4) bytes (in
- * hopes of getting (4 - (X % 4)) more bytes to make a complete word).
- * it is imporant to make sure we don't leave any important data in
- * this internal buffer because it is discarded on the last (end) DTQ.
- * one way to do this is to DMA in (4 - (X % 4)) more bytes to flush
- * the darn thing out.
- */
-
- if (addtail) {
-
- pad = need % sizeof(u_int32_t);
- if (pad) {
- /*
- * FLUSH internal data buffer. pad out with random data from the front
- * of the mbuf chain...
- */
- bcode = (sc->is_adaptec) ? 0 : MIDDMA_BYTE;
- EN_COUNT(sc->tailflush);
- EN_WRAPADD(start, stop, cur, pad);
- EN_DTQADD(sc, pad, chan, bcode, vtophys(l->t->m_data), 0, 0);
- need -= pad;
-#ifdef EN_DEBUG
- printf("%s: tx%d: pad/FLUSH dma %d bytes (%d left, cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, pad, need, cur);
-#endif
- }
+ /* signal to RX interrupt */
+ sc->drq[MID_DRQ_A2REG(sc->drq_us)] = EN_DQ_MK(slot - sc->rxslot, 0);
+ slot->cur = cur;
- /* copy data */
- pad = need / sizeof(u_int32_t); /* round *down* */
- if (l->aal == MID_TBD_AAL5)
- pad -= 2;
-#ifdef EN_DEBUG
- printf("%s: tx%d: padding %d bytes (cur now 0x%x)\n",
- sc->sc_dev.dv_xname, chan, (int)(pad * sizeof(u_int32_t)), cur);
-#endif
- while (pad--) {
- EN_WRITEDAT(sc, cur, 0); /* no byte order issues with zero */
- EN_WRAPADD(start, stop, cur, 4);
- }
- if (l->aal == MID_TBD_AAL5) {
- EN_WRITE(sc, cur, l->pdu1); /* in host byte order */
- EN_WRAPADD(start, stop, cur, 8);
- }
- }
-
- if (addtail || dma != cur) {
- /* write final descritor */
- EN_DTQADD(sc, WORD_IDX(start,cur), chan, MIDDMA_JK, 0,
- l->mlen, MID_DMA_END);
- /* dma = cur; */ /* not necessary since we are done */
- }
-
-done:
- /* update current pointer */
- sc->txslot[chan].cur = cur;
-#ifdef EN_DEBUG
- printf("%s: tx%d: DONE! cur now = 0x%x\n",
- sc->sc_dev.dv_xname, chan, cur);
-#endif
+ /* signal to card */
+ en_write(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_us));
- return;
+ goto same_vci;
}
-
/*
* interrupt handler
+ *
+ * LOCK: unlocked, needed
*/
-
-EN_INTR_TYPE en_intr(arg)
-
-void *arg;
-
+void
+en_intr(void *arg)
{
- struct en_softc *sc = (struct en_softc *) arg;
- struct mbuf *m;
- struct atm_pseudohdr ah;
- struct ifnet *ifp;
- u_int32_t reg, kick, val, mask, chip, vci, slot, dtq, drq;
- int lcv, idx, need_softserv = 0;
+ struct en_softc *sc = arg;
+ uint32_t reg, kick, mask;
+ int lcv, need_softserv;
- reg = EN_READ(sc, MID_INTACK);
+ EN_LOCK(sc);
- if ((reg & MID_INT_ANY) == 0)
- EN_INTR_RET(0); /* not us */
-
-#ifdef EN_DEBUG
- printf("%s: interrupt=0x%b\n", sc->sc_dev.dv_xname, reg, MID_INTBITS);
-#endif
+ reg = en_read(sc, MID_INTACK);
+ DBG(sc, INTR, ("interrupt=0x%b", reg, MID_INTBITS));
- /*
- * unexpected errors that need a reset
- */
+ if ((reg & MID_INT_ANY) == 0) {
+ EN_UNLOCK(sc);
+ return;
+ }
- if ((reg & (MID_INT_IDENT|MID_INT_LERR|MID_INT_DMA_ERR|MID_INT_SUNI)) != 0) {
- printf("%s: unexpected interrupt=0x%b, resetting card\n",
- sc->sc_dev.dv_xname, reg, MID_INTBITS);
+ /*
+ * unexpected errors that need a reset
+ */
+ if ((reg & (MID_INT_IDENT | MID_INT_LERR | MID_INT_DMA_ERR)) != 0) {
+ if_printf(&sc->enif, "unexpected interrupt=0x%b, resetting\n",
+ reg, MID_INTBITS);
#ifdef EN_DEBUG
#ifdef DDB
-#ifdef __FreeBSD__
- Debugger("en: unexpected error");
-#else
- Debugger();
-#endif
+ Debugger("en: unexpected error");
#endif /* DDB */
- sc->enif.if_flags &= ~IFF_RUNNING; /* FREEZE! */
+ sc->enif.if_flags &= ~IFF_RUNNING; /* FREEZE! */
#else
- en_reset(sc);
- en_init(sc);
-#endif
- EN_INTR_RET(1); /* for us */
- }
-
- /*******************
- * xmit interrupts *
- ******************/
-
- kick = 0; /* bitmask of channels to kick */
- if (reg & MID_INT_TX) { /* TX done! */
-
- /*
- * check for tx complete, if detected then this means that some space
- * has come free on the card. we must account for it and arrange to
- * kick the channel to life (in case it is stalled waiting on the card).
- */
- for (mask = 1, lcv = 0 ; lcv < EN_NTX ; lcv++, mask = mask * 2) {
- if (reg & MID_TXCHAN(lcv)) {
- kick = kick | mask; /* want to kick later */
- val = EN_READ(sc, MIDX_READPTR(lcv)); /* current read pointer */
- val = (val * sizeof(u_int32_t)) + sc->txslot[lcv].start;
- /* convert to offset */
- if (val > sc->txslot[lcv].cur)
- sc->txslot[lcv].bfree = val - sc->txslot[lcv].cur;
- else
- sc->txslot[lcv].bfree = (val + (EN_TXSZ*1024)) - sc->txslot[lcv].cur;
-#ifdef EN_DEBUG
- printf("%s: tx%d: trasmit done. %d bytes now free in buffer\n",
- sc->sc_dev.dv_xname, lcv, sc->txslot[lcv].bfree);
-#endif
- }
- }
- }
-
- if (reg & MID_INT_DMA_TX) { /* TX DMA done! */
-
- /*
- * check for TX DMA complete, if detected then this means that some DTQs
- * are now free. it also means some indma mbufs can be freed.
- * if we needed DTQs, kick all channels.
- */
- val = EN_READ(sc, MID_DMA_RDTX); /* chip's current location */
- idx = MID_DTQ_A2REG(sc->dtq_chip);/* where we last saw chip */
- if (sc->need_dtqs) {
- kick = MID_NTX_CH - 1; /* assume power of 2, kick all! */
- sc->need_dtqs = 0; /* recalculated in "kick" loop below */
-#ifdef EN_DEBUG
- printf("%s: cleared need DTQ condition\n", sc->sc_dev.dv_xname);
-#endif
- }
- while (idx != val) {
- sc->dtq_free++;
- if ((dtq = sc->dtq[idx]) != 0) {
- sc->dtq[idx] = 0; /* don't forget to zero it out when done */
- slot = EN_DQ_SLOT(dtq);
- _IF_DEQUEUE(&sc->txslot[slot].indma, m);
- if (!m) panic("enintr: dtqsync");
- sc->txslot[slot].mbsize -= EN_DQ_LEN(dtq);
-#ifdef EN_DEBUG
- printf("%s: tx%d: free %d dma bytes, mbsize now %d\n",
- sc->sc_dev.dv_xname, slot, EN_DQ_LEN(dtq),
- sc->txslot[slot].mbsize);
-#endif
- m_freem(m);
- }
- EN_WRAPADD(0, MID_DTQ_N, idx, 1);
- };
- sc->dtq_chip = MID_DTQ_REG2A(val); /* sync softc */
- }
-
-
- /*
- * kick xmit channels as needed
- */
-
- if (kick) {
-#ifdef EN_DEBUG
- printf("%s: tx kick mask = 0x%x\n", sc->sc_dev.dv_xname, kick);
-#endif
- for (mask = 1, lcv = 0 ; lcv < EN_NTX ; lcv++, mask = mask * 2) {
- if ((kick & mask) && sc->txslot[lcv].q.ifq_head) {
- en_txdma(sc, lcv); /* kick it! */
- }
- } /* for each slot */
- } /* if kick */
-
-
- /*******************
- * recv interrupts *
- ******************/
-
- /*
- * check for RX DMA complete, and pass the data "upstairs"
- */
-
- if (reg & MID_INT_DMA_RX) {
- val = EN_READ(sc, MID_DMA_RDRX); /* chip's current location */
- idx = MID_DRQ_A2REG(sc->drq_chip);/* where we last saw chip */
- while (idx != val) {
- sc->drq_free++;
- if ((drq = sc->drq[idx]) != 0) {
- sc->drq[idx] = 0; /* don't forget to zero it out when done */
- slot = EN_DQ_SLOT(drq);
- if (EN_DQ_LEN(drq) == 0) { /* "JK" trash DMA? */
- m = NULL;
- } else {
- _IF_DEQUEUE(&sc->rxslot[slot].indma, m);
- if (!m)
- panic("enintr: drqsync: %s: lost mbuf in slot %d!",
- sc->sc_dev.dv_xname, slot);
- }
- /* do something with this mbuf */
- if (sc->rxslot[slot].oth_flags & ENOTHER_DRAIN) { /* drain? */
- if (m)
- m_freem(m);
- vci = sc->rxslot[slot].atm_vci;
- if (sc->rxslot[slot].indma.ifq_head == NULL &&
- sc->rxslot[slot].q.ifq_head == NULL &&
- (EN_READ(sc, MID_VC(vci)) & MIDV_INSERVICE) == 0 &&
- (sc->rxslot[slot].oth_flags & ENOTHER_SWSL) == 0) {
- sc->rxslot[slot].oth_flags = ENOTHER_FREE; /* done drain */
- sc->rxslot[slot].atm_vci = RX_NONE;
- sc->rxvc2slot[vci] = RX_NONE;
-#ifdef EN_DEBUG
- printf("%s: rx%d: VCI %d now free\n", sc->sc_dev.dv_xname,
- slot, vci);
-#endif
- }
- } else if (m != NULL) {
- ATM_PH_FLAGS(&ah) = sc->rxslot[slot].atm_flags;
- ATM_PH_VPI(&ah) = 0;
- ATM_PH_SETVCI(&ah, sc->rxslot[slot].atm_vci);
-#ifdef EN_DEBUG
- printf("%s: rx%d: rxvci%d: atm_input, mbuf %p, len %d, hand %p\n",
- sc->sc_dev.dv_xname, slot, sc->rxslot[slot].atm_vci, m,
- EN_DQ_LEN(drq), sc->rxslot[slot].rxhand);
-#endif
-
- ifp = &sc->enif;
- ifp->if_ipackets++;
-
-#if NBPF > 0
- if (ifp->if_bpf)
- BPF_MTAP(ifp, m);
+ en_reset_ul(sc);
+ en_init(sc);
#endif
-
- atm_input(ifp, &ah, m, sc->rxslot[slot].rxhand);
+ EN_UNLOCK(sc);
+ return;
}
- }
- EN_WRAPADD(0, MID_DRQ_N, idx, 1);
- };
- sc->drq_chip = MID_DRQ_REG2A(val); /* sync softc */
-
- if (sc->need_drqs) { /* true if we had a DRQ shortage */
- need_softserv = 1;
- sc->need_drqs = 0;
-#ifdef EN_DEBUG
- printf("%s: cleared need DRQ condition\n", sc->sc_dev.dv_xname);
-#endif
- }
- }
-
- /*
- * handle service interrupts
- */
-
- if (reg & MID_INT_SERVICE) {
- chip = MID_SL_REG2A(EN_READ(sc, MID_SERV_WRITE));
-
- while (sc->hwslistp != chip) {
-
- /* fetch and remove it from hardware service list */
- vci = EN_READ(sc, sc->hwslistp);
- EN_WRAPADD(MID_SLOFF, MID_SLEND, sc->hwslistp, 4);/* advance hw ptr */
- slot = sc->rxvc2slot[vci];
- if (slot == RX_NONE) {
-#ifdef EN_DEBUG
- printf("%s: unexpected rx interrupt on VCI %d\n",
- sc->sc_dev.dv_xname, vci);
-#endif
- EN_WRITE(sc, MID_VC(vci), MIDV_TRASH); /* rx off, damn it! */
- continue; /* next */
- }
- EN_WRITE(sc, MID_VC(vci), sc->rxslot[slot].mode); /* remove from hwsl */
- EN_COUNT(sc->hwpull);
+#if 0
+ if (reg & MID_INT_SUNI)
+ if_printf(&sc->enif, "interrupt from SUNI (probably carrier "
+ "change)\n");
+#endif
+
+ kick = 0;
+ if (reg & MID_INT_TX)
+ kick |= en_intr_tx(sc, reg);
+
+ if (reg & MID_INT_DMA_TX)
+ kick |= en_intr_tx_dma(sc);
+
+ /*
+ * kick xmit channels as needed.
+ */
+ if (kick) {
+ DBG(sc, INTR, ("tx kick mask = 0x%x", kick));
+ for (mask = 1, lcv = 0 ; lcv < EN_NTX ; lcv++, mask = mask * 2)
+ if ((kick & mask) && _IF_QLEN(&sc->txslot[lcv].q) != 0)
+ en_txdma(sc, &sc->txslot[lcv]);
+ }
-#ifdef EN_DEBUG
- printf("%s: pulled VCI %d off hwslist\n", sc->sc_dev.dv_xname, vci);
-#endif
+ need_softserv = 0;
+ if (reg & MID_INT_DMA_RX)
+ need_softserv |= en_intr_rx_dma(sc);
- /* add it to the software service list (if needed) */
- if ((sc->rxslot[slot].oth_flags & ENOTHER_SWSL) == 0) {
- EN_COUNT(sc->swadd);
- need_softserv = 1;
- sc->rxslot[slot].oth_flags |= ENOTHER_SWSL;
- sc->swslist[sc->swsl_tail] = slot;
- EN_WRAPADD(0, MID_SL_N, sc->swsl_tail, 1);
- sc->swsl_size++;
-#ifdef EN_DEBUG
- printf("%s: added VCI %d to swslist\n", sc->sc_dev.dv_xname, vci);
-#endif
- }
- };
- }
+ if (reg & MID_INT_SERVICE)
+ need_softserv |= en_intr_service(sc);
- /*
- * now service (function too big to include here)
- */
+ if (need_softserv)
+ en_service(sc);
- if (need_softserv)
- en_service(sc);
+ /*
+ * keep our stats
+ */
+ if (reg & MID_INT_DMA_OVR) {
+ EN_COUNT(sc->stats.dmaovr);
+ DBG(sc, INTR, ("MID_INT_DMA_OVR"));
+ }
+ reg = en_read(sc, MID_STAT);
+ sc->stats.otrash += MID_OTRASH(reg);
+ sc->stats.vtrash += MID_VTRASH(reg);
- /*
- * keep our stats
- */
+ EN_UNLOCK(sc);
+}
- if (reg & MID_INT_DMA_OVR) {
- EN_COUNT(sc->dmaovr);
-#ifdef EN_DEBUG
- printf("%s: MID_INT_DMA_OVR\n", sc->sc_dev.dv_xname);
-#endif
- }
- reg = EN_READ(sc, MID_STAT);
-#ifdef EN_STAT
- sc->otrash += MID_OTRASH(reg);
- sc->vtrash += MID_VTRASH(reg);
-#endif
+/*********************************************************************/
+/*
+ * Probing the DMA brokeness of the card
+ */
- EN_INTR_RET(1); /* for us */
+/*
+ * Physical address load helper function for DMA probe
+ *
+ * LOCK: unlocked, not needed
+ */
+static void
+en_dmaprobe_load(void *uarg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ if (error == 0)
+ *(bus_addr_t *)uarg = segs[0].ds_addr;
}
-
/*
- * en_service: handle a service interrupt
+ * en_dmaprobe: helper function for en_attach.
*
- * Q: why do we need a software service list?
+ * see how the card handles DMA by running a few DMA tests. we need
+ * to figure out the largest number of bytes we can DMA in one burst
+ * ("bestburstlen"), and if the starting address for a burst needs to
+ * be aligned on any sort of boundary or not ("alburst").
*
- * A: if we remove a VCI from the hardware list and we find that we are
- * out of DRQs we must defer processing until some DRQs become free.
- * so we must remember to look at this RX VCI/slot later, but we can't
- * put it back on the hardware service list (since that isn't allowed).
- * so we instead save it on the software service list. it would be nice
- * if we could peek at the VCI on top of the hwservice list without removing
- * it, however this leads to a race condition: if we peek at it and
- * decide we are done with it new data could come in before we have a
- * chance to remove it from the hwslist. by the time we get it out of
- * the list the interrupt for the new data will be lost. oops!
+ * Things turn out more complex than that, because on my (harti) brand
+ * new motherboard (2.4GHz) we can do 64byte aligned DMAs, but everything
+ * we more than 4 bytes fails (with an RX DMA timeout) for physical
+ * addresses that end with 0xc. Therefor we search not only the largest
+ * burst that is supported (hopefully 64) but also check what is the largerst
+ * unaligned supported size. If that appears to be lesser than 4 words,
+ * set the noalbursts flag. That will be set only if also alburst is set.
+ */
+
+/*
+ * en_dmaprobe_doit: do actual testing for the DMA test.
+ * Cycle through all bursts sizes from 8 up to 64 and try whether it works.
+ * Return the largest one that works.
*
+ * LOCK: unlocked, not needed
*/
+static int
+en_dmaprobe_doit(struct en_softc *sc, uint8_t *sp, bus_addr_t psp)
+{
+ uint8_t *dp = sp + MIDDMA_MAXBURST;
+ bus_addr_t pdp = psp + MIDDMA_MAXBURST;
+ int lcv, retval = 4, cnt;
+ uint32_t reg, bcode, midvloc;
+
+ if (sc->en_busreset)
+ sc->en_busreset(sc);
+ en_write(sc, MID_RESID, 0x0); /* reset card before touching RAM */
+
+ /*
+ * set up a 1k buffer at MID_BUFOFF
+ */
+ midvloc = ((MID_BUFOFF - MID_RAMOFF) / sizeof(uint32_t))
+ >> MIDV_LOCTOPSHFT;
+ en_write(sc, MIDX_PLACE(0), MIDX_MKPLACE(en_k2sz(1), midvloc));
+ en_write(sc, MID_VC(0), (midvloc << MIDV_LOCSHIFT)
+ | (en_k2sz(1) << MIDV_SZSHIFT) | MIDV_TRASH);
+ en_write(sc, MID_DST_RP(0), 0);
+ en_write(sc, MID_WP_ST_CNT(0), 0);
+
+ /* set up sample data */
+ for (lcv = 0 ; lcv < MIDDMA_MAXBURST; lcv++)
+ sp[lcv] = lcv + 1;
+
+ /* enable DMA (only) */
+ en_write(sc, MID_MAST_CSR, MID_MCSR_ENDMA);
+
+ sc->drq_chip = MID_DRQ_REG2A(en_read(sc, MID_DMA_RDRX));
+ sc->dtq_chip = MID_DTQ_REG2A(en_read(sc, MID_DMA_RDTX));
+
+ /*
+ * try it now . . . DMA it out, then DMA it back in and compare
+ *
+ * note: in order to get the dma stuff to reverse directions it wants
+ * the "end" flag set! since we are not dma'ing valid data we may
+ * get an ident mismatch interrupt (which we will ignore).
+ */
+ DBG(sc, DMA, ("test sp=%p/%#lx, dp=%p/%#lx",
+ sp, (u_long)psp, dp, (u_long)pdp));
+ for (lcv = 8 ; lcv <= MIDDMA_MAXBURST ; lcv = lcv * 2) {
+ DBG(sc, DMA, ("test lcv=%d", lcv));
+
+ /* zero SRAM and dest buffer */
+ bus_space_set_region_4(sc->en_memt, sc->en_base,
+ MID_BUFOFF, 0, 1024 / 4);
+ bzero(dp, MIDDMA_MAXBURST);
+
+ bcode = en_sz2b(lcv);
+
+ /* build lcv-byte-DMA x NBURSTS */
+ if (sc->is_adaptec)
+ en_write(sc, sc->dtq_chip,
+ MID_MK_TXQ_ADP(lcv, 0, MID_DMA_END, 0));
+ else
+ en_write(sc, sc->dtq_chip,
+ MID_MK_TXQ_ENI(1, 0, MID_DMA_END, bcode));
+ en_write(sc, sc->dtq_chip + 4, psp);
+ EN_WRAPADD(MID_DTQOFF, MID_DTQEND, sc->dtq_chip, 8);
+ en_write(sc, MID_DMA_WRTX, MID_DTQ_A2REG(sc->dtq_chip));
+
+ cnt = 1000;
+ while ((reg = en_readx(sc, MID_DMA_RDTX)) !=
+ MID_DTQ_A2REG(sc->dtq_chip)) {
+ DELAY(1);
+ if (--cnt == 0) {
+ DBG(sc, DMA, ("unexpected timeout in tx "
+ "DMA test\n alignment=0x%lx, burst size=%d"
+ ", dma addr reg=%#x, rdtx=%#x, stat=%#x\n",
+ (u_long)sp & 63, lcv,
+ en_read(sc, MID_DMA_ADDR), reg,
+ en_read(sc, MID_INTSTAT)));
+ return (retval);
+ }
+ }
-STATIC void en_service(sc)
+ reg = en_read(sc, MID_INTACK);
+ if ((reg & MID_INT_DMA_TX) != MID_INT_DMA_TX) {
+ DBG(sc, DMA, ("unexpected status in tx DMA test: %#x\n",
+ reg));
+ return (retval);
+ }
+ /* re-enable DMA (only) */
+ en_write(sc, MID_MAST_CSR, MID_MCSR_ENDMA);
+
+ /* "return to sender..." address is known ... */
+
+ /* build lcv-byte-DMA x NBURSTS */
+ if (sc->is_adaptec)
+ en_write(sc, sc->drq_chip,
+ MID_MK_RXQ_ADP(lcv, 0, MID_DMA_END, 0));
+ else
+ en_write(sc, sc->drq_chip,
+ MID_MK_RXQ_ENI(1, 0, MID_DMA_END, bcode));
+ en_write(sc, sc->drq_chip + 4, pdp);
+ EN_WRAPADD(MID_DRQOFF, MID_DRQEND, sc->drq_chip, 8);
+ en_write(sc, MID_DMA_WRRX, MID_DRQ_A2REG(sc->drq_chip));
+ cnt = 1000;
+ while ((reg = en_readx(sc, MID_DMA_RDRX)) !=
+ MID_DRQ_A2REG(sc->drq_chip)) {
+ DELAY(1);
+ cnt--;
+ if (--cnt == 0) {
+ DBG(sc, DMA, ("unexpected timeout in rx "
+ "DMA test, rdrx=%#x\n", reg));
+ return (retval);
+ }
+ }
+ reg = en_read(sc, MID_INTACK);
+ if ((reg & MID_INT_DMA_RX) != MID_INT_DMA_RX) {
+ DBG(sc, DMA, ("unexpected status in rx DMA "
+ "test: 0x%x\n", reg));
+ return (retval);
+ }
+ if (bcmp(sp, dp, lcv)) {
+ DBG(sc, DMA, ("DMA test failed! lcv=%d, sp=%p, "
+ "dp=%p", lcv, sp, dp));
+ return (retval);
+ }
-struct en_softc *sc;
+ retval = lcv;
+ }
+ return (retval); /* studly 64 byte DMA present! oh baby!! */
+}
+/*
+ * Find the best DMA parameters
+ *
+ * LOCK: unlocked, not needed
+ */
+static void
+en_dmaprobe(struct en_softc *sc)
{
- struct mbuf *m, *tmp;
- u_int32_t cur, dstart, rbd, pdu, *sav, dma, bcode, count, *data, *datastop;
- u_int32_t start, stop, cnt, needalign;
- int slot, raw, aal5, llc, vci, fill, mlen, tlen, drqneed, need, needfill, end;
-
- aal5 = 0; /* Silence gcc */
-next_vci:
- if (sc->swsl_size == 0) {
-#ifdef EN_DEBUG
- printf("%s: en_service done\n", sc->sc_dev.dv_xname);
-#endif
- return; /* >>> exit here if swsl now empty <<< */
- }
+ bus_dma_tag_t tag;
+ bus_dmamap_t map;
+ int err;
+ void *buffer;
+ int bestalgn, lcv, try, bestnoalgn;
+ bus_addr_t phys;
+ uint8_t *addr;
+
+ sc->alburst = 0;
+ sc->noalbursts = 0;
+
+ /*
+ * Allocate some DMA-able memory.
+ * We need 3 times the max burst size aligned to the max burst size.
+ */
+ err = bus_dma_tag_create(NULL, MIDDMA_MAXBURST, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ 3 * MIDDMA_MAXBURST, 1, 3 * MIDDMA_MAXBURST, 0, &tag);
+ if (err)
+ panic("%s: cannot create test DMA tag %d", __func__, err);
+
+ err = bus_dmamem_alloc(tag, &buffer, 0, &map);
+ if (err)
+ panic("%s: cannot allocate test DMA memory %d", __func__, err);
+
+ err = bus_dmamap_load(tag, map, buffer, 3 * MIDDMA_MAXBURST,
+ en_dmaprobe_load, &phys, 0);
+ if (err)
+ panic("%s: cannot load test DMA map %d", __func__, err);
+ addr = buffer;
+ DBG(sc, DMA, ("phys=%#lx addr=%p", (u_long)phys, addr));
+
+ /*
+ * Now get the best burst size of the aligned case.
+ */
+ bestalgn = bestnoalgn = en_dmaprobe_doit(sc, addr, phys);
+
+ /*
+ * Now try unaligned.
+ */
+ for (lcv = 4; lcv < MIDDMA_MAXBURST; lcv += 4) {
+ try = en_dmaprobe_doit(sc, addr + lcv, phys + lcv);
+
+ if (try < bestnoalgn)
+ bestnoalgn = try;
+ }
- /*
- * get slot/vci to service
- */
+ if (bestnoalgn < bestalgn) {
+ sc->alburst = 1;
+ if (bestnoalgn < 32)
+ sc->noalbursts = 1;
+ }
- slot = sc->swslist[sc->swsl_head];
- vci = sc->rxslot[slot].atm_vci;
-#ifdef EN_DIAG
- if (sc->rxvc2slot[vci] != slot) panic("en_service rx slot/vci sync");
-#endif
+ sc->bestburstlen = bestalgn;
+ sc->bestburstshift = en_log2(bestalgn);
+ sc->bestburstmask = sc->bestburstlen - 1; /* must be power of 2 */
+ sc->bestburstcode = en_sz2b(bestalgn);
+
+ /*
+ * Reset the chip before freeing the buffer. It may still be trying
+ * to DMA.
+ */
+ if (sc->en_busreset)
+ sc->en_busreset(sc);
+ en_write(sc, MID_RESID, 0x0); /* reset card before touching RAM */
+
+ DELAY(10000); /* may still do DMA */
+
+ /*
+ * Free the DMA stuff
+ */
+ bus_dmamap_unload(tag, map);
+ bus_dmamem_free(tag, buffer, map);
+ bus_dma_tag_destroy(tag);
+}
- /*
- * determine our mode and if we've got any work to do
- */
+/*********************************************************************/
+/*
+ * Attach/detach.
+ */
- raw = sc->rxslot[slot].oth_flags & ENOTHER_RAW;
- start= sc->rxslot[slot].start;
- stop= sc->rxslot[slot].stop;
- cur = sc->rxslot[slot].cur;
+/*
+ * Attach to the card.
+ *
+ * LOCK: unlocked, not needed (but initialized)
+ */
+int
+en_attach(struct en_softc *sc)
+{
+ struct ifnet *ifp = &sc->enif;
+ int sz;
+ uint32_t reg, lcv, check, ptr, sav, midvloc;
+
+#ifdef EN_DEBUG
+ sc->debug = EN_DEBUG;
+#endif
+ /*
+ * Probe card to determine memory size.
+ *
+ * The stupid ENI card always reports to PCI that it needs 4MB of
+ * space (2MB regs and 2MB RAM). If it has less than 2MB RAM the
+ * addresses wrap in the RAM address space (i.e. on a 512KB card
+ * addresses 0x3ffffc, 0x37fffc, and 0x2ffffc are aliases for
+ * 0x27fffc [note that RAM starts at offset 0x200000]).
+ */
+
+ /* reset card before touching RAM */
+ if (sc->en_busreset)
+ sc->en_busreset(sc);
+ en_write(sc, MID_RESID, 0x0);
+
+ for (lcv = MID_PROBEOFF; lcv <= MID_MAXOFF ; lcv += MID_PROBSIZE) {
+ en_write(sc, lcv, lcv); /* data[address] = address */
+ for (check = MID_PROBEOFF; check < lcv ;check += MID_PROBSIZE) {
+ reg = en_read(sc, check);
+ if (reg != check)
+ /* found an alias! - quit */
+ goto done_probe;
+ }
+ }
+ done_probe:
+ lcv -= MID_PROBSIZE; /* take one step back */
+ sc->en_obmemsz = (lcv + 4) - MID_RAMOFF;
+
+ /*
+ * determine the largest DMA burst supported
+ */
+ en_dmaprobe(sc);
+
+ /*
+ * "hello world"
+ */
+
+ /* reset */
+ if (sc->en_busreset)
+ sc->en_busreset(sc);
+ en_write(sc, MID_RESID, 0x0); /* reset */
+
+ /* zero memory */
+ bus_space_set_region_4(sc->en_memt, sc->en_base,
+ MID_RAMOFF, 0, sc->en_obmemsz / 4);
+
+ reg = en_read(sc, MID_RESID);
+
+ if_printf(&sc->enif, "ATM midway v%d, board IDs %d.%d, %s%s%s, "
+ "%ldKB on-board RAM\n", MID_VER(reg), MID_MID(reg), MID_DID(reg),
+ (MID_IS_SABRE(reg)) ? "sabre controller, " : "",
+ (MID_IS_SUNI(reg)) ? "SUNI" : "Utopia",
+ (!MID_IS_SUNI(reg) && MID_IS_UPIPE(reg)) ? " (pipelined)" : "",
+ (long)sc->en_obmemsz / 1024);
+
+ if (sc->is_adaptec) {
+ if (sc->bestburstlen == 64 && sc->alburst == 0)
+ if_printf(&sc->enif, "passed 64 byte DMA test\n");
+ else
+ if_printf(&sc->enif, "FAILED DMA TEST: burst=%d, "
+ "alburst=%d\n", sc->bestburstlen, sc->alburst);
+ } else {
+ if_printf(&sc->enif, "maximum DMA burst length = %d bytes%s\n",
+ sc->bestburstlen, sc->alburst ? sc->noalbursts ?
+ " (no large bursts)" : " (must align)" : "");
+ }
-#ifdef EN_DEBUG
- printf("%s: rx%d: service vci=%d raw=%d start/stop/cur=0x%x 0x%x 0x%x\n",
- sc->sc_dev.dv_xname, slot, vci, raw, start, stop, cur);
-#endif
+ /*
+ * link into network subsystem and prepare card
+ */
+ sc->enif.if_softc = sc;
+ ifp->if_flags = IFF_SIMPLEX;
+ ifp->if_ioctl = en_ioctl;
+ ifp->if_start = en_start;
+
+ /*
+ * Make the sysctl tree
+ */
+ sysctl_ctx_init(&sc->sysctl_ctx);
+
+ if ((sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw_en), OID_AUTO,
+ device_get_nameunit(sc->dev), CTLFLAG_RD, 0, "")) == NULL)
+ goto fail;
+
+ if (SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "istats", CTLFLAG_RD, sc, 0, en_sysctl_istats,
+ "A", "internal statistics") == NULL)
+ goto fail;
+
+#ifdef EN_DEBUG
+ if (SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "debug", CTLFLAG_RW , &sc->debug, 0, "") == NULL)
+ goto fail;
+#endif
+
+ mtx_init(&sc->en_mtx, device_get_nameunit(sc->dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+
+ MGET(sc->padbuf, M_TRYWAIT, MT_DATA);
+ if (sc->padbuf == NULL)
+ goto fail;
+ bzero(sc->padbuf->m_data, MLEN);
+
+ if (bus_dma_tag_create(NULL, 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ EN_TXSZ * 1024, EN_MAX_DMASEG, EN_TXSZ * 1024, 0, &sc->txtag))
+ goto fail;
+
+ sc->map_zone = uma_zcreate("en dma maps", sizeof(struct en_map),
+ en_map_ctor, en_map_dtor, NULL, en_map_fini, UMA_ALIGN_PTR,
+ UMA_ZONE_ZINIT);
+ if (sc->map_zone == NULL)
+ goto fail;
+ uma_zone_set_max(sc->map_zone, EN_MAX_MAPS);
+
+ /*
+ * init softc
+ */
+ for (lcv = 0 ; lcv < MID_N_VC ; lcv++) {
+ sc->rxvc2slot[lcv] = RX_NONE;
+ sc->txspeed[lcv] = 0; /* full */
+ sc->txvc2slot[lcv] = 0; /* full speed == slot 0 */
+ }
-same_vci:
- dstart = MIDV_DSTART(EN_READ(sc, MID_DST_RP(vci)));
- dstart = (dstart * sizeof(u_int32_t)) + start;
-
- /* check to see if there is any data at all */
- if (dstart == cur) {
-defer: /* defer processing */
- EN_WRAPADD(0, MID_SL_N, sc->swsl_head, 1);
- sc->rxslot[slot].oth_flags &= ~ENOTHER_SWSL;
- sc->swsl_size--;
- /* >>> remove from swslist <<< */
-#ifdef EN_DEBUG
- printf("%s: rx%d: remove vci %d from swslist\n",
- sc->sc_dev.dv_xname, slot, vci);
-#endif
- goto next_vci;
- }
-
- /*
- * figure out how many bytes we need
- * [mlen = # bytes to go in mbufs, fill = # bytes to dump (MIDDMA_JK)]
- */
-
- if (raw) {
-
- /* raw mode (aka boodi mode) */
- fill = 0;
- if (dstart > cur)
- mlen = dstart - cur;
- else
- mlen = (dstart + (EN_RXSZ*1024)) - cur;
-
- if (mlen < sc->rxslot[slot].raw_threshold)
- goto defer; /* too little data to deal with */
-
- } else {
-
- /* normal mode */
- aal5 = (sc->rxslot[slot].atm_flags & ATM_PH_AAL5);
- llc = (aal5 && (sc->rxslot[slot].atm_flags & ATM_PH_LLCSNAP)) ? 1 : 0;
- rbd = EN_READ(sc, cur);
- if (MID_RBD_ID(rbd) != MID_RBD_STDID)
- panic("en_service: id mismatch");
-
- if (rbd & MID_RBD_T) {
- mlen = 0; /* we've got trash */
- fill = MID_RBD_SIZE;
- EN_COUNT(sc->ttrash);
-#ifdef EN_DEBUG
- printf("RX overflow lost %d cells!\n", MID_RBD_CNT(rbd));
-#endif
- } else if (!aal5) {
- mlen = MID_RBD_SIZE + MID_CHDR_SIZE + MID_ATMDATASZ; /* 1 cell (ick!) */
- fill = 0;
- } else {
- struct ifnet *ifp;
-
- tlen = (MID_RBD_CNT(rbd) * MID_ATMDATASZ) + MID_RBD_SIZE;
- pdu = cur + tlen - MID_PDU_SIZE;
- if (pdu >= stop)
- pdu -= (EN_RXSZ*1024);
- pdu = EN_READ(sc, pdu); /* get PDU in correct byte order */
- fill = tlen - MID_RBD_SIZE - MID_PDU_LEN(pdu);
- if (fill < 0 || (rbd & MID_RBD_CRCERR) != 0) {
- static int first = 1;
-
- if (first) {
- printf("%s: %s, dropping frame\n", sc->sc_dev.dv_xname,
- (rbd & MID_RBD_CRCERR) ?
- "CRC error" : "invalid AAL5 PDU length");
- printf("%s: got %d cells (%d bytes), AAL5 len is %d bytes (pdu=0x%x)\n",
- sc->sc_dev.dv_xname, MID_RBD_CNT(rbd),
- tlen - MID_RBD_SIZE, MID_PDU_LEN(pdu), pdu);
-#ifndef EN_DEBUG
- printf("CRC error report disabled from now on!\n");
- first = 0;
-#endif
+ sz = sc->en_obmemsz - (MID_BUFOFF - MID_RAMOFF);
+ ptr = sav = MID_BUFOFF;
+ ptr = roundup(ptr, EN_TXSZ * 1024); /* align */
+ sz = sz - (ptr - sav);
+ if (EN_TXSZ*1024 * EN_NTX > sz) {
+ if_printf(&sc->enif, "EN_NTX/EN_TXSZ too big\n");
+ goto fail;
+ }
+ for (lcv = 0 ;lcv < EN_NTX ;lcv++) {
+ sc->txslot[lcv].mbsize = 0;
+ sc->txslot[lcv].start = ptr;
+ ptr += (EN_TXSZ * 1024);
+ sz -= (EN_TXSZ * 1024);
+ sc->txslot[lcv].stop = ptr;
+ sc->txslot[lcv].nref = 0;
+ DBG(sc, INIT, ("tx%d: start 0x%x, stop 0x%x", lcv,
+ sc->txslot[lcv].start, sc->txslot[lcv].stop));
}
- fill = tlen;
-
- ifp = &sc->enif;
- ifp->if_ierrors++;
-
- }
- mlen = tlen - fill;
- }
-
- }
-
- /*
- * now allocate mbufs for mlen bytes of data, if out of mbufs, trash all
- *
- * notes:
- * 1. it is possible that we've already allocated an mbuf for this pkt
- * but ran out of DRQs, in which case we saved the allocated mbuf on
- * "q".
- * 2. if we save an mbuf in "q" we store the "cur" (pointer) in the front
- * of the mbuf as an identity (that we can check later), and we also
- * store drqneed (so we don't have to recompute it).
- * 3. after this block of code, if m is still NULL then we ran out of mbufs
- */
-
- m = sc->rxslot[slot].q.ifq_head;
- drqneed = 1;
- if (m) {
- sav = mtod(m, u_int32_t *);
- if (sav[0] != cur) {
-#ifdef EN_DEBUG
- printf("%s: rx%d: q'ed mbuf %p not ours\n",
- sc->sc_dev.dv_xname, slot, m);
-#endif
- m = NULL; /* wasn't ours */
- EN_COUNT(sc->rxqnotus);
- } else {
- EN_COUNT(sc->rxqus);
- _IF_DEQUEUE(&sc->rxslot[slot].q, m);
- drqneed = sav[1];
-#ifdef EN_DEBUG
- printf("%s: rx%d: recovered q'ed mbuf %p (drqneed=%d)\n",
- sc->sc_dev.dv_xname, slot, m, drqneed);
-#endif
- }
- }
-
- if (mlen != 0 && m == NULL) {
- m = en_mget(sc, mlen, &drqneed); /* allocate! */
- if (m == NULL) {
- fill += mlen;
- mlen = 0;
- EN_COUNT(sc->rxmbufout);
-#ifdef EN_DEBUG
- printf("%s: rx%d: out of mbufs\n", sc->sc_dev.dv_xname, slot);
-#endif
- }
-#ifdef EN_DEBUG
- printf("%s: rx%d: allocate mbuf %p, mlen=%d, drqneed=%d\n",
- sc->sc_dev.dv_xname, slot, m, mlen, drqneed);
-#endif
- }
-#ifdef EN_DEBUG
- printf("%s: rx%d: VCI %d, mbuf_chain %p, mlen %d, fill %d\n",
- sc->sc_dev.dv_xname, slot, vci, m, mlen, fill);
-#endif
+ sav = ptr;
+ ptr = roundup(ptr, EN_RXSZ * 1024); /* align */
+ sz = sz - (ptr - sav);
+ sc->en_nrx = sz / (EN_RXSZ * 1024);
+ if (sc->en_nrx <= 0) {
+ if_printf(&sc->enif, "EN_NTX/EN_TXSZ/EN_RXSZ too big\n");
+ goto fail;
+ }
- /*
- * now check to see if we've got the DRQs needed. if we are out of
- * DRQs we must quit (saving our mbuf, if we've got one).
- */
+ /*
+ * ensure that there is always one VC slot on the service list free
+ * so that we can tell the difference between a full and empty list.
+ */
+ if (sc->en_nrx >= MID_N_VC)
+ sc->en_nrx = MID_N_VC - 1;
+
+ for (lcv = 0 ; lcv < sc->en_nrx ; lcv++) {
+ sc->rxslot[lcv].rxhand = NULL;
+ sc->rxslot[lcv].oth_flags = ENOTHER_FREE;
+ midvloc = sc->rxslot[lcv].start = ptr;
+ ptr += (EN_RXSZ * 1024);
+ sz -= (EN_RXSZ * 1024);
+ sc->rxslot[lcv].stop = ptr;
+ midvloc = midvloc - MID_RAMOFF;
+ /* mask, cvt to words */
+ midvloc = (midvloc & ~((EN_RXSZ*1024) - 1)) >> 2;
+ /* we only want the top 11 bits */
+ midvloc = midvloc >> MIDV_LOCTOPSHFT;
+ midvloc = (midvloc & MIDV_LOCMASK) << MIDV_LOCSHIFT;
+ sc->rxslot[lcv].mode = midvloc |
+ (en_k2sz(EN_RXSZ) << MIDV_SZSHIFT) | MIDV_TRASH;
+
+ DBG(sc, INIT, ("rx%d: start 0x%x, stop 0x%x, mode 0x%x", lcv,
+ sc->rxslot[lcv].start, sc->rxslot[lcv].stop,
+ sc->rxslot[lcv].mode));
+ }
- needfill = (fill) ? 1 : 0;
- if (drqneed + needfill > sc->drq_free) {
- sc->need_drqs = 1; /* flag condition */
- if (m == NULL) {
- EN_COUNT(sc->rxoutboth);
-#ifdef EN_DEBUG
- printf("%s: rx%d: out of DRQs *and* mbufs!\n", sc->sc_dev.dv_xname, slot);
-#endif
- return; /* >>> exit here if out of both mbufs and DRQs <<< */
- }
- sav = mtod(m, u_int32_t *);
- sav[0] = cur;
- sav[1] = drqneed;
- _IF_ENQUEUE(&sc->rxslot[slot].q, m);
- EN_COUNT(sc->rxdrqout);
-#ifdef EN_DEBUG
- printf("%s: rx%d: out of DRQs\n", sc->sc_dev.dv_xname, slot);
-#endif
- return; /* >>> exit here if out of DRQs <<< */
- }
-
- /*
- * at this point all resources have been allocated and we are commited
- * to servicing this slot.
- *
- * dma = last location we told chip about
- * cur = current location
- * mlen = space in the mbuf we want
- * need = bytes to xfer in (decrs to zero)
- * fill = how much fill we need
- * tlen = how much data to transfer to this mbuf
- * cnt/bcode/count = <same as xmit>
- *
- * 'needfill' not used after this point
- */
-
- dma = cur; /* dma = last location we told chip about */
- need = roundup(mlen, sizeof(u_int32_t));
- fill = fill - (need - mlen); /* note: may invalidate 'needfill' */
-
- for (tmp = m ; tmp != NULL && need > 0 ; tmp = tmp->m_next) {
- tlen = roundup(tmp->m_len, sizeof(u_int32_t)); /* m_len set by en_mget */
- data = mtod(tmp, u_int32_t *);
+ bzero(&sc->stats, sizeof(sc->stats));
-#ifdef EN_DEBUG
- printf("%s: rx%d: load mbuf %p, m_len=%d, m_data=%p, tlen=%d\n",
- sc->sc_dev.dv_xname, slot, tmp, tmp->m_len, tmp->m_data, tlen);
-#endif
-
- /* copy data */
- if (EN_NORXDMA || !en_dma || tlen < EN_MINDMA) {
- datastop = (u_int32_t *)((u_char *) data + tlen);
- /* copy loop: preserve byte order!!! use READDAT */
- while (data != datastop) {
- *data = EN_READDAT(sc, cur);
- data++;
- EN_WRAPADD(start, stop, cur, 4);
- }
- need -= tlen;
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: copied %d bytes (%d left)\n",
- sc->sc_dev.dv_xname, slot, vci, tlen, need);
-#endif
- continue;
- }
+ if_printf(&sc->enif, "%d %dKB receive buffers, %d %dKB transmit "
+ "buffers\n", sc->en_nrx, EN_RXSZ, EN_NTX, EN_TXSZ);
+ if_printf(&sc->enif, "end station identifier (mac address) %6D\n",
+ sc->macaddr, ":");
- /* DMA data (check to see if we need to sync DRQ first) */
- if (dma != cur) {
- EN_DRQADD(sc, WORD_IDX(start,cur), vci, MIDDMA_JK, 0, 0, 0, 0);
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: drq_sync: advance pointer to %d\n",
- sc->sc_dev.dv_xname, slot, vci, cur);
-#endif
- }
+ /*
+ * final commit
+ */
+ if_attach(ifp);
+ atm_ifattach(ifp);
-#if !defined(MIDWAY_ENIONLY)
-
- /*
- * the adaptec DMA engine is smart and handles everything for us.
- */
-
- if (sc->is_adaptec) {
- need -= tlen;
- EN_WRAPADD(start, stop, cur, tlen);
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: adp_dma %d bytes (%d left)\n",
- sc->sc_dev.dv_xname, slot, vci, tlen, need);
-#endif
- end = (need == 0 && !fill) ? MID_DMA_END : 0;
- EN_DRQADD(sc, tlen, vci, 0, vtophys(data), mlen, slot, end);
- if (end)
- goto done;
- dma = cur; /* update dma pointer */
- continue;
- }
-#endif /* !MIDWAY_ENIONLY */
-
-
-#if !defined(MIDWAY_ADPONLY)
-
- /*
- * the ENI DMA engine is not so smart and need more help from us
- */
-
- /* do we need to do a DMA op to align? */
- if (sc->alburst &&
- (needalign = (((uintptr_t) (void *) data) & sc->bestburstmask)) != 0) {
- cnt = sc->bestburstlen - needalign;
- if (cnt > tlen) {
- cnt = tlen;
- count = cnt / sizeof(u_int32_t);
- bcode = MIDDMA_WORD;
- } else {
- count = cnt / sizeof(u_int32_t);
- bcode = en_dmaplan[count].bcode;
- count = cnt >> en_dmaplan[count].divshift;
- }
- need -= cnt;
- EN_WRAPADD(start, stop, cur, cnt);
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: al_dma %d bytes (%d left)\n",
- sc->sc_dev.dv_xname, slot, vci, cnt, need);
+#ifdef ENABLE_BPF
+ bpfattach(ifp, DLT_ATM_RFC1483, sizeof(struct atmllc));
#endif
- tlen -= cnt;
- end = (need == 0 && !fill) ? MID_DMA_END : 0;
- EN_DRQADD(sc, count, vci, bcode, vtophys(data), mlen, slot, end);
- if (end)
- goto done;
- data = (u_int32_t *)((u_char *) data + cnt);
- }
-
- /* do we need a max-sized burst? */
- if (tlen >= sc->bestburstlen) {
- count = tlen >> sc->bestburstshift;
- cnt = count << sc->bestburstshift;
- bcode = sc->bestburstcode;
- need -= cnt;
- EN_WRAPADD(start, stop, cur, cnt);
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: best_dma %d bytes (%d left)\n",
- sc->sc_dev.dv_xname, slot, vci, cnt, need);
-#endif
- tlen -= cnt;
- end = (need == 0 && !fill) ? MID_DMA_END : 0;
- EN_DRQADD(sc, count, vci, bcode, vtophys(data), mlen, slot, end);
- if (end)
- goto done;
- data = (u_int32_t *)((u_char *) data + cnt);
- }
-
- /* do we need to do a cleanup burst? */
- if (tlen) {
- count = tlen / sizeof(u_int32_t);
- bcode = en_dmaplan[count].bcode;
- count = tlen >> en_dmaplan[count].divshift;
- need -= tlen;
- EN_WRAPADD(start, stop, cur, tlen);
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: cleanup_dma %d bytes (%d left)\n",
- sc->sc_dev.dv_xname, slot, vci, tlen, need);
-#endif
- end = (need == 0 && !fill) ? MID_DMA_END : 0;
- EN_DRQADD(sc, count, vci, bcode, vtophys(data), mlen, slot, end);
- if (end)
- goto done;
- }
- dma = cur; /* update dma pointer */
+ return (0);
-#endif /* !MIDWAY_ADPONLY */
+ fail:
+ en_destroy(sc);
+ return (-1);
+}
- }
+/*
+ * Free all internal resources. No access to bus resources here.
+ * No locking required here (interrupt is already disabled).
+ *
+ * LOCK: unlocked, not needed (but destroyed)
+ */
+void
+en_destroy(struct en_softc *sc)
+{
+ if (sc->padbuf != NULL)
+ m_free(sc->padbuf);
- /* skip the end */
- if (fill || dma != cur) {
-#ifdef EN_DEBUG
- if (fill)
- printf("%s: rx%d: vci%d: skipping %d bytes of fill\n",
- sc->sc_dev.dv_xname, slot, vci, fill);
- else
- printf("%s: rx%d: vci%d: syncing chip from 0x%x to 0x%x [cur]\n",
- sc->sc_dev.dv_xname, slot, vci, dma, cur);
-#endif
- EN_WRAPADD(start, stop, cur, fill);
- EN_DRQADD(sc, WORD_IDX(start,cur), vci, MIDDMA_JK, 0, mlen,
- slot, MID_DMA_END);
- /* dma = cur; */ /* not necessary since we are done */
- }
-
- /*
- * done, remove stuff we don't want to pass up:
- * raw mode (boodi mode): pass everything up for later processing
- * aal5: remove RBD
- * aal0: remove RBD + cell header
- */
-
-done:
- if (m) {
- if (!raw) {
- cnt = MID_RBD_SIZE;
- if (!aal5) cnt += MID_CHDR_SIZE;
- m->m_len -= cnt; /* chop! */
- m->m_pkthdr.len -= cnt;
- m->m_data += cnt;
- }
- _IF_ENQUEUE(&sc->rxslot[slot].indma, m);
- }
- sc->rxslot[slot].cur = cur; /* update master copy of 'cur' */
+ /*
+ * Destroy the map zone before the tag (the fini function will
+ * destroy the DMA maps using the tag)
+ */
+ if (sc->map_zone != NULL)
+ uma_zdestroy(sc->map_zone);
-#ifdef EN_DEBUG
- printf("%s: rx%d: vci%d: DONE! cur now =0x%x\n",
- sc->sc_dev.dv_xname, slot, vci, cur);
-#endif
+ if (sc->txtag != NULL)
+ bus_dma_tag_destroy(sc->txtag);
+
+ (void)sysctl_ctx_free(&sc->sysctl_ctx);
- goto same_vci; /* get next packet in this slot */
+ mtx_destroy(&sc->en_mtx);
}
+/*********************************************************************/
+/*
+ * Debugging support
+ */
#ifdef EN_DDBHOOK
/*
@@ -3144,7 +2931,6 @@ done:
/*
* en_dump: dump the state
*/
-
#define END_SWSL 0x00000040 /* swsl state */
#define END_DRQ 0x00000020 /* drq state */
#define END_DTQ 0x00000010 /* dtq state */
@@ -3155,219 +2941,242 @@ done:
#define END_BITS "\20\7SWSL\6DRQ\5DTQ\4RX\3TX\2MREGS\1STATS"
-/* Do not staticize - meant for calling from DDB! */
-int en_dump(unit, level)
-
-int unit, level;
-
+static void
+en_dump_stats(const struct en_stats *s)
{
- struct en_softc *sc;
- int lcv, cnt, slot;
- u_int32_t ptr, reg;
-#ifdef __FreeBSD__
- devclass_t dc;
- int maxunit;
-
- dc = devclass_find("en");
- if (dc == NULL) {
- printf("en_dump: can't find devclass!\n");
- return 0;
- }
- maxunit = devclass_get_maxunit(dc);
- for (lcv = 0 ; lcv < maxunit ; lcv++) {
- sc = devclass_get_softc(dc, lcv);
-#else
- for (lcv = 0 ; lcv < en_cd.cd_ndevs ; lcv++) {
- sc = (struct en_softc *) en_cd.cd_devs[lcv];
-#endif
- if (sc == NULL) continue;
- if (unit != -1 && unit != lcv)
- continue;
-
- printf("dumping device %s at level 0x%b\n", sc->sc_dev.dv_xname, level,
- END_BITS);
-
- if (sc->dtq_us == 0) {
- printf("<hasn't been en_init'd yet>\n");
- continue;
- }
-
- if (level & END_STATS) {
- printf(" en_stats:\n");
- printf(" %d mfix (%d failed); %d/%d head/tail byte DMAs, %d flushes\n",
- sc->mfix, sc->mfixfail, sc->headbyte, sc->tailbyte, sc->tailflush);
- printf(" %d rx dma overflow interrupts\n", sc->dmaovr);
- printf(" %d times we ran out of TX space and stalled\n",
- sc->txoutspace);
- printf(" %d times we ran out of DTQs\n", sc->txdtqout);
- printf(" %d times we launched a packet\n", sc->launch);
- printf(" %d times we launched without on-board header\n", sc->lheader);
- printf(" %d times we launched without on-board tail\n", sc->ltail);
- printf(" %d times we pulled the hw service list\n", sc->hwpull);
- printf(" %d times we pushed a vci on the sw service list\n",
- sc->swadd);
- printf(" %d times RX pulled an mbuf from Q that wasn't ours\n",
- sc->rxqnotus);
- printf(" %d times RX pulled a good mbuf from Q\n", sc->rxqus);
- printf(" %d times we ran out of mbufs *and* DRQs\n", sc->rxoutboth);
- printf(" %d times we ran out of DRQs\n", sc->rxdrqout);
-
- printf(" %d trasmit packets dropped due to mbsize\n", sc->txmbovr);
- printf(" %d cells trashed due to turned off rxvc\n", sc->vtrash);
- printf(" %d cells trashed due to totally full buffer\n", sc->otrash);
- printf(" %d cells trashed due almost full buffer\n", sc->ttrash);
- printf(" %d rx mbuf allocation failures\n", sc->rxmbufout);
+ printf("en_stats:\n");
+ printf("\t%d/%d mfix (%d failed)\n", s->mfixaddr, s->mfixlen,
+ s->mfixfail);
+ printf("\t%d rx dma overflow interrupts\n", s->dmaovr);
+ printf("\t%d times out of TX space and stalled\n", s->txoutspace);
+ printf("\t%d times out of DTQs\n", s->txdtqout);
+ printf("\t%d times launched a packet\n", s->launch);
+ printf("\t%d times pulled the hw service list\n", s->hwpull);
+ printf("\t%d times pushed a vci on the sw service list\n", s->swadd);
+ printf("\t%d times RX pulled an mbuf from Q that wasn't ours\n",
+ s->rxqnotus);
+ printf("\t%d times RX pulled a good mbuf from Q\n", s->rxqus);
+ printf("\t%d times ran out of DRQs\n", s->rxdrqout);
+ printf("\t%d transmit packets dropped due to mbsize\n", s->txmbovr);
+ printf("\t%d cells trashed due to turned off rxvc\n", s->vtrash);
+ printf("\t%d cells trashed due to totally full buffer\n", s->otrash);
+ printf("\t%d cells trashed due almost full buffer\n", s->ttrash);
+ printf("\t%d rx mbuf allocation failures\n", s->rxmbufout);
+ printf("\t%d times out of tx maps\n", s->txnomap);
#ifdef NATM
-#if 0
- printf(" %d drops at natmintrq\n", natmintrq.ifq_drops);
-#endif
#ifdef NATM_STAT
- printf(" natmintr so_rcv: ok/drop cnt: %d/%d, ok/drop bytes: %d/%d\n",
- natm_sookcnt, natm_sodropcnt, natm_sookbytes, natm_sodropbytes);
+ printf("\tnatmintr so_rcv: ok/drop cnt: %d/%d, ok/drop bytes: %d/%d\n",
+ natm_sookcnt, natm_sodropcnt, natm_sookbytes, natm_sodropbytes);
#endif
#endif
- }
-
- if (level & END_MREGS) {
- printf("mregs:\n");
- printf("resid = 0x%x\n", EN_READ(sc, MID_RESID));
- printf("interrupt status = 0x%b\n",
- (int)EN_READ(sc, MID_INTSTAT), MID_INTBITS);
- printf("interrupt enable = 0x%b\n",
- (int)EN_READ(sc, MID_INTENA), MID_INTBITS);
- printf("mcsr = 0x%b\n", (int)EN_READ(sc, MID_MAST_CSR), MID_MCSRBITS);
- printf("serv_write = [chip=%u] [us=%u]\n", EN_READ(sc, MID_SERV_WRITE),
+}
+
+static void
+en_dump_mregs(struct en_softc *sc)
+{
+ u_int cnt;
+
+ printf("mregs:\n");
+ printf("resid = 0x%x\n", en_read(sc, MID_RESID));
+ printf("interrupt status = 0x%b\n",
+ (int)en_read(sc, MID_INTSTAT), MID_INTBITS);
+ printf("interrupt enable = 0x%b\n",
+ (int)en_read(sc, MID_INTENA), MID_INTBITS);
+ printf("mcsr = 0x%b\n", (int)en_read(sc, MID_MAST_CSR), MID_MCSRBITS);
+ printf("serv_write = [chip=%u] [us=%u]\n", en_read(sc, MID_SERV_WRITE),
MID_SL_A2REG(sc->hwslistp));
- printf("dma addr = 0x%x\n", EN_READ(sc, MID_DMA_ADDR));
- printf("DRQ: chip[rd=0x%x,wr=0x%x], sc[chip=0x%x,us=0x%x]\n",
- MID_DRQ_REG2A(EN_READ(sc, MID_DMA_RDRX)),
- MID_DRQ_REG2A(EN_READ(sc, MID_DMA_WRRX)), sc->drq_chip, sc->drq_us);
- printf("DTQ: chip[rd=0x%x,wr=0x%x], sc[chip=0x%x,us=0x%x]\n",
- MID_DTQ_REG2A(EN_READ(sc, MID_DMA_RDTX)),
- MID_DTQ_REG2A(EN_READ(sc, MID_DMA_WRTX)), sc->dtq_chip, sc->dtq_us);
-
- printf(" unusal txspeeds: ");
- for (cnt = 0 ; cnt < MID_N_VC ; cnt++)
- if (sc->txspeed[cnt])
- printf(" vci%d=0x%x", cnt, sc->txspeed[cnt]);
- printf("\n");
-
- printf(" rxvc slot mappings: ");
- for (cnt = 0 ; cnt < MID_N_VC ; cnt++)
- if (sc->rxvc2slot[cnt] != RX_NONE)
- printf(" %d->%d", cnt, sc->rxvc2slot[cnt]);
- printf("\n");
-
- }
-
- if (level & END_TX) {
- printf("tx:\n");
- for (slot = 0 ; slot < EN_NTX; slot++) {
- printf("tx%d: start/stop/cur=0x%x/0x%x/0x%x [%d] ", slot,
- sc->txslot[slot].start, sc->txslot[slot].stop, sc->txslot[slot].cur,
- (sc->txslot[slot].cur - sc->txslot[slot].start)/4);
- printf("mbsize=%d, bfree=%d\n", sc->txslot[slot].mbsize,
- sc->txslot[slot].bfree);
- printf("txhw: base_address=0x%x, size=%u, read=%u, descstart=%u\n",
- (u_int)MIDX_BASE(EN_READ(sc, MIDX_PLACE(slot))),
- MIDX_SZ(EN_READ(sc, MIDX_PLACE(slot))),
- EN_READ(sc, MIDX_READPTR(slot)), EN_READ(sc, MIDX_DESCSTART(slot)));
- }
- }
-
- if (level & END_RX) {
- printf(" recv slots:\n");
- for (slot = 0 ; slot < sc->en_nrx; slot++) {
- printf("rx%d: vci=%d: start/stop/cur=0x%x/0x%x/0x%x ", slot,
- sc->rxslot[slot].atm_vci, sc->rxslot[slot].start,
- sc->rxslot[slot].stop, sc->rxslot[slot].cur);
- printf("mode=0x%x, atm_flags=0x%x, oth_flags=0x%x\n",
- sc->rxslot[slot].mode, sc->rxslot[slot].atm_flags,
- sc->rxslot[slot].oth_flags);
- printf("RXHW: mode=0x%x, DST_RP=0x%x, WP_ST_CNT=0x%x\n",
- EN_READ(sc, MID_VC(sc->rxslot[slot].atm_vci)),
- EN_READ(sc, MID_DST_RP(sc->rxslot[slot].atm_vci)),
- EN_READ(sc, MID_WP_ST_CNT(sc->rxslot[slot].atm_vci)));
- }
- }
-
- if (level & END_DTQ) {
- printf(" dtq [need_dtqs=%d,dtq_free=%d]:\n",
- sc->need_dtqs, sc->dtq_free);
- ptr = sc->dtq_chip;
- while (ptr != sc->dtq_us) {
- reg = EN_READ(sc, ptr);
- printf("\t0x%x=[cnt=%d, chan=%d, end=%d, type=%d @ 0x%x]\n",
- sc->dtq[MID_DTQ_A2REG(ptr)], MID_DMA_CNT(reg), MID_DMA_TXCHAN(reg),
- (reg & MID_DMA_END) != 0, MID_DMA_TYPE(reg), EN_READ(sc, ptr+4));
- EN_WRAPADD(MID_DTQOFF, MID_DTQEND, ptr, 8);
- }
- }
-
- if (level & END_DRQ) {
- printf(" drq [need_drqs=%d,drq_free=%d]:\n",
- sc->need_drqs, sc->drq_free);
- ptr = sc->drq_chip;
- while (ptr != sc->drq_us) {
- reg = EN_READ(sc, ptr);
- printf("\t0x%x=[cnt=%d, chan=%d, end=%d, type=%d @ 0x%x]\n",
- sc->drq[MID_DRQ_A2REG(ptr)], MID_DMA_CNT(reg), MID_DMA_RXVCI(reg),
- (reg & MID_DMA_END) != 0, MID_DMA_TYPE(reg), EN_READ(sc, ptr+4));
- EN_WRAPADD(MID_DRQOFF, MID_DRQEND, ptr, 8);
- }
- }
-
- if (level & END_SWSL) {
- printf(" swslist [size=%d]: ", sc->swsl_size);
- for (cnt = sc->swsl_head ; cnt != sc->swsl_tail ;
- cnt = (cnt + 1) % MID_SL_N)
- printf("0x%x ", sc->swslist[cnt]);
- printf("\n");
- }
- }
- return(0);
+ printf("dma addr = 0x%x\n", en_read(sc, MID_DMA_ADDR));
+ printf("DRQ: chip[rd=0x%x,wr=0x%x], sc[chip=0x%x,us=0x%x]\n",
+ MID_DRQ_REG2A(en_read(sc, MID_DMA_RDRX)),
+ MID_DRQ_REG2A(en_read(sc, MID_DMA_WRRX)), sc->drq_chip, sc->drq_us);
+ printf("DTQ: chip[rd=0x%x,wr=0x%x], sc[chip=0x%x,us=0x%x]\n",
+ MID_DTQ_REG2A(en_read(sc, MID_DMA_RDTX)),
+ MID_DTQ_REG2A(en_read(sc, MID_DMA_WRTX)), sc->dtq_chip, sc->dtq_us);
+
+ printf(" unusal txspeeds:");
+ for (cnt = 0 ; cnt < MID_N_VC ; cnt++)
+ if (sc->txspeed[cnt])
+ printf(" vci%d=0x%x", cnt, sc->txspeed[cnt]);
+ printf("\n");
+
+ printf(" rxvc slot mappings:");
+ for (cnt = 0 ; cnt < MID_N_VC ; cnt++)
+ if (sc->rxvc2slot[cnt] != RX_NONE)
+ printf(" %d->%d", cnt, sc->rxvc2slot[cnt]);
+ printf("\n");
+}
+
+static void
+en_dump_tx(struct en_softc *sc)
+{
+ u_int slot;
+
+ printf("tx:\n");
+ for (slot = 0 ; slot < EN_NTX; slot++) {
+ printf("tx%d: start/stop/cur=0x%x/0x%x/0x%x [%d] ", slot,
+ sc->txslot[slot].start, sc->txslot[slot].stop,
+ sc->txslot[slot].cur,
+ (sc->txslot[slot].cur - sc->txslot[slot].start) / 4);
+ printf("mbsize=%d, bfree=%d\n", sc->txslot[slot].mbsize,
+ sc->txslot[slot].bfree);
+ printf("txhw: base_address=0x%x, size=%u, read=%u, "
+ "descstart=%u\n",
+ (u_int)MIDX_BASE(en_read(sc, MIDX_PLACE(slot))),
+ MIDX_SZ(en_read(sc, MIDX_PLACE(slot))),
+ en_read(sc, MIDX_READPTR(slot)),
+ en_read(sc, MIDX_DESCSTART(slot)));
+ }
+}
+
+static void
+en_dump_rx(struct en_softc *sc)
+{
+ u_int slot;
+
+ printf(" recv slots:\n");
+ for (slot = 0 ; slot < sc->en_nrx; slot++) {
+ printf("rx%d: vci=%d: start/stop/cur=0x%x/0x%x/0x%x ",
+ slot, sc->rxslot[slot].atm_vci,
+ sc->rxslot[slot].start, sc->rxslot[slot].stop,
+ sc->rxslot[slot].cur);
+ printf("mode=0x%x, atm_flags=0x%x, oth_flags=0x%x\n",
+ sc->rxslot[slot].mode, sc->rxslot[slot].atm_flags,
+ sc->rxslot[slot].oth_flags);
+ printf("RXHW: mode=0x%x, DST_RP=0x%x, WP_ST_CNT=0x%x\n",
+ en_read(sc, MID_VC(sc->rxslot[slot].atm_vci)),
+ en_read(sc, MID_DST_RP(sc->rxslot[slot].atm_vci)),
+ en_read(sc,
+ MID_WP_ST_CNT(sc->rxslot[slot].atm_vci)));
+ }
}
/*
- * en_dumpmem: dump the memory
+ * This is only correct for non-adaptec adapters
*/
+static void
+en_dump_dtqs(struct en_softc *sc)
+{
+ uint32_t ptr, reg;
+
+ printf(" dtq [need_dtqs=%d,dtq_free=%d]:\n", sc->need_dtqs,
+ sc->dtq_free);
+ ptr = sc->dtq_chip;
+ while (ptr != sc->dtq_us) {
+ reg = en_read(sc, ptr);
+ printf("\t0x%x=[%#x cnt=%d, chan=%d, end=%d, type=%d @ 0x%x]\n",
+ sc->dtq[MID_DTQ_A2REG(ptr)], reg, MID_DMA_CNT(reg),
+ MID_DMA_TXCHAN(reg), (reg & MID_DMA_END) != 0,
+ MID_DMA_TYPE(reg), en_read(sc, ptr + 4));
+ EN_WRAPADD(MID_DTQOFF, MID_DTQEND, ptr, 8);
+ }
+}
+
+static void
+en_dump_drqs(struct en_softc *sc)
+{
+ uint32_t ptr, reg;
+
+ printf(" drq [need_drqs=%d,drq_free=%d]:\n", sc->need_drqs,
+ sc->drq_free);
+ ptr = sc->drq_chip;
+ while (ptr != sc->drq_us) {
+ reg = en_read(sc, ptr);
+ printf("\t0x%x=[cnt=%d, chan=%d, end=%d, type=%d @ 0x%x]\n",
+ sc->drq[MID_DRQ_A2REG(ptr)], MID_DMA_CNT(reg),
+ MID_DMA_RXVCI(reg), (reg & MID_DMA_END) != 0,
+ MID_DMA_TYPE(reg), en_read(sc, ptr + 4));
+ EN_WRAPADD(MID_DRQOFF, MID_DRQEND, ptr, 8);
+ }
+}
/* Do not staticize - meant for calling from DDB! */
-int en_dumpmem(unit, addr, len)
+int
+en_dump(int unit, int level)
+{
+ struct en_softc *sc;
+ int lcv, cnt;
+ devclass_t dc;
+ int maxunit;
+
+ dc = devclass_find("en");
+ if (dc == NULL) {
+ printf("%s: can't find devclass!\n", __func__);
+ return (0);
+ }
+ maxunit = devclass_get_maxunit(dc);
+ for (lcv = 0 ; lcv < maxunit ; lcv++) {
+ sc = devclass_get_softc(dc, lcv);
+ if (sc == NULL)
+ continue;
+ if (unit != -1 && unit != lcv)
+ continue;
+
+ if_printf(&sc->enif, "dumping device at level 0x%b\n",
+ level, END_BITS);
+
+ if (sc->dtq_us == 0) {
+ printf("<hasn't been en_init'd yet>\n");
+ continue;
+ }
-int unit, addr, len;
+ if (level & END_STATS)
+ en_dump_stats(&sc->stats);
+ if (level & END_MREGS)
+ en_dump_mregs(sc);
+ if (level & END_TX)
+ en_dump_tx(sc);
+ if (level & END_RX)
+ en_dump_rx(sc);
+ if (level & END_DTQ)
+ en_dump_dtqs(sc);
+ if (level & END_DRQ)
+ en_dump_drqs(sc);
+
+ if (level & END_SWSL) {
+ printf(" swslist [size=%d]: ", sc->swsl_size);
+ for (cnt = sc->swsl_head ; cnt != sc->swsl_tail ;
+ cnt = (cnt + 1) % MID_SL_N)
+ printf("0x%x ", sc->swslist[cnt]);
+ printf("\n");
+ }
+ }
+ return (0);
+}
+/*
+ * en_dumpmem: dump the memory
+ *
+ * Do not staticize - meant for calling from DDB!
+ */
+int
+en_dumpmem(int unit, int addr, int len)
{
- struct en_softc *sc;
- u_int32_t reg;
-#ifdef __FreeBSD__
- devclass_t dc;
-
- dc = devclass_find("en");
- if (dc == NULL) {
- printf("en_dumpmem: can't find devclass!\n");
- return 0;
- }
- sc = devclass_get_softc(dc, unit);
-#else
- if (unit < 0 || unit > en_cd.cd_ndevs ||
- (sc = (struct en_softc *) en_cd.cd_devs[unit]) == NULL) {
- printf("invalid unit number: %d\n", unit);
- return(0);
- }
-#endif
+ struct en_softc *sc;
+ uint32_t reg;
+ devclass_t dc;
+
+ dc = devclass_find("en");
+ if (dc == NULL) {
+ printf("%s: can't find devclass\n", __func__);
+ return (0);
+ }
+ sc = devclass_get_softc(dc, unit);
+ if (sc == NULL) {
+ printf("%s: invalid unit number: %d\n", __func__, unit);
+ return (0);
+ }
- addr = addr & ~3;
- if (addr < MID_RAMOFF || addr + len*4 > MID_MAXOFF || len <= 0) {
- printf("invalid addr/len number: %d, %d\n", addr, len);
- return(0);
- }
- printf("dumping %d words starting at offset 0x%x\n", len, addr);
- while (len--) {
- reg = EN_READ(sc, addr);
- printf("mem[0x%x] = 0x%x\n", addr, reg);
- addr += 4;
- }
- return(0);
+ addr = addr & ~3;
+ if (addr < MID_RAMOFF || addr + len * 4 > MID_MAXOFF || len <= 0) {
+ printf("invalid addr/len number: %d, %d\n", addr, len);
+ return (0);
+ }
+ printf("dumping %d words starting at offset 0x%x\n", len, addr);
+ while (len--) {
+ reg = en_read(sc, addr);
+ printf("mem[0x%x] = 0x%x\n", addr, reg);
+ addr += 4;
+ }
+ return (0);
}
#endif
diff --git a/sys/dev/en/midwayreg.h b/sys/dev/en/midwayreg.h
index aefbb88..5c2fe1b 100644
--- a/sys/dev/en/midwayreg.h
+++ b/sys/dev/en/midwayreg.h
@@ -9,28 +9,11 @@
* $FreeBSD$
*/
-#if defined(sparc)
-/* XXX: gross. netbsd/sparc doesn't have machine/bus.h yet. */
-typedef void * bus_space_tag_t;
-typedef u_int32_t pci_chipset_tag_t;
-typedef caddr_t bus_space_handle_t;
-typedef u_int32_t bus_size_t;
-typedef caddr_t bus_addr_t;
-
-#define bus_space_read_4(t, h, o) ((void) t, \
- (*(volatile u_int32_t *)((h) + (o))))
-#define bus_space_write_4(t, h, o, v) \
- ((void) t, ((void)(*(volatile u_int32_t *)((h) + (o)) = (v))))
-
-#define vtophys(x) ((u_int32_t)(x)) /* sun4c dvma */
-
-#endif
-
-
#define MID_SZTOB(X) ((X) * 256 * 4) /* size to bytes */
#define MID_BTOSZ(X) ((X) / 256 / 4) /* bytes to "size" */
#define MID_N_VC 1024 /* # of VCs we can use */
+#define MID_VCI_BITS 10 /* number of bits */
#define MID_NTX_CH 8 /* 8 transmit channels (shared) */
#define MID_ATMDATASZ 48 /* need data in 48 byte blocks */
@@ -50,6 +33,7 @@ typedef caddr_t bus_addr_t;
*/
/* byte offsets from en_base of various items */
+#define MID_SUNIOFF 0x020000 /* SUNI offset */
#define MID_PHYOFF 0x030000 /* PHY offset */
#define MID_MIDOFF 0x040000 /* midway regs offset */
#define MID_RAMOFF 0x200000 /* RAM offset */
@@ -68,11 +52,11 @@ typedef caddr_t bus_addr_t;
* prom & phy: not defined here
*/
#define MID_ADPMACOFF 0xffc0 /* mac address offset (adaptec only) */
+#define MID_NSUNI 256 /* suni registers */
/*
* midway regs (byte offsets from en_base)
*/
-
#define MID_RESID 0x40000 /* write=reset reg, read=ID reg */
#define MID_VER(X) (((X) & 0xf0000000) >> 28) /* midway version # */
@@ -135,7 +119,7 @@ typedef caddr_t bus_addr_t;
#define MID_DMA_RDTX 0x4002c /* read ptr for DMA xmit queue (r/o) */
/* (i.e. current host->adaptor xfer) */
- /* xmit channel regs (1 per channel, MID_NTX_CH max channels) */
+/* xmit channel regs (1 per channel, MID_NTX_CH max channels) */
#define MIDX_PLACE(N) (0x40040+((N)*0x10)) /* xmit place */
@@ -143,12 +127,13 @@ typedef caddr_t bus_addr_t;
#define MIDX_LOC(X) ((X) & 0x7ff) /* location in obmem */
#define MIDX_SZ(X) ((X) >> 11) /* (size of block / 256) in int32_t's*/
#define MIDX_BASE(X) \
- (((MIDX_LOC(X) << MIDV_LOCTOPSHFT) * sizeof(u_int32_t)) + MID_RAMOFF)
-
- /* the following two regs are word offsets in the block */
-#define MIDX_READPTR(N) (0x40044+((N)*0x10)) /* xmit read pointer (r/o) */
-#define MIDX_DESCSTART(N) (0x40048+((N)*0x10)) /* seg currently in DMA (r/o) */
+ (((MIDX_LOC(X) << MIDV_LOCTOPSHFT) * sizeof(uint32_t)) + MID_RAMOFF)
+/* the following two regs are word offsets in the block */
+/* xmit read pointer (r/o) */
+#define MIDX_READPTR(N) (0x40044 + ((N) * 0x10))
+/* seg currently in DMA (r/o) */
+#define MIDX_DESCSTART(N) (0x40048 + ((N) * 0x10))
/*
* obmem items
@@ -157,8 +142,7 @@ typedef caddr_t bus_addr_t;
/*
* vci table in obmem (offset from MID_VCTOFF)
*/
-
-#define MID_VC(N) (MID_RAMOFF+((N)*0x10))
+#define MID_VC(N) (MID_RAMOFF + ((N) * 0x10))
#define MIDV_TRASH 0x00000000 /* ignore VC */
#define MIDV_AAL5 0x80000000 /* do AAL5 on it */
@@ -196,45 +180,43 @@ typedef caddr_t bus_addr_t;
/*
* dma recv q.
*/
-
-#define MID_DMA_END (1 << 5) /* for both tx and rx */
-#define MID_DMA_CNT(X) (((X) >> 16) & 0xffff)
-#define MID_DMA_TXCHAN(X) (((X) >> 6) & 0x7)
-#define MID_DMA_RXVCI(X) (((X) >> 6) & 0x3ff)
-#define MID_DMA_TYPE(X) ((X) & 0xf)
-
-#define MID_DRQ_N 512 /* # of descriptors */
+#define MID_DMA_END (1 << 5) /* for both tx and rx */
+#define MID_DMA_CNT(X) (((X) >> 16) & 0xffff)
+#define MID_DMA_TXCHAN(X) (((X) >> 6) & 0x7)
+#define MID_DMA_RXVCI(X) (((X) >> 6) & 0x3ff)
+#define MID_DMA_TYPE(X) ((X) & 0xf)
+
+#define MID_DRQ_N 512 /* # of descriptors */
+/* convert byte offset to reg value */
#define MID_DRQ_A2REG(N) (((N) - MID_DRQOFF) >> 3)
- /* convert byte offset to reg value */
-#define MID_DRQ_REG2A(N) (((N) << 3) + MID_DRQOFF) /* and back */
+/* and back */
+#define MID_DRQ_REG2A(N) (((N) << 3) + MID_DRQOFF)
/* note: format of word 1 of RXQ is different beween ENI and ADP cards */
-#define MID_MK_RXQ_ENI(CNT,VC,END,TYPE) \
- ( ((CNT) << 16)|((VC) << 6)|(END)|(TYPE) )
+#define MID_MK_RXQ_ENI(CNT, VC, END, TYPE) \
+ (((CNT) << 16) | ((VC) << 6) | (END) | (TYPE))
-#define MID_MK_RXQ_ADP(CNT,VC,END,JK) \
- ( ((CNT) << 12)|((VC) << 2)|((END) >> 4)|(((JK) != 0) ? 1 : 0))
+#define MID_MK_RXQ_ADP(CNT, VC, END, JK) \
+ (((CNT) << 12) | ((VC) << 2) | ((END) >> 4) | (((JK) != 0) ? 1 : 0))
/*
* dma xmit q.
*/
-
-#define MID_DTQ_N 512 /* # of descriptors */
+#define MID_DTQ_N 512 /* # of descriptors */
+/* convert byte offset to reg value */
#define MID_DTQ_A2REG(N) (((N) - MID_DTQOFF) >> 3)
- /* convert byte offset to reg value */
-#define MID_DTQ_REG2A(N) (((N) << 3) + MID_DTQOFF) /* and back */
-
+/* and back */
+#define MID_DTQ_REG2A(N) (((N) << 3) + MID_DTQOFF)
/* note: format of word 1 of TXQ is different beween ENI and ADP cards */
-#define MID_MK_TXQ_ENI(CNT,CHN,END,TYPE) \
- ( ((CNT) << 16)|((CHN) << 6)|(END)|(TYPE) )
+#define MID_MK_TXQ_ENI(CNT, CHN, END, TYPE) \
+ (((CNT) << 16) | ((CHN) << 6) | (END) | (TYPE))
-#define MID_MK_TXQ_ADP(CNT,CHN,END,JK) \
- ( ((CNT) << 12)|((CHN) << 2)|((END) >> 4)|(((JK) != 0) ? 1 : 0) )
+#define MID_MK_TXQ_ADP(CNT, CHN, END, JK) \
+ (((CNT) << 12) | ((CHN) << 2) | ((END) >> 4) | (((JK) != 0) ? 1 : 0))
/*
* dma types
*/
-
#define MIDDMA_JK 0x3 /* just kidding */
#define MIDDMA_BYTE 0x1 /* byte */
#define MIDDMA_2BYTE 0x2 /* 2 bytes */
@@ -249,25 +231,23 @@ typedef caddr_t bus_addr_t;
#define MIDDMA_16WMAYBE 0xe /* 16 words, maybe */
#define MIDDMA_MAYBE 0xc /* mask to detect WMAYBE dma code */
-#define MIDDMA_MAXBURST (16 * sizeof(u_int32_t)) /* largest burst */
+#define MIDDMA_MAXBURST (16 * sizeof(uint32_t)) /* largest burst */
/*
* service list
*/
-
-#define MID_SL_N 1024 /* max # entries on slist */
-#define MID_SL_A2REG(N) (((N) - MID_SLOFF) >> 2)
- /* convert byte offset to reg value */
-#define MID_SL_REG2A(N) (((N) << 2) + MID_SLOFF) /* and back */
+#define MID_SL_N 1024 /* max # entries on slist */
+/* convert byte offset to reg value */
+#define MID_SL_A2REG(N) (((N) - MID_SLOFF) >> 2)
+/* and back */
+#define MID_SL_REG2A(N) (((N) << 2) + MID_SLOFF)
/*
* data in the buffer area of obmem
*/
-
/*
- * recv buffer desc. (1 u_int32_t at start of buffer)
+ * recv buffer desc. (1 uint32_t at start of buffer)
*/
-
#define MID_RBD_SIZE 4 /* RBD size */
#define MID_CHDR_SIZE 4 /* on aal0, cell header size */
#define MID_RBD_ID(X) ((X) & 0xfe000000) /* get ID */
@@ -279,26 +259,24 @@ typedef caddr_t bus_addr_t;
#define MID_RBD_CNT(X) ((X) & 0x7ff) /* cell count */
/*
- * xmit buffer desc. (2 u_int32_t's at start of buffer)
- * (note we treat the PR & RATE as a single u_int8_t)
+ * xmit buffer desc. (2 uint32_t's at start of buffer)
+ * (note we treat the PR & RATE as a single uint8_t)
*/
-
#define MID_TBD_SIZE 8
#define MID_TBD_MK1(AAL,PR_RATE,CNT) \
- (MID_TBD_STDID|(AAL)|((PR_RATE) << 19)|(CNT))
+ (MID_TBD_STDID | (AAL) | ((PR_RATE) << 19) | (CNT))
#define MID_TBD_STDID 0xb0000000 /* standard ID */
#define MID_TBD_AAL5 0x08000000 /* AAL 5 */
#define MID_TBD_NOAAL5 0x00000000 /* not AAL 5 */
#define MID_TBD_MK2(VCI,PTI,CLP) \
- (((VCI) << 4)|((PTI) << 1)|(CLP))
+ (((VCI) << 4) | ((PTI) << 1) | (CLP))
/*
* aal5 pdu tail, last 2 words of last cell of AAL5 frame
* (word 2 is CRC .. handled by hw)
*/
-
-#define MID_PDU_SIZE 8
-#define MID_PDU_MK1(UU,CPI,LEN) \
- (((UU) << 24)|((CPI) << 16)|(LEN))
+#define MID_PDU_SIZE 8
+#define MID_PDU_MK1(UU, CPI, LEN) \
+ (((UU) << 24) | ((CPI) << 16) | (LEN))
#define MID_PDU_LEN(X) ((X) & 0xffff)
diff --git a/sys/dev/en/midwayvar.h b/sys/dev/en/midwayvar.h
index 72afdcb..8942fd9 100644
--- a/sys/dev/en/midwayvar.h
+++ b/sys/dev/en/midwayvar.h
@@ -38,8 +38,7 @@
* m i d w a y v a r . h
*
* we define the en_softc here so that bus specific modules can allocate
- * it as the first item in their softc. note that BSD-required
- * "struct device" is in the mid_softc!
+ * it as the first item in their softc.
*
* author: Chuck Cranor <chuck@ccrc.wustl.edu>
*/
@@ -47,7 +46,6 @@
/*
* params needed to determine softc size
*/
-
#ifndef EN_NTX
#define EN_NTX 8 /* number of tx bufs to use */
#endif
@@ -57,144 +55,176 @@
#ifndef EN_RXSZ
#define EN_RXSZ 32 /* recv buf size in KB */
#endif
-#define EN_MAXNRX ((2048-(EN_NTX*EN_TXSZ))/EN_RXSZ)
- /* largest possible NRX (depends on RAM size) */
+/* largest possible NRX (depends on RAM size) */
+#define EN_MAXNRX ((2048 - (EN_NTX * EN_TXSZ)) / EN_RXSZ)
-#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
-#define EN_INTR_TYPE int
-#define EN_INTR_RET(X) return(X)
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-#define EN_IOCTL_CMDT u_long
-#elif defined(__bsdi__)
-#define EN_IOCTL_CMDT int
+#ifndef EN_MAX_DMASEG
+#define EN_MAX_DMASEG 32
#endif
-#elif defined(__FreeBSD__)
+/* number of bytes to use in the first receive buffer. This must not be larger
+ * than MHLEN, should be a multiple of 64 and must be a multiple of 4. */
+#define EN_RX1BUF 128
+
+/*
+ * Structure to hold DMA maps. These are handle via a typestable uma zone.
+ */
+struct en_map {
+ uintptr_t flags; /* map flags */
+ struct en_map *rsvd2; /* see uma_zalloc(9) */
+ struct en_softc *sc; /* back pointer */
+ bus_dmamap_t map; /* the map */
+};
+#define ENMAP_LOADED 0x02
+#define ENMAP_ALLOC 0x01
-#define EN_INTR_TYPE void
-#define EN_INTR_RET(X) return
-#define EN_IOCTL_CMDT u_long
+#define EN_MAX_MAPS 400
-struct midway_device {
- char dv_xname[IFNAMSIZ];
+/*
+ * Statistics
+ */
+struct en_stats {
+ uint32_t vtrash; /* sw copy of counter */
+ uint32_t otrash; /* sw copy of counter */
+ uint32_t ttrash; /* # of RBD's with T bit set */
+ uint32_t mfixaddr; /* # of times we had to mfix an address */
+ uint32_t mfixlen; /* # of times we had to mfix a lenght*/
+ uint32_t mfixfail; /* # of times mfix failed */
+ uint32_t txmbovr; /* # of times we dropped due to mbsize */
+ uint32_t dmaovr; /* tx dma overflow count */
+ uint32_t txoutspace; /* out of space in xmit buffer */
+ uint32_t txdtqout; /* out of DTQs */
+ uint32_t launch; /* total # of launches */
+ uint32_t hwpull; /* # of pulls off hardware service list */
+ uint32_t swadd; /* # of pushes on sw service list */
+ uint32_t rxqnotus; /* # of times we pull from rx q, but fail */
+ uint32_t rxqus; /* # of good pulls from rx q */
+ uint32_t rxdrqout; /* # of times out of DRQs */
+ uint32_t rxmbufout; /* # of time out of mbufs */
+ uint32_t txnomap; /* out of DMA maps in TX */
};
-#define DV_IFNET 1
+/*
+ * Each of these structures describes one of the eight transmit channels
+ */
+struct en_txslot {
+ uint32_t mbsize; /* # mbuf bytes in use (max=TXHIWAT) */
+ uint32_t bfree; /* # free bytes in buffer */
+ uint32_t start; /* start of buffer area (byte offset) */
+ uint32_t stop; /* ends of buffer area (byte offset) */
+ uint32_t cur; /* next free area (byte offset) */
+ uint32_t nref; /* # of VCs using this channel */
+ struct ifqueue q; /* mbufs waiting for DMA now */
+ struct ifqueue indma; /* mbufs waiting for DMA now */
+};
-#endif
+/*
+ * Each of these structures is used for each of the receive buffers on the
+ * card.
+ */
+struct en_rxslot {
+ void *rxhand; /* recv. handle for direct delivery */
+ uint32_t mode; /* saved copy of mode info */
+ uint32_t start; /* begin of my buffer area */
+ uint32_t stop; /* end of my buffer area */
+ uint32_t cur; /* where I am at in the buffer */
+ uint16_t atm_vci; /* backpointer to VCI */
+ uint8_t atm_flags; /* copy of atm_flags from atm_ph */
+ uint8_t oth_flags; /* other flags */
+ uint32_t raw_threshold; /* for raw mode */
+ struct ifqueue q; /* mbufs waiting for dma now */
+ struct ifqueue indma; /* mbufs being dma'd now */
+};
/*
* softc
*/
-
struct en_softc {
- /* bsd glue */
- struct midway_device sc_dev; /* system device */
- struct ifnet enif; /* network ifnet handle */
-
- /* bus glue */
- bus_space_tag_t en_memt; /* for EN_READ/EN_WRITE */
- bus_space_handle_t en_base; /* base of en card */
- bus_size_t en_obmemsz; /* size of en card (bytes) */
- void (*en_busreset)(void *);
- /* bus specific reset function */
-
- /* serv list */
- u_int32_t hwslistp; /* hw pointer to service list (byte offset) */
- u_int16_t swslist[MID_SL_N]; /* software service list (see en_service()) */
- u_int16_t swsl_head, /* ends of swslist (index into swslist) */
- swsl_tail;
- u_int32_t swsl_size; /* # of items in swsl */
-
-
- /* xmit dma */
- u_int32_t dtq[MID_DTQ_N]; /* sw copy of dma q (see ENIDQ macros) */
- u_int32_t dtq_free; /* # of dtq's free */
- u_int32_t dtq_us; /* software copy of our pointer (byte offset) */
- u_int32_t dtq_chip; /* chip's pointer (byte offset) */
- u_int32_t need_dtqs; /* true if we ran out of DTQs */
-
- /* recv dma */
- u_int32_t drq[MID_DRQ_N]; /* sw copy of dma q (see ENIDQ macros) */
- u_int32_t drq_free; /* # of drq's free */
- u_int32_t drq_us; /* software copy of our pointer (byte offset) */
- u_int32_t drq_chip; /* chip's pointer (byte offset) */
- u_int32_t need_drqs; /* true if we ran out of DRQs */
-
- /* xmit buf ctrl. (per channel) */
- struct {
- u_int32_t mbsize; /* # mbuf bytes we are using (max=TXHIWAT) */
- u_int32_t bfree; /* # free bytes in buffer (not dma or xmit) */
- u_int32_t start, stop; /* ends of buffer area (byte offset) */
- u_int32_t cur; /* next free area (byte offset) */
- u_int32_t nref; /* # of VCs using this channel */
- struct ifqueue indma; /* mbufs being dma'd now */
- struct ifqueue q; /* mbufs waiting for dma now */
- } txslot[MID_NTX_CH];
-
- /* xmit vc ctrl. (per vc) */
- u_int8_t txspeed[MID_N_VC]; /* speed of tx on a VC */
- u_int8_t txvc2slot[MID_N_VC]; /* map VC to slot */
-
- /* recv vc ctrl. (per vc). maps VC number to recv slot */
- u_int16_t rxvc2slot[MID_N_VC];
- int en_nrx; /* # of active rx slots */
-
- /* recv buf ctrl. (per recv slot) */
- struct {
- void *rxhand; /* recv. handle if doing direct delivery */
- u_int32_t mode; /* saved copy of mode info */
- u_int32_t start, stop; /* ends of my buffer area */
- u_int32_t cur; /* where I am at */
- u_int16_t atm_vci; /* backpointer to VCI */
- u_int8_t atm_flags; /* copy of atm_flags from atm_ph */
- u_int8_t oth_flags; /* other flags */
- u_int32_t raw_threshold; /* for raw mode */
- struct ifqueue indma; /* mbufs being dma'd now */
- struct ifqueue q; /* mbufs waiting for dma now */
- } rxslot[EN_MAXNRX]; /* recv info */
-
- u_int8_t macaddr[6]; /* card unique mac address */
-
- /* stats */
- u_int32_t vtrash; /* sw copy of counter */
- u_int32_t otrash; /* sw copy of counter */
- u_int32_t ttrash; /* # of RBD's with T bit set */
- u_int32_t mfix; /* # of times we had to call mfix */
- u_int32_t mfixfail; /* # of times mfix failed */
- u_int32_t headbyte; /* # of times we used BYTE DMA at front */
- u_int32_t tailbyte; /* # of times we used BYTE DMA at end */
- u_int32_t tailflush; /* # of times we had to FLUSH out DMA bytes */
- u_int32_t txmbovr; /* # of times we dropped due to mbsize */
- u_int32_t dmaovr; /* tx dma overflow count */
- u_int32_t txoutspace; /* out of space in xmit buffer */
- u_int32_t txdtqout; /* out of DTQs */
- u_int32_t launch; /* total # of launches */
- u_int32_t lheader; /* # of launches without OB header */
- u_int32_t ltail; /* # of launches without OB tail */
- u_int32_t hwpull; /* # of pulls off hardware service list */
- u_int32_t swadd; /* # of pushes on sw service list */
- u_int32_t rxqnotus; /* # of times we pull from rx q, but fail */
- u_int32_t rxqus; /* # of good pulls from rx q */
- u_int32_t rxoutboth; /* # of times out of mbufs and DRQs */
- u_int32_t rxdrqout; /* # of times out of DRQs */
- u_int32_t rxmbufout; /* # of time out of mbufs */
-
- /* random stuff */
- u_int32_t ipl; /* sbus interrupt lvl (1 on pci?) */
- u_int8_t bestburstcode; /* code of best burst we can use */
- u_int8_t bestburstlen; /* length of best burst (bytes) */
- u_int8_t bestburstshift; /* (x >> shift) == (x / bestburstlen) */
- u_int8_t bestburstmask; /* bits to check if not multiple of burst */
- u_int8_t alburst; /* align dma bursts? */
- u_int8_t is_adaptec; /* adaptec version of midway? */
+ /* bsd glue */
+ struct ifnet enif; /* network ifnet handle */
+ device_t dev;
+
+ /* bus glue */
+ bus_space_tag_t en_memt; /* for EN_READ/EN_WRITE */
+ bus_space_handle_t en_base; /* base of en card */
+ bus_size_t en_obmemsz; /* size of en card (bytes) */
+ void (*en_busreset)(void *); /* bus specific reset function */
+ bus_dma_tag_t txtag; /* TX DMA tag */
+
+ /* serv list */
+ uint32_t hwslistp; /* hw pointer to service list (byte offset) */
+ uint16_t swslist[MID_SL_N]; /* software svc list (see en_service()) */
+ uint16_t swsl_head; /* ends of swslist (index into swslist) */
+ uint16_t swsl_tail;
+ uint32_t swsl_size; /* # of items in swsl */
+
+ /* xmit dma */
+ uint32_t dtq[MID_DTQ_N];/* sw copy of dma q (see EN_DQ_MK macros) */
+ uint32_t dtq_free; /* # of dtq's free */
+ uint32_t dtq_us; /* software copy of our pointer (byte offset) */
+ uint32_t dtq_chip; /* chip's pointer (byte offset) */
+ uint32_t need_dtqs; /* true if we ran out of DTQs */
+
+ /* recv dma */
+ uint32_t drq[MID_DRQ_N];/* sw copy of dma q (see ENIDQ macros) */
+ uint32_t drq_free; /* # of drq's free */
+ uint32_t drq_us; /* software copy of our pointer (byte offset) */
+ uint32_t drq_chip; /* chip's pointer (byte offset) */
+ uint32_t need_drqs; /* true if we ran out of DRQs */
+
+ /* xmit buf ctrl. (per channel) */
+ struct en_txslot txslot[MID_NTX_CH];
+
+ /* xmit vc ctrl. (per vc) */
+ uint8_t txspeed[MID_N_VC]; /* speed of tx on a VC */
+ uint8_t txvc2slot[MID_N_VC]; /* map VC to slot */
+
+ /* recv vc ctrl. (per vc). maps VC number to recv slot */
+ uint16_t rxvc2slot[MID_N_VC];
+ int en_nrx; /* # of active rx slots */
+
+ /* recv buf ctrl. (per recv slot) */
+ struct en_rxslot rxslot[EN_MAXNRX];
+
+ /* stats */
+ struct en_stats stats;
+
+ /* random stuff */
+ uint32_t ipl; /* sbus interrupt lvl (1 on pci?) */
+ uint8_t bestburstcode; /* code of best burst we can use */
+ uint8_t bestburstlen; /* length of best burst (bytes) */
+ uint8_t bestburstshift; /* (x >> shift) == (x / bestburstlen) */
+ uint8_t bestburstmask; /* bits to check if not multiple of burst */
+ uint8_t alburst; /* align dma bursts? */
+ uint8_t noalbursts; /* don't use unaligned > 4 byte bursts */
+ uint8_t is_adaptec; /* adaptec version of midway? */
+ struct mbuf *padbuf; /* buffer of zeros for TX padding */
+
+ /* mutex to protect this structure and the associated hardware */
+ struct mtx en_mtx;
+
+ /* sysctl support */
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+
+ /* memory zones */
+ uma_zone_t map_zone;
+
+ /* board info */
+ uint8_t macaddr[6];
+ uint32_t serial;
+
+#ifdef EN_DEBUG
+ /* debugging */
+ u_int debug;
+#endif
};
/*
* exported functions
*/
-
-void en_attach(struct en_softc *);
-EN_INTR_TYPE en_intr(void *);
+int en_attach(struct en_softc *);
+void en_destroy(struct en_softc *);
+void en_intr(void *);
void en_reset(struct en_softc *);
OpenPOWER on IntegriCloud