diff options
Diffstat (limited to 'sys/dev/en')
-rw-r--r-- | sys/dev/en/if_en_pci.c | 573 | ||||
-rw-r--r-- | sys/dev/en/midway.c | 5605 | ||||
-rw-r--r-- | sys/dev/en/midwayreg.h | 116 | ||||
-rw-r--r-- | sys/dev/en/midwayvar.h | 278 |
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 = ⊤ - - /* 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 *); |