summaryrefslogtreecommitdiffstats
path: root/sys/dev/hea
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>1998-09-15 08:23:17 +0000
committerphk <phk@FreeBSD.org>1998-09-15 08:23:17 +0000
commitc3dd1fa899d435ea4bf79897f646a93cb80c94ac (patch)
tree98dfbc96e3c6aa7ff1f322855f6484c4e609819d /sys/dev/hea
parent9ed6892f4808d56de443849229e151f8f7ad43b0 (diff)
downloadFreeBSD-src-c3dd1fa899d435ea4bf79897f646a93cb80c94ac.zip
FreeBSD-src-c3dd1fa899d435ea4bf79897f646a93cb80c94ac.tar.gz
Add new files for HARP3
Host ATM Research Platform (HARP), Network Computing Services, Inc. This software was developed with the support of the Defense Advanced Research Projects Agency (DARPA).
Diffstat (limited to 'sys/dev/hea')
-rw-r--r--sys/dev/hea/eni.c664
-rw-r--r--sys/dev/hea/eni.h499
-rw-r--r--sys/dev/hea/eni_buffer.c465
-rw-r--r--sys/dev/hea/eni_globals.c102
-rw-r--r--sys/dev/hea/eni_if.c270
-rw-r--r--sys/dev/hea/eni_init.c142
-rw-r--r--sys/dev/hea/eni_intr.c230
-rw-r--r--sys/dev/hea/eni_receive.c871
-rw-r--r--sys/dev/hea/eni_stats.h134
-rw-r--r--sys/dev/hea/eni_suni.h78
-rw-r--r--sys/dev/hea/eni_transmit.c823
-rw-r--r--sys/dev/hea/eni_var.h85
-rw-r--r--sys/dev/hea/eni_vcm.c283
13 files changed, 4646 insertions, 0 deletions
diff --git a/sys/dev/hea/eni.c b/sys/dev/hea/eni.c
new file mode 100644
index 0000000..088503a
--- /dev/null
+++ b/sys/dev/hea/eni.c
@@ -0,0 +1,664 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni.c,v 1.4 1998/06/29 19:39:10 jpt Exp $
+ *
+ */
+
+/*
+ * Efficient ENI adapter support
+ * -----------------------------
+ *
+ * Module supports PCI interface to ENI adapter
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni.c,v 1.4 1998/06/29 19:39:10 jpt Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+/*
+ * Typedef local functions
+ */
+static char *eni_pci_probe __P((pcici_t, pcidi_t));
+static void eni_pci_attach __P((pcici_t, int));
+static int eni_get_ack __P((Eni_unit *));
+static int eni_get_sebyte __P((Eni_unit *));
+static void eni_read_seeprom __P((Eni_unit *));
+#ifdef __FreeBSD__
+#if BSD < 199506
+static int eni_pci_shutdown __P((struct kern_devconf *, int));
+#else
+static void eni_pci_shutdown __P((int, void *));
+#endif
+static void eni_pci_reset __P((Eni_unit *));
+#endif /* __FreeBSD__ */
+
+/*
+ * Used by kernel to return number of claimed devices
+ */
+#ifdef __FreeBSD__
+static u_long eni_nunits;
+
+static struct pci_device eni_pci_device = {
+ ENI_DEV_NAME,
+ eni_pci_probe,
+ eni_pci_attach,
+ &eni_nunits,
+#if BSD < 199506
+ eni_pci_shutdown
+#else
+ NULL
+#endif
+};
+
+DATA_SET ( pcidevice_set, eni_pci_device );
+#endif /* __FreeBSD__ */
+
+/*
+ * Called by kernel with PCI device_id which was read from the PCI
+ * register set. If the identified vendor is Efficient, see if we
+ * recognize the particular device. If so, return an identifying string,
+ * if not, return null.
+ *
+ * Arguments:
+ * config_id PCI config token
+ * device_id contents of PCI device ID register
+ *
+ * Returns:
+ * string Identifying string if we will handle this device
+ * NULL unrecognized vendor/device
+ *
+ */
+static char *
+eni_pci_probe ( pcici_t config_id, pcidi_t device_id )
+{
+
+ if ( (device_id & 0xFFFF) == EFF_VENDOR_ID ) {
+ switch ( (device_id >> 16) ) {
+ case EFF_DEV_ID:
+ return ( "Efficient ENI ATM Adapter" );
+/* NOTREACHED */
+ break;
+ }
+ }
+
+ return ( NULL );
+}
+
+/*
+ * The ENI-155p adapter uses an ATMEL AT24C01 serial EEPROM to store
+ * configuration information. The SEEPROM is accessed via two wires,
+ * CLOCK and DATA, which are accessible via the PCI configuration
+ * registers. The following macros manipulate the lines to access the
+ * SEEPROM. See http://www.atmel.com/atmel/products/prod162.htm for
+ * a description of the AT24C01 part. Value to be read/written is
+ * part of the per unit structure.
+ */
+/*
+ * Write bits to SEEPROM
+ */
+#define WRITE_SEEPROM() ( \
+ { \
+ (void) pci_conf_write ( eup->eu_pcitag, SEEPROM, \
+ eup->eu_sevar ); \
+ DELAY(SEPROM_DELAY); \
+ } \
+)
+/*
+ * Stobe first the DATA, then the CLK lines high
+ */
+#define STROBE_HIGH() ( \
+ { \
+ eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM(); \
+ eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \
+ } \
+)
+/*
+ * Strobe first the CLK, then the DATA lines high
+ */
+#define INV_STROBE_HIGH() ( \
+ { \
+ eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \
+ eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM(); \
+ } \
+)
+/*
+ * Strobe first the CLK, then the DATA lines low - companion to
+ * STROBE_HIGH()
+ */
+#define STROBE_LOW() ( \
+ { \
+ eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \
+ eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM(); \
+ } \
+)
+/*
+ * Strobe first the DATA, then the CLK lines low - companion to
+ * INV_STROBE_HIGH()
+ */
+#define INV_STROBE_LOW() ( \
+ { \
+ eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM(); \
+ eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \
+ } \
+)
+/*
+ * Strobe the CLK line high, then low
+ */
+#define STROBE_CLK() ( \
+ { \
+ eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \
+ eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \
+ } \
+)
+
+/*
+ * Look for a positive ACK from the SEEPROM. Cycle begins by asserting
+ * the DATA line, then the CLK line. The DATA line is then read to
+ * retrieve the ACK status, and then the cycle is finished by deasserting
+ * the CLK line, and asserting the DATA line.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * 0/1 value of ACK
+ *
+ */
+static int
+eni_get_ack ( eup )
+ Eni_unit *eup;
+{
+ int ack;
+
+ STROBE_HIGH();
+ /*
+ * Read DATA line from SEPROM
+ */
+ eup->eu_sevar = pci_conf_read ( eup->eu_pcitag, SEEPROM );
+ DELAY ( SEPROM_DELAY );
+ ack = eup->eu_sevar & SEPROM_DATA;
+
+ eup->eu_sevar &= ~SEPROM_CLK;
+ WRITE_SEEPROM ();
+ eup->eu_sevar |= SEPROM_DATA;
+ WRITE_SEEPROM ();
+
+ return ( ack );
+}
+
+/*
+ * Read a byte from the SEEPROM. Data is read as 8 bits. There are two types
+ * of read operations. The first is a single byte read, the second is
+ * multiple sequential bytes read. Both cycles begin with a 'START' operation,
+ * followed by a memory address word. Following the memory address, the
+ * SEEPROM will send a data byte, followed by an ACK. If the host responds
+ * with a 'STOP' operation, then a single byte cycle is performed. If the
+ * host responds with an 'ACK', then the memory address is incremented, and
+ * the next sequential memory byte is serialized.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * val value of byte read from SEEPROM
+ *
+ */
+static int
+eni_get_sebyte( eup )
+ Eni_unit *eup;
+{
+ int i;
+ int data;
+ int rval;
+
+ /* Initial value */
+ rval = 0;
+ /* Read 8 bits */
+ for ( i = 0; i < 8; i++ ) {
+ /* Shift bits to left so the next bit goes to position 0 */
+ rval <<= 1;
+ /* Indicate we're ready to read bit */
+ STROBE_HIGH();
+ /*
+ * Read DATA line from SEPROM
+ */
+ data = pci_conf_read ( eup->eu_pcitag, SEEPROM );
+ DELAY ( SEPROM_DELAY );
+ /* (Possibly) mask bit into accumulating value */
+ if ( data & SEPROM_DATA )
+ rval |= 1; /* If DATA bit '1' */
+ /* Indicate we're done reading this bit */
+ STROBE_LOW();
+ }
+
+ /* Return acquired byte */
+ return ( rval );
+}
+
+/*
+ * The AT24C01 is a 1024 bit part organized as 128 words by 8 bits.
+ * We will read the entire contents into the per unit structure. Later,
+ * we'll retrieve the MAC address and serial number from the data read.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+eni_read_seeprom ( eup )
+ Eni_unit *eup;
+{
+ int addr;
+ int i, j;
+
+ /*
+ * Set initial state
+ */
+ eup->eu_sevar = SEPROM_DATA | SEPROM_CLK;
+ WRITE_SEEPROM ();
+
+ /* Loop for all bytes */
+ for ( i = 0; i < SEPROM_SIZE ; i++ ) {
+ /* Send START operation */
+ STROBE_HIGH();
+ INV_STROBE_LOW();
+
+ /*
+ * Send address. Addresses are sent as 7 bits plus
+ * last bit high.
+ */
+ addr = ((i) << 1) + 1;
+ /*
+ * Start with high order bit first working toward low
+ * order bit.
+ */
+ for ( j = 7; j >= 0; j-- ) {
+ /* Set current bit value */
+ eup->eu_sevar = ( addr >> j ) & 1 ?
+ eup->eu_sevar | SEPROM_DATA :
+ eup->eu_sevar & ~SEPROM_DATA;
+ WRITE_SEEPROM ();
+ /* Indicate we've sent it */
+ STROBE_CLK();
+ }
+ /*
+ * We expect a zero ACK after sending the address
+ */
+ if ( !eni_get_ack ( eup ) ) {
+ /* Address okay - read data byte */
+ eup->eu_seeprom[i] = eni_get_sebyte ( eup );
+ /* Grab but ignore the ACK op */
+ (void) eni_get_ack ( eup );
+ } else {
+ /* Address ACK was bad - can't retrieve data byte */
+ eup->eu_seeprom[i] = 0xff;
+ }
+ }
+
+ return;
+}
+
+/*
+ * The kernel has found a device which we are willing to support.
+ * We are now being called to do any necessary work to make the
+ * device initially usable. In our case, this means allocating
+ * structure memory, configuring registers, mapping device
+ * memory, setting pointers, registering with the core services,
+ * and doing the initial PDU processing configuration.
+ *
+ * Arguments:
+ * config_id PCI device token
+ * unit instance of the unit
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+eni_pci_attach ( pcici_t config_id, int unit )
+{
+ vm_offset_t va;
+ vm_offset_t pa;
+ Eni_unit *eup;
+ long val;
+
+ /*
+ * Just checking...
+ */
+ if ( unit >= ENI_MAX_UNITS ) {
+ log ( LOG_ERR, "%s%d: too many devices\n",
+ ENI_DEV_NAME, unit );
+ return;
+ }
+
+ /*
+ * Make sure this isn't a duplicate unit
+ */
+ if ( eni_units[unit] != NULL )
+ return;
+
+ /*
+ * Allocate a new unit structure
+ */
+ eup = (Eni_unit *) atm_dev_alloc ( sizeof(Eni_unit), sizeof(int), 0 );
+ if ( eup == NULL )
+ return;
+
+ /*
+ * Start initializing it
+ */
+ eup->eu_unit = unit;
+ eup->eu_mtu = ENI_IFF_MTU;
+ eup->eu_pcitag = config_id;
+ eup->eu_ioctl = eni_atm_ioctl;
+ eup->eu_instvcc = eni_instvcc;
+ eup->eu_openvcc = eni_openvcc;
+ eup->eu_closevcc = eni_closevcc;
+ eup->eu_output = eni_output;
+ eup->eu_vcc_pool = &eni_vcc_pool;
+ eup->eu_nif_pool = &eni_nif_pool;
+
+ /*
+ * Map in adapter RAM
+ */
+ if ( ( pci_map_mem ( config_id, PCI_MAP_REG_START, &va, &pa ) ) == 0 )
+ {
+ /*
+ * Nothing's happened yet that we need to undo.
+ */
+ return;
+ }
+ /*
+ * Map okay - retain address assigned
+ */
+ eup->eu_base = (Eni_mem)va;
+ eup->eu_ram = (Eni_mem)(eup->eu_base + RAM_OFFSET);
+
+ /*
+ * Map memory structures into adapter space
+ */
+ eup->eu_suni = (Eni_mem)(eup->eu_base + SUNI_OFFSET);
+ eup->eu_midway = (Eni_mem)(eup->eu_base + MIDWAY_OFFSET);
+ eup->eu_vcitbl = (VCI_Table *)(eup->eu_base + VCITBL_OFFSET);
+ eup->eu_rxdma = (Eni_mem)(eup->eu_base + RXQUEUE_OFFSET);
+ eup->eu_txdma = (Eni_mem)(eup->eu_base + TXQUEUE_OFFSET);
+ eup->eu_svclist = (Eni_mem)(eup->eu_base + SVCLIST_OFFSET);
+ eup->eu_servread = 0;
+
+ /*
+ * Reset the midway chip
+ */
+ eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
+
+ /*
+ * Size and test adapter memory. Initialize our adapter memory
+ * allocater.
+ */
+ if ( eni_init_memory ( eup ) < 0 ) {
+ /*
+ * Adapter memory test failed. Clean up and
+ * return.
+ */
+ /*
+ * FreeBSD doesn't support unmapping PCI memory (yet?).
+ */
+ return;
+ }
+
+ /*
+ * Read the contents of the SEEPROM
+ */
+ eni_read_seeprom ( eup );
+ /*
+ * Copy MAC address to PIF and config structures
+ */
+ KM_COPY ( (caddr_t)&eup->eu_seeprom[SEPROM_MAC_OFF],
+ (caddr_t)&eup->eu_pif.pif_macaddr, sizeof(struct mac_addr) );
+ eup->eu_config.ac_macaddr = eup->eu_pif.pif_macaddr;
+
+ /*
+ * Copy serial number into config space
+ */
+ eup->eu_config.ac_serial =
+ ntohl(*(u_long *)&eup->eu_seeprom[SEPROM_SN_OFF]);
+
+ /*
+ * Convert Endianess on DMA
+ */
+ val = pci_conf_read ( config_id, PCI_CONTROL_REG );
+ val |= ENDIAN_SWAP_DMA;
+ pci_conf_write ( config_id, PCI_CONTROL_REG, val );
+
+ /*
+ * Map interrupt in
+ */
+ if ( !pci_map_int ( config_id, eni_intr, (void *)eup, &net_imask ) )
+ {
+ /*
+ * Finish by unmapping memory, etc.
+ */
+ log ( LOG_ERR, "eni_pci_attach: Unable to map interrupt\n" );
+ /*
+ * Can't unmap PCI memory (yet).
+ */
+ return;
+ }
+
+ /*
+ * Setup some of the adapter configuration
+ */
+ /*
+ * Get MIDWAY ID
+ */
+ val = eup->eu_midway[MIDWAY_ID];
+ eup->eu_config.ac_vendor = VENDOR_ENI;
+ eup->eu_config.ac_vendapi = VENDAPI_ENI_1;
+ eup->eu_config.ac_device = DEV_ENI_155P;
+ eup->eu_config.ac_media = val & MEDIA_MASK ? MEDIA_UTP155 : MEDIA_OC3C;
+ eup->eu_pif.pif_pcr = ATM_PCR_OC3C;
+ eup->eu_config.ac_bustype = BUS_PCI;
+ eup->eu_config.ac_busslot = config_id->bus << 8 | config_id->slot;
+
+ /*
+ * Make a hw version number from the ID register values.
+ * Format: {Midway ID}.{Mother board ID}.{Daughter board ID}
+ */
+ sprintf ( eup->eu_config.ac_hard_vers, "%d/%d/%d",
+ (val >> ID_SHIFT) & ID_MASK,
+ (val >> MID_SHIFT) & MID_MASK,
+ (val >> DID_SHIFT) & DID_MASK );
+
+ /*
+ * There is no software version number
+ */
+ sprintf ( eup->eu_config.ac_firm_vers, "\0" );
+
+ /*
+ * Save device ram info for user-level programs
+ * NOTE: This really points to start of EEPROM
+ * and includes all the device registers in the
+ * lower 2 Megabytes.
+ */
+ eup->eu_config.ac_ram = (long)eup->eu_base;
+ eup->eu_config.ac_ramsize = eup->eu_ramsize + ENI_REG_SIZE;
+
+ /*
+ * Setup max VPI/VCI values
+ */
+ eup->eu_pif.pif_maxvpi = ENI_MAX_VPI;
+ eup->eu_pif.pif_maxvci = ENI_MAX_VCI;
+
+ /*
+ * Register this interface with ATM core services
+ */
+ if ( atm_physif_register
+ ( (Cmn_unit *)eup, ENI_DEV_NAME, eni_services ) != 0 )
+ {
+ /*
+ * Registration failed - back everything out
+ */
+ /*
+ * Can't unmap PCI memory (yet).
+ */
+ /*
+ * Unmap PCI interrupt
+ */
+ (void) pci_unmap_int ( config_id );
+ log ( LOG_ERR,
+ "eni_pci_attach: atm_physif_register failed\n" );
+ return;
+ }
+
+ eni_units[unit] = eup;
+
+#if BSD >= 199506
+ /*
+ * Add hook to out shutdown function
+ */
+ at_shutdown ( (bootlist_fn)eni_pci_shutdown, eup, SHUTDOWN_POST_SYNC );
+#endif
+
+ /*
+ * Initialize driver processing
+ */
+ if ( eni_init ( eup ) ) {
+ log ( LOG_ERR, "eni_pci_attach: Failed eni_init()\n" );
+ /*
+ * Can't unmap PCI memory (yet).
+ */
+ /*
+ * Unmap PCI interrupt
+ */
+ (void) pci_unmap_int ( config_id );
+ /*
+ * Fall through to return
+ */
+ }
+
+ return;
+
+}
+
+/*
+ * Device reset routine
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+eni_pci_reset ( eup )
+ Eni_unit *eup;
+{
+
+ /*
+ * We should really close down any open VCI's and
+ * release all memory (TX and RX) buffers. For now,
+ * we assume we're shutting the card down for good.
+ */
+
+ /*
+ * Issue RESET command to Midway chip
+ */
+ eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
+
+ /*
+ * Delay to allow everything to terminate
+ */
+ DELAY ( MIDWAY_DELAY );
+
+ return;
+}
+
+#ifdef __FreeBSD__
+#if BSD < 199506
+/*
+ * Device shutdown routine
+ *
+ * Arguments:
+ * kdc pointer to device's configuration table
+ * force forced shutdown flag
+ *
+ * Returns:
+ * none
+ *
+ */
+static int
+eni_pci_shutdown ( kdc, force )
+ struct kern_devconf *kdc;
+ int force;
+{
+ Eni_unit *eup = NULL;
+
+ if ( kdc->kdc_unit < eni_nunits ) {
+
+ eup = eni_units[kdc->kdc_unit];
+ if ( eup != NULL ) {
+ /* Do device reset */
+ eni_pci_reset ( eup );
+ }
+ }
+
+ (void) dev_detach ( kdc );
+ return ( 0 );
+}
+#else
+/*
+ * Device shutdown routine
+ *
+ * Arguments:
+ * howto type of shutdown
+ * eup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+eni_pci_shutdown ( howto, eup )
+ int howto;
+ void *eup;
+{
+
+ /* Do device reset */
+ eni_pci_reset ( eup );
+
+}
+#endif /* BSD < 199506 */
+#endif /* __FreeBSD__ */
diff --git a/sys/dev/hea/eni.h b/sys/dev/hea/eni.h
new file mode 100644
index 0000000..ff4a81b
--- /dev/null
+++ b/sys/dev/hea/eni.h
@@ -0,0 +1,499 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni.h,v 1.7 1998/06/29 19:45:14 jpt Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ *
+ * Protocol and implementation definitions
+ *
+ */
+
+#ifndef _ENI_ENI_H
+#define _ENI_ENI_H
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+/*
+ * Physical device name - used to configure HARP devices
+ */
+#ifndef ENI_DEV_NAME
+#define ENI_DEV_NAME "hea" /* HARP Efficient ATM */
+#endif
+
+#define ENI_MAX_UNITS 4
+
+#define ENI_IFF_MTU 9188
+#define ENI_MAX_VCI 1023 /* 0 - 1023 */
+#define ENI_MAX_VPI 0
+
+#define ENI_IFQ_MAXLEN 1000 /* rx/tx queue lengths */
+
+#ifdef BSD
+/*
+ * Size of small and large receive buffers
+ */
+#define ENI_SMALL_BSIZE 64
+#define ENI_LARGE_BSIZE MCLBYTES
+#endif /* BSD */
+
+/*
+ * ENI memory map offsets IN WORDS, not bytes
+ *
+ * The Efficient Adapter implements a 4 MB address space. The lower
+ * 2 MB are used by bootprom (E)EPROM and by chipset registers such
+ * as the MIDWAY and SUNI chips. The (upto) upper 2 MB is used for
+ * RAM. Of the RAM, the lower 28 KB is used for fixed tables - the
+ * VCI table, the RX and TX DMA queues, and the Service List queue.
+ * Memory above the 28 KB range is available for RX and TX buffers.
+ *
+ * NOTE: Access to anything other then the (E)EPROM MUST be as a 32 bit
+ * access. Also note that Efficient uses both byte addresses and word
+ * addresses when describing offsets. BE CAREFUL or you'll get confused!
+ */
+/*
+ * Size of memory space reserved for registers and expansion (e)eprom.
+ */
+#define ENI_REG_SIZE 0x200000 /* Two megabytes */
+
+#define SUNI_OFFSET 0x008000 /* SUNI chip registers */
+#define MIDWAY_OFFSET 0x010000 /* MIDWAY chip registers */
+#define RAM_OFFSET 0x080000 /* Adapter RAM */
+#define VCITBL_OFFSET 0x080000 /* VCI Table offset */
+#define RXQUEUE_OFFSET 0x081000 /* RX DMA Queue offset */
+#define TXQUEUE_OFFSET 0x081400 /* TX DMA Queue offset */
+#define SVCLIST_OFFSET 0x081800 /* SVC List Queue offset */
+
+#define SEGBUF_BASE 0x007000 /* Base from start of RAM */
+
+#define DMA_LIST_SIZE 512 /* 1024 words / 2 words per entry */
+#define SVC_LIST_SIZE 1024 /* 1024 words / 1 word per entry */
+
+/*
+ * Values for testing size of RAM on adapter
+ *
+ * Efficient has (at least) two different memory sizes available. One
+ * is a client card which has either 128 KB or 512 KB RAM, the other
+ * is a server card which has 2 MB RAM. The driver will size and test
+ * the memory to correctly determine what's available.
+ */
+#define MAX_ENI_MEM 0x200000 /* 2 MB - max. mem supported */
+#define TEST_STEP 0x000400 /* Look at 1 KB steps */
+#define TEST_PAT 0xA5A5A5A5 /* Test pattern */
+
+/*
+ * Values for memory allocator
+ */
+#define ENI_BUF_PGSZ 1024 /* Allocation unit of buffers */
+#define ENI_BUF_NBIT 8 /* Number of bits to get from */
+ /* min buffer (1KB) to max (128KB) */
+
+/*
+ * Values for allocating TX buffers
+ */
+#define MAX_CLIENT_RAM 512 /* Most RAM a client card will have */
+#define TX_SMALL_BSIZE 32 /* Small buffer - 32KB */
+#define TX_LARGE_BSIZE 128 /* Large buffer - 128KB */
+
+/*
+ * Values for allocating RX buffers
+ */
+#define RX_SIG_BSIZE 4 /* Signalling buffer - 4KB */
+#define RX_CLIENT_BSIZE 16 /* Client buffer - 16KB */
+#define RX_SERVER_BSIZE 32 /* Server buffer - 32KB */
+
+/*
+ * Adapter bases all addresses off of some power from 1KB. Thus, it
+ * only needs to store the most sigificant bits and can drop the lower
+ * 10 bits.
+ */
+#define ENI_LOC_PREDIV 10 /* Bits location is shifted */
+ /* Location is prescaled by 1KB */
+ /* before use in various places */
+
+#define MIDWAY_DELAY 10 /* Time to wait for Midway finish */
+
+/*
+ * Define the MIDWAY register offsets and any interesting bits within
+ * the register
+ */
+#define MIDWAY_ID 0x00 /* ID/Reset register */
+ #define MIDWAY_RESET 0 /* iWrite of any value */
+ #define ID_SHIFT 27 /* Midway ID version */
+ #define ID_MASK 0x1F /* ID mask */
+ #define MID_SHIFT 7 /* Mother board ID */
+ #define MID_MASK 0x7 /* MID mask */
+ #define DID_SHIFT 0 /* Daughter board ID */
+ #define DID_MASK 0x1F /* DID mask */
+ /*
+ * Efficient defines the following IDs for their adapters:
+ * 0x420/0x620 - SONET MMF, client memory size
+ * 0x430/0x630 - SONET MMF, server memory size
+ * 0x424/0x624 - UTP-5, client memory size
+ * 0x434/0x634 - UTP-5, server memory size
+ */
+ #define MEDIA_MASK 0x04 /* Mask off UTP-5/MMF media */
+
+#define MIDWAY_ISA 0x01 /* Interrupt Status Ack. */
+ /* Reading this register */
+ /* also acknowledges the */
+ /* posted interrupt(s) */
+
+#define MIDWAY_IS 0x02 /* Interrupt Status */
+ /* Reading this register */
+ /* does NOT acknowledge the */
+ /* posted interrupt(s) */
+ /* Interrupt names */
+ #define ENI_INT_STAT 0x00000001
+ #define ENI_INT_SUNI 0x00000002
+ #define ENI_INT_SERVICE 0x00000004
+ #define ENI_INT_TX_DMA 0x00000008
+ #define ENI_INT_RX_DMA 0x00000010
+ #define ENI_INT_DMA_ERR 0x00000020
+ #define ENI_INT_DMA_LERR 0x00000040
+ #define ENI_INT_IDEN 0x00000080
+ #define ENI_INT_DMA_OVFL 0x00000100
+ #define ENI_INT_TX_MASK 0x0001FE00
+
+#define MIDWAY_IE 0x03 /* Interrupt Enable register */
+ /* Interrupt enable bits are the same as the Interrupt names */
+
+#define MIDWAY_MASTER 0x04 /* Master Control */
+ /* Master control bits */
+ #define ENI_M_WAIT500 0x00000001 /* Disable interrupts .5 ms */
+ #define ENI_M_WAIT1 0x00000002 /* Disable interrupts 1 ms */
+ #define ENI_M_RXENABLE 0x00000004 /* Enable RX engine */
+ #define ENI_M_TXENABLE 0x00000008 /* Enable TX engine */
+ #define ENI_M_DMAENABLE 0x00000010 /* Enable DMA */
+ #define ENI_M_TXLOCK 0x00000020 /* 0: Streaming, 1: Lock */
+ #define ENI_M_INTSEL 0x000001C0 /* Int Select mask */
+ #define ENI_ISEL_SHIFT 6 /* Bits to shift ISEL value */
+
+#define MIDWAY_STAT 0x05 /* Statistics register */
+
+#define MIDWAY_SVCWR 0x06 /* Svc List write pointer */
+ #define SVC_SIZE_MASK 0x3FF /* Valid bits in svc pointer */
+
+#define MIDWAY_DMAADDR 0x07 /* Current virtual DMA addr */
+
+#define MIDWAY_RX_WR 0x08 /* Write ptr to RX DMA queue */
+
+#define MIDWAY_RX_RD 0x09 /* Read ptr to RX DMA queue */
+
+#define MIDWAY_TX_WR 0x0A /* Write ptr to TX DMA queue */
+
+#define MIDWAY_TX_RD 0x0B /* Read ptr to TX DMA queue */
+
+/*
+ * Registers 0x0C - 0x0F are unused
+ */
+
+/*
+ * MIDWAY supports 8 transmit channels. Each channel has 3 registers
+ * to control operation. Each new channel starts on N * 4 set. Thus,
+ * channel 0 uses register 0x10 - 0x13, channel 1 uses 0x14 - 0x17, etc.
+ * Register 0x13 + N * 4 is unused.
+ */
+
+#define MIDWAY_TXPLACE 0x10 /* Channel N TX location */
+ #define TXSIZE_SHIFT 11 /* Bits to shift size by */
+ #define TX_PLACE_MASK 0x7FF /* Valid bits in TXPLACE */
+
+#define MIDWAY_RDPTR 0x11 /* Channel N Read ptr */
+
+#define MIDWAY_DESCR 0x12 /* Channel N Descr ptr */
+
+/*
+ * Register 0x30 on up are unused
+ */
+
+/*
+ * Part of PCI configuration registers but not defined in <pci/pcireg.h>
+ */
+#define PCI_CONTROL_REG 0x60
+#define ENDIAN_SWAP_DMA 0x80 /* Enable endian swaps on DMA */
+
+/*
+ * The Efficient adapter references adapter RAM through the use of
+ * location and size values. Eight sizes are defined. When allocating
+ * buffers, there size must be rounded up to the next size which will
+ * hold the requested size. Buffers are allocated on 'SIZE' boundaries.
+ * See eni_buffer.c for more info.
+ */
+
+/*
+ * Buffer SIZE definitions - in words, so from 1 KB to 128 KB
+ */
+#define SIZE_256 0x00
+#define SIZE_512 0x01
+#define SIZE_1K 0x02
+#define SIZE_2K 0x03
+#define SIZE_4K 0x04
+#define SIZE_8K 0x05
+#define SIZE_16K 0x06
+#define SIZE_32K 0x07
+
+/*
+ * Define values for DMA type - DMA descriptors include a type field and a
+ * count field except in the special case of JK (just-kidding). With type JK,
+ * the count field should be set to the address which will be loaded
+ * into the pointer, ie. where the pointer should next point to, since
+ * JK doesn't have a "size" associated with it. JK DMA is used to skip
+ * over descriptor words, and to strip off padding of AAL5 PDUs. The
+ * DMA_nWORDM types will do a n word DMA burst, but the count field
+ * does not have to equal n. Any difference results in garbage filling
+ * the remaining words of the DMA. These types could be used where a
+ * particular burst size yields better DMA performance.
+ */
+#define DMA_WORD 0x00
+#define DMA_BYTE 0x01
+#define DMA_HWORD 0x02
+#define DMA_JK 0x03
+#define DMA_4WORD 0x04
+#define DMA_8WORD 0x05
+#define DMA_16WORD 0x06
+#define DMA_2WORD 0x07
+#define DMA_4WORDM 0x0C
+#define DMA_8WORDM 0x0D
+#define DMA_16WORDM 0x0E
+#define DMA_2WORDM 0x0F
+
+/*
+ * Define the size of the local DMA list we'll build before
+ * giving up on the PDU.
+ */
+#define TEMP_DMA_SIZE 120 /* Enough for 58/59 buffers */
+
+#define DMA_COUNT_SHIFT 16 /* Number of bits to shift count */
+ /* in DMA descriptor word */
+#define DMA_VCC_SHIFT 6 /* Number of bits to shift RX VCC or */
+ /* TX channel in DMA descriptor word */
+#define DMA_END_BIT 0x20 /* Signal end of DMA list */
+
+/*
+ * Defines for VCI table
+ *
+ * The VCI table is a 1K by 4 word table allowing up to 1024 (0-1023)
+ * VCIs. Entries into the table use the VCI number as the index.
+ */
+struct vci_table {
+ u_long vci_control; /* Control word */
+ u_long vci_descr; /* Descr/ReadPtr */
+ u_long vci_write; /* WritePtr/State/Cell count */
+ u_long vci_crc; /* ongoing CRC calculation */
+};
+typedef volatile struct vci_table VCI_Table;
+
+#define VCI_MODE_SHIFT 30 /* Shift to get MODE field */
+#define VCI_MODE_MASK 0x3FFFFFFF /* Bits to strip MODE off */
+#define VCI_PTI_SHIFT 29 /* Shift to get PTI mode field */
+#define VCI_LOC_SHIFT 18 /* Shift to get location field */
+#define VCI_LOC_MASK 0x7FF /* Valid bits in location field */
+#define VCI_SIZE_SHIFT 15 /* Shift to get size field */
+#define VCI_SIZE_MASK 7 /* Valid bits in size field */
+#define VCI_IN_SERVICE 1 /* Mask for IN_SERVICE field */
+
+/*
+ * Defines for VC mode
+ */
+#define VCI_MODE_TRASH 0x00 /* Trash all cells for this VC */
+#define VCI_MODE_AAL0 0x01 /* Reassemble as AAL_0 PDU */
+#define VCI_MODE_AAL5 0x02 /* Reassemble as AAL_5 PDU */
+/*
+ * Defines for handling cells with PTI(2) set to 1.
+ */
+#define PTI_MODE_TRASH 0x00 /* Trash cell */
+#define PTI_MODE_PRESV 0x01 /* Send cell to OAM channel */
+/*
+ * Current state of VC
+ */
+#define VCI_STATE_IDLE 0x00 /* VC is idle */
+#define VCI_STATE_REASM 0x01 /* VC is reassembling PDU */
+#define VCI_STATE_TRASH 0x03 /* VC is trashing cells */
+
+/*
+ * RX Descriptor word values
+ */
+#define DESCR_TRASH_BIT 0x1000 /* VCI was trashing cells */
+#define DESCR_CRC_ERR 0x0800 /* PDU has CRC error */
+#define DESCR_CELL_COUNT 0x07FF /* Mask to get cell count */
+/*
+ * TX Descriptor word values
+ */
+#define TX_IDEN_SHIFT 28 /* Unique identifier location */
+#define TX_MODE_SHIFT 27 /* AAL5 or AAL0 */
+#define TX_VCI_SHIFT 4 /* Bits to shift VCI value */
+
+/*
+ * When setting up descriptor words (at head of segmentation queues), there
+ * is a unique identifier used to help detect sync problems.
+ */
+#define MIDWAY_UNQ_ID 0x0B
+
+/*
+ * Defines for cell sizes
+ */
+#define BYTES_PER_CELL 48 /* Number of data bytes per cell */
+#define WORDS_PER_CELL 12 /* Number of data words per cell */
+
+/*
+ * Access to Serial EEPROM [as opposed to expansion (E)PROM].
+ *
+ * This is a ATMEL AT24C01 serial EEPROM part.
+ * See http://www.atmel.com/atmel/products/prod162.htm for timimg diagrams
+ * for START/STOP/ACK/READ cycles.
+ */
+#define SEEPROM PCI_CONTROL_REG /* Serial EEPROM is accessed thru */
+ /* PCI control register */
+#define SEPROM_DATA 0x02 /* SEEPROM DATA line */
+#define SEPROM_CLK 0x01 /* SEEPROM CLK line */
+#define SEPROM_SIZE 128 /* Size of Serial EEPROM */
+#define SEPROM_MAC_OFF 64 /* Offset to MAC address */
+#define SEPROM_SN_OFF 112 /* Offset to serial number */
+#define SEPROM_DELAY 10 /* Delay when strobing CLK/DATA lines */
+
+/*
+ * Host protocol control blocks
+ *
+ */
+
+/*
+ * Device VCC Entry
+ *
+ * Contains the common and ENI-specific information for each VCC
+ * which is opened through a ENI device.
+ */
+struct eni_vcc {
+ struct cmn_vcc ev_cmn; /* Common VCC stuff */
+ caddr_t ev_rxbuf; /* Receive buffer */
+ u_long ev_rxpos; /* Adapter buffer read pointer */
+};
+typedef struct eni_vcc Eni_vcc;
+
+#define ev_next ev_cmn.cv_next
+#define ev_toku ev_cmn.cv_toku
+#define ev_upper ev_cmn.cv_upper
+#define ev_connvc ev_cmn.cv_connvc
+#define ev_state ev_cmn.cv_state
+
+typedef volatile unsigned long * Eni_mem;
+
+/*
+ * Define the ID's we'll look for in the PCI config
+ * register when deciding if we'll support this device.
+ * The DEV_ID will need to be turned into an array of
+ * ID's in order to support multiple adapters with
+ * the same driver.
+ */
+#define EFF_VENDOR_ID 0x111A
+#define EFF_DEV_ID 0x0002
+
+/*
+ * Memory allocator defines and buffer descriptors
+ */
+#define MEM_FREE 0
+#define MEM_INUSE 1
+
+typedef struct mbd Mbd;
+struct mbd {
+ Mbd *prev;
+ Mbd *next;
+ caddr_t base; /* Adapter base address */
+ int size; /* Size of buffer */
+ int state; /* INUSE or FREE */
+};
+
+/*
+ * We use a hack to allocate a smaller RX buffer for signalling
+ * channels as they tend to have small MTU lengths.
+ */
+#define UNI_SIG_VCI 5
+
+/*
+ * Device Unit Structure
+ *
+ * Contains all the information for a single device (adapter).
+ */
+struct eni_unit {
+ Cmn_unit eu_cmn; /* Common unit stuff */
+ pcici_t eu_pcitag; /* PCI tag */
+ Eni_mem eu_base; /* Adapter memory base */
+ Eni_mem eu_ram; /* Adapter RAM */
+ u_long eu_ramsize;
+
+ Eni_mem eu_suni; /* SUNI registers */
+
+ Eni_mem eu_midway; /* MIDWAY registers */
+
+ VCI_Table *eu_vcitbl; /* VCI Table */
+ Eni_mem eu_rxdma; /* Receive DMA queue */
+ Eni_mem eu_txdma; /* Transmit DMA queue */
+ Eni_mem eu_svclist; /* Service list */
+ u_long eu_servread; /* Read pointer into Service list */
+
+ caddr_t eu_txbuf; /* One large TX buff for everything */
+ u_long eu_txsize; /* Size of TX buffer */
+ u_long eu_txpos; /* Current word being stored in RAM */
+ u_long eu_txfirst; /* First word of unack'ed data */
+
+ u_long eu_trash;
+ u_long eu_ovfl;
+
+ struct ifqueue eu_txqueue;
+ u_long eu_txdmawr;
+ struct ifqueue eu_rxqueue;
+ u_long eu_rxdmawr; /* DMA list write pointer */
+
+ u_char eu_seeprom[SEPROM_SIZE]; /* Serial EEPROM contents */
+ u_int eu_sevar; /* Unique (per unit) seeprom var. */
+
+ Mbd *eu_memmap; /* Adapter RAM memory allocator map */
+ int eu_memclicks[ENI_BUF_NBIT];/* Count of INUSE buffers */
+
+ Eni_stats eu_stats; /* Statistics */
+
+};
+typedef struct eni_unit Eni_unit;
+
+#define eu_pif eu_cmn.cu_pif
+#define eu_unit eu_cmn.cu_unit
+#define eu_flags eu_cmn.cu_flags
+#define eu_mtu eu_cmn.cu_mtu
+#define eu_open_vcc eu_cmn.cu_open_vcc
+#define eu_vcc eu_cmn.cu_vcc
+#define eu_vcc_pool eu_cmn.cu_vcc_pool
+#define eu_nif_pool eu_cmn.cu_nif_pool
+#define eu_ioctl eu_cmn.cu_ioctl
+#define eu_instvcc eu_cmn.cu_instvcc
+#define eu_openvcc eu_cmn.cu_openvcc
+#define eu_closevcc eu_cmn.cu_closevcc
+#define eu_output eu_cmn.cu_output
+#define eu_config eu_cmn.cu_config
+
+#endif /* _ENI_ENI_H */
diff --git a/sys/dev/hea/eni_buffer.c b/sys/dev/hea/eni_buffer.c
new file mode 100644
index 0000000..35d1b13
--- /dev/null
+++ b/sys/dev/hea/eni_buffer.c
@@ -0,0 +1,465 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_buffer.c,v 1.8 1998/08/26 23:28:53 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Handle adapter memory buffers for ENI adapters
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_buffer.c,v 1.8 1998/08/26 23:28:53 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+static int eni_test_memory __P((Eni_unit *));
+
+/*
+ * The host is going to manage (that is, allocate and free) buffers
+ * in the adapters RAM space. We are going to implement this as a
+ * linked list describing FREE and INUSE memory segments. Initially,
+ * the list contains one element with all memory marked free. As requests
+ * are made, we search the list until we find the first free element
+ * which can satisfy the request. If necessary, we will break the free
+ * element into an INUSE element, and a new FREE element. When freeing
+ * memory, we look at adjacent elements and if one or more are free,
+ * we will combine into a single larger FREE element.
+ */
+
+/*
+ * This is for testing purposes. Since there are two versions of
+ * the Efficient adapter with different memory sizes, this allows
+ * us to fool an adapter with more memory into thinking it has less.
+ */
+int eni_mem_max = MAX_ENI_MEM; /* Default to all available memory */
+
+/*
+ * Size and test adapter RAM
+ *
+ * Walk through adapter RAM writing known patterns and reading back
+ * for comparison. We write more than one pattern on the off chance
+ * that we "get lucky" and read what we expected.
+ *
+ * Arguments:
+ * eup pointer to device unit structure
+ *
+ * Returns
+ * size memory size in bytes
+ */
+static int
+eni_test_memory ( eup )
+ Eni_unit *eup;
+{
+ int ram_size = 0;
+ int i;
+ Eni_mem mp;
+
+ /*
+ * Walk through to maximum looking for RAM
+ */
+ for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) {
+ mp = (Eni_mem)((int)eup->eu_ram + i);
+ /* write pattern */
+ *mp = (u_long)TEST_PAT;
+ /* read pattern, match? */
+ if ( *mp == (u_long)TEST_PAT ) {
+ /* yes - write inverse pattern */
+ *mp = (u_long)~TEST_PAT;
+ /* read pattern, match? */
+ if ( *mp == (u_long)~TEST_PAT ) {
+ /* yes - assume another 1K available */
+ ram_size = i + TEST_STEP;
+ } else
+ break;
+ } else
+ break;
+ }
+ /*
+ * Clear all RAM to initial value of zero.
+ * This makes sure we don't leave anything funny in the
+ * queues.
+ */
+ KM_ZERO ( eup->eu_ram, ram_size );
+
+ /*
+ * If we'd like to claim to have less memory, here's where
+ * we do so. We take the minimum of what we'd like and what
+ * we really found on the adapter.
+ */
+ ram_size = MIN ( ram_size, eni_mem_max );
+
+ return ( ram_size );
+
+}
+
+/*
+ * Initialize our memory allocator.
+ *
+ * Arguments:
+ * eup Pointer to per unit structure
+ *
+ * Returns:
+ * size Physical RAM size
+ * -1 failed to initialize memory
+ *
+ */
+int
+eni_init_memory ( eup )
+ Eni_unit *eup;
+{
+
+ /*
+ * Have we (somehow) been called before?
+ */
+ if ( eup->eu_memmap != NULL )
+ {
+ /* Oops - it's already been initialized */
+ return -1;
+ }
+
+ /*
+ * Allocate initial element which will hold all of memory
+ */
+ if ( ( eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
+ M_NOWAIT ) ) == NULL )
+ {
+ /* Memory allocation error */
+ return -1;
+ }
+
+ /*
+ * Test and size memory
+ */
+ eup->eu_ramsize = eni_test_memory ( eup );
+
+ /*
+ * Initialize a one element list which contains
+ * all buffer memory
+ */
+ eup->eu_memmap->prev = eup->eu_memmap->next = NULL;
+ eup->eu_memmap->base = (caddr_t)SEGBUF_BASE;
+ eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE;
+ eup->eu_memmap->state = MEM_FREE;
+
+ return ( eup->eu_ramsize );
+}
+
+/*
+ * Allocate a buffer from adapter RAM. Due to constraints on the card,
+ * we may roundup the size request to the next largest chunksize. Note
+ * also that we must pay attention to address alignment within adapter
+ * memory as well.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ * size pointer to requested size - in bytes
+ *
+ * Returns:
+ * addr address relative to adapter of allocated memory
+ * size modified to reflect actual size of buffer
+ *
+ */
+caddr_t
+eni_allocate_buffer ( eup, size )
+ Eni_unit *eup;
+ u_long *size;
+{
+ int nsize;
+ int nclicks;
+ Mbd *eptr = eup->eu_memmap;
+
+ /*
+ * Initial size requested
+ */
+ nsize = *size;
+
+ /*
+ * Find the buffer size which will hold this request. There
+ * are 8 possible sizes, each a power of two up, starting at
+ * 256 words or 1024 bytes.
+ */
+ for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
+ if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
+ break;
+
+ /*
+ * Request was for larger then the card supports
+ */
+ if ( nclicks >= ENI_BUF_NBIT ) {
+ eup->eu_stats.eni_st_drv.drv_mm_toobig++;
+ /* Indicate 0 bytes allocated */
+ *size = 0;
+ /* Return NULL buffer */
+ return ( (caddr_t)NULL );
+ }
+
+ /*
+ * New size will be buffer size
+ */
+ nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
+
+ /*
+ * Look through memory for a segment large enough to
+ * hold request
+ */
+ while ( eptr ) {
+ /*
+ * State must be FREE and size must hold request
+ */
+ if ( eptr->state == MEM_FREE && eptr->size >= nsize )
+ {
+ /*
+ * Request will fit - now check if the
+ * alignment needs fixing
+ */
+ if ( ((u_int)eptr->base & (nsize-1)) != 0 )
+ {
+ caddr_t nbase;
+
+ /*
+ * Calculate where the buffer would have to
+ * fall to be aligned.
+ */
+ nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
+ ~(nsize-1));
+ /*
+ * If we use this alignment, will it still fit?
+ */
+ if ( (eptr->size - (nbase - eptr->base)) >= 0 )
+ {
+ Mbd *etmp;
+
+ /* Yep - create a new segment */
+ etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
+ M_NOWAIT );
+ if ( etmp == (Mbd *)NULL ) {
+ /*
+ * Couldn't allocate a new descriptor. Indicate
+ * failure and exit now or we'll start losing
+ * memory.
+ */
+ eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
+ *size = 0;
+ return ( (caddr_t)NULL );
+ }
+ /* Place it in the list */
+ etmp->next = eptr->next;
+ if ( etmp->next )
+ etmp->next->prev = etmp;
+ etmp->prev = eptr;
+ eptr->next = etmp;
+ /* Fill in new base and size */
+ etmp->base = nbase;
+ etmp->size = eptr->size - ( nbase - eptr->base );
+ /* Adjust old size */
+ eptr->size -= etmp->size;
+ /* Mark its state */
+ etmp->state = MEM_FREE;
+ eptr = etmp;
+ /* Done - outa here */
+ break;
+ }
+ } else
+ break; /* Alignment is okay - we're done */
+ }
+ /* Haven't found anything yet - keep looking */
+ eptr = eptr->next;
+ }
+
+ if ( eptr != NULL )
+ {
+ /* Found a usable segment - grab what we need */
+ /* Exact fit? */
+ if ( eptr->size == nsize )
+ /* Mark it as INUSE */
+ eptr->state = MEM_INUSE;
+ else
+ {
+ Mbd *etmp;
+ /* larger then we need - split it */
+
+ etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_NOWAIT );
+ if ( etmp == (Mbd *)NULL ) {
+ /*
+ * Couldn't allocate new descriptor. Indicate
+ * failure and exit now or we'll start losing
+ * memory.
+ */
+ eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
+ *size = 0;
+ return ( (caddr_t)NULL );
+ }
+ /* Place new element in list */
+ etmp->next = eptr->next;
+ if ( etmp->next )
+ etmp->next->prev = etmp;
+ etmp->prev = eptr;
+ eptr->next = etmp;
+ /* Set new base, size and state */
+ etmp->base = eptr->base + nsize;
+ etmp->size = eptr->size - nsize;
+ etmp->state = MEM_FREE;
+ /* Adjust size and state of element we intend to use */
+ eptr->size = nsize;
+ eptr->state = MEM_INUSE;
+ }
+ }
+
+ /* After all that, did we find a usable buffer? */
+ if ( eptr )
+ {
+ /* Record another inuse buffer of this size */
+ if ( eptr->base )
+ eup->eu_memclicks[nclicks]++;
+
+ /*
+ * Return true size of allocated buffer
+ */
+ *size = eptr->size;
+ /*
+ * Make address relative to start of RAM since
+ * its (the address) for use by the adapter, not
+ * the host.
+ */
+ return ((caddr_t)eptr->base);
+ } else {
+ eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
+ /* No buffer to return - indicate zero length */
+ *size = 0;
+ /* Return NULL buffer */
+ return ( (caddr_t)NULL );
+ }
+}
+
+/*
+ * Procedure to release a buffer previously allocated from adapter
+ * RAM. When possible, we'll compact memory.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ * base base adapter address of buffer to be freed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+eni_free_buffer ( eup, base )
+ Eni_unit *eup;
+ caddr_t base;
+{
+ Mbd *eptr = eup->eu_memmap;
+ int nclicks;
+
+ /* Look through entire list */
+ while ( eptr )
+ {
+ /* Is this the buffer to be freed? */
+ if ( eptr->base == base )
+ {
+ /*
+ * We're probably asking for trouble but,
+ * assume this is it.
+ */
+ if ( eptr->state != MEM_INUSE )
+ {
+ eup->eu_stats.eni_st_drv.drv_mm_notuse++;
+ /* Huh? Something's wrong */
+ return;
+ }
+ /* Reset state to FREE */
+ eptr->state = MEM_FREE;
+
+ /* Determine size for stats info */
+ for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
+ if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
+ break;
+
+ /* Valid size? Yes - decrement inuse count */
+ if ( nclicks < ENI_BUF_NBIT )
+ eup->eu_memclicks[nclicks]--;
+
+ /* Try to compact neighbors */
+ /* with previous */
+ if ( eptr->prev )
+ if ( eptr->prev->state == MEM_FREE )
+ {
+ Mbd *etmp = eptr;
+ /* Add to previous block */
+ eptr->prev->size += eptr->size;
+ /* Set prev block to skip this one */
+ eptr->prev->next = eptr->next;
+ /* Set next block to skip this one */
+ if ( eptr->next )
+ eptr->next->prev = eptr->prev;
+ /* Reset to where we want to be */
+ eptr = eptr->prev;
+ /* and free this element */
+ (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
+ }
+ /* with next */
+ if ( eptr->next )
+ if ( eptr->next->state == MEM_FREE )
+ {
+ Mbd *etmp = eptr->next;
+
+ /* add following block in */
+ eptr->size += etmp->size;
+ /* set next next block to skip next block */
+ if ( etmp->next )
+ etmp->next->prev = eptr;
+ /* skip next block */
+ eptr->next = etmp->next;
+ /* and free next element */
+ (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
+ }
+ /*
+ * We've freed the buffer and done any compaction,
+ * we needn't look any further...
+ */
+ return;
+ }
+ eptr = eptr->next;
+ }
+
+ if ( eptr == NULL )
+ {
+ /* Oops - failed to find the buffer. This is BAD */
+ eup->eu_stats.eni_st_drv.drv_mm_notfnd++;
+ }
+
+}
+
diff --git a/sys/dev/hea/eni_globals.c b/sys/dev/hea/eni_globals.c
new file mode 100644
index 0000000..a2a51fa
--- /dev/null
+++ b/sys/dev/hea/eni_globals.c
@@ -0,0 +1,102 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_globals.c,v 1.2 1997/05/06 22:07:52 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Global variable definitions
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_globals.c,v 1.2 1997/05/06 22:07:52 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+/*
+ * Device unit table
+ */
+Eni_unit *eni_units[ENI_MAX_UNITS] = {NULL};
+
+/*
+ * ATM Interface services
+ */
+/*
+ * AAL5 service stack
+ */
+static struct stack_defn eni_svaal5 = {
+ NULL,
+ SAP_CPCS_AAL5,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+/*
+ * Efficient hardware doesn't support AAL3/4. Don't define
+ * an AAL3/4 stack.
+ */
+/*
+ * AAL0 service stack
+ */
+static struct stack_defn eni_svaal0 = {
+ &eni_svaal5,
+ SAP_ATM,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+struct stack_defn *eni_services = &eni_svaal0;
+
+/*
+ * Storage pools
+ */
+struct sp_info eni_nif_pool = {
+ "eni nif pool", /* si_name */
+ sizeof(struct atm_nif), /* si_blksiz */
+ 5, /* si_blkcnt */
+ 20 /* si_maxallow */
+};
+
+struct sp_info eni_vcc_pool = {
+ "eni vcc pool", /* si_name */
+ sizeof(Eni_vcc), /* si_blksiz */
+ 10, /* si_blkcnt */
+ 100 /* si_maxallow */
+};
+
diff --git a/sys/dev/hea/eni_if.c b/sys/dev/hea/eni_if.c
new file mode 100644
index 0000000..41ffd48
--- /dev/null
+++ b/sys/dev/hea/eni_if.c
@@ -0,0 +1,270 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_if.c,v 1.6 1998/08/26 23:28:53 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Network interface layer support
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_if.c,v 1.6 1998/08/26 23:28:53 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_suni.h>
+#include <dev/hea/eni_var.h>
+
+static void eni_get_stats __P((Eni_unit *));
+
+/*
+ * SUNI statistics counters take one of three forms:
+ * single byte value (0x0 - 0xff)
+ * two byte value (0x0 - 0xffff)
+ * two + 1/2 (three) byte value
+ * (0x0 - 0x0fffff)
+ */
+#define READ_ONE(x) ( (eup->eu_suni[(x)] & 0xff) )
+
+#define READ_TWO(x) ( (eup->eu_suni[(x)+1] & 0xff) << 8 | \
+ (eup->eu_suni[(x)] & 0xff) )
+
+#define READ_THREE(x) ( (eup->eu_suni[(x)+2] & 0xf) << 16 | \
+ (eup->eu_suni[(x)+1] & 0xff) << 8 | \
+ (eup->eu_suni[(x)] & 0xff) )
+
+/*
+ * Do an initial read of the error counters without saving them.
+ * In effect, this will "zero" our idea of the number of errors
+ * which have occurred since the driver was loaded.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+eni_zero_stats ( eup )
+ Eni_unit *eup;
+{
+ int val;
+
+ /*
+ * Write the SUNI master control register which
+ * will cause all the statistics counters to be
+ * loaded.
+ */
+ eup->eu_suni[SUNI_MASTER_REG] = eup->eu_suni[SUNI_MASTER_REG];
+
+ /*
+ * Delay to allow for counter load time...
+ */
+ DELAY ( SUNI_DELAY );
+
+ /*
+ * Statistics counters contain the number of events
+ * since the last time the counter was read.
+ */
+ val = READ_TWO ( SUNI_SECT_BIP_REG ); /* oc3_sect_bip8 */
+ val = READ_TWO ( SUNI_PATH_BIP_REG ); /* oc3_path_bip8 */
+ val = READ_THREE ( SUNI_LINE_BIP_REG ); /* oc3_line_bip24 */
+ val = READ_THREE ( SUNI_LINE_FEBE_REG ); /* oc3_line_febe */
+ val = READ_TWO ( SUNI_PATH_FEBE_REG ); /* oc3_path_febe */
+ val = READ_ONE ( SUNI_HECS_REG ); /* oc3_hec_corr */
+ val = READ_ONE ( SUNI_UHECS_REG ); /* oc3_hec_uncorr */
+}
+
+/*
+ * Retrieve SUNI stats
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+eni_get_stats ( eup )
+ Eni_unit *eup;
+{
+ /*
+ * Write the SUNI master control register which
+ * will cause all the statistics counters to be
+ * loaded.
+ */
+ eup->eu_suni[SUNI_MASTER_REG] = eup->eu_suni[SUNI_MASTER_REG];
+
+ /*
+ * Delay to allow for counter load time...
+ */
+ DELAY ( 10 );
+
+ /*
+ * Statistics counters contain the number of events
+ * since the last time the counter was read.
+ */
+ eup->eu_stats.eni_st_oc3.oc3_sect_bip8 +=
+ READ_TWO ( SUNI_SECT_BIP_REG );
+ eup->eu_stats.eni_st_oc3.oc3_path_bip8 +=
+ READ_TWO ( SUNI_PATH_BIP_REG );
+ eup->eu_stats.eni_st_oc3.oc3_line_bip24 +=
+ READ_THREE ( SUNI_LINE_BIP_REG );
+ eup->eu_stats.eni_st_oc3.oc3_line_febe +=
+ READ_THREE ( SUNI_LINE_FEBE_REG );
+ eup->eu_stats.eni_st_oc3.oc3_path_febe +=
+ READ_TWO ( SUNI_PATH_FEBE_REG );
+ eup->eu_stats.eni_st_oc3.oc3_hec_corr +=
+ READ_ONE ( SUNI_HECS_REG );
+ eup->eu_stats.eni_st_oc3.oc3_hec_uncorr +=
+ READ_ONE ( SUNI_UHECS_REG );
+}
+
+/*
+ * Handle netatm core service interface ioctl requests
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * code ioctl function (sub)code
+ * data data to/from ioctl
+ * arg optional code-specific argument
+ *
+ * Returns:
+ * 0 request processed successfully
+ * error request failed - reason code
+ *
+ */
+int
+eni_atm_ioctl ( code, data, arg )
+ int code;
+ caddr_t data;
+ caddr_t arg;
+{
+ struct atminfreq *aip = (struct atminfreq *)data;
+ struct atm_pif *pip = (struct atm_pif *)arg;
+ Eni_unit *eup = (Eni_unit *)pip;
+ caddr_t buf = aip->air_buf_addr;
+ struct air_vinfo_rsp *avr;
+ int count, len, buf_len = aip->air_buf_len;
+ int err = 0;
+ char ifname[2*IFNAMSIZ];
+
+ ATM_DEBUG2("eni_atm_ioctl: code=%d, opcode=%d\n",
+ code, aip->air_opcode );
+
+ switch ( aip->air_opcode ) {
+
+ case AIOCS_INF_VST:
+ /*
+ * Get vendor statistics
+ */
+ if ( eup == NULL )
+ return ( ENXIO );
+ sprintf ( ifname, "%s%d", pip->pif_name, pip->pif_unit );
+
+ /*
+ * Cast response structure onto user's buffer
+ */
+ avr = (struct air_vinfo_rsp *)buf;
+
+ /*
+ * How large is the response structure
+ */
+ len = sizeof(struct air_vinfo_rsp);
+
+ /*
+ * Sanity check - enough room for response structure?
+ */
+ if ( buf_len < len )
+ return ( ENOSPC );
+
+ /*
+ * Copy interface name into response structure
+ */
+ if ( err = copyout ( ifname, avr->avsp_intf, IFNAMSIZ ) )
+ break;
+
+ /*
+ * Advance the buffer address and decrement the size
+ */
+ buf += len;
+ buf_len -= len;
+
+ /*
+ * Get the vendor stats (SUNI) from the hardware
+ */
+ eni_get_stats ( eup );
+ /*
+ * Stick as much of it as we have room for
+ * into the response
+ */
+ count = MIN ( sizeof(Eni_stats), buf_len );
+
+ /*
+ * Copy stats into user's buffer. Return value is
+ * amount of data copied.
+ */
+ if ( err = copyout ((void *)&eup->eu_stats, buf,
+ count))
+ break;
+ buf += count;
+ buf_len -= count;
+ if ( count < sizeof(Eni_stats) )
+ err = ENOSPC;
+
+ /*
+ * Record amount we're returning as vendor info...
+ */
+ if (err = copyout(&count, &avr->avsp_len, sizeof(int)))
+ break;
+
+ /*
+ * Update the reply pointers and length
+ */
+ aip->air_buf_addr = buf;
+ aip->air_buf_len = buf_len;
+ break;
+
+ default:
+ err = ENOSYS; /* Operation not supported */
+ break;
+ }
+
+ return ( err );
+
+}
+
diff --git a/sys/dev/hea/eni_init.c b/sys/dev/hea/eni_init.c
new file mode 100644
index 0000000..d8106ab
--- /dev/null
+++ b/sys/dev/hea/eni_init.c
@@ -0,0 +1,142 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_init.c,v 1.6 1998/08/26 23:28:53 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Driver initialization support
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_init.c,v 1.6 1998/08/26 23:28:53 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+/*
+ * Initialize adapter for PDU processing
+ *
+ * Enable interrupts, set master control, initialize TX buffer,
+ * set initial pointers, etc.
+ *
+ * Arguments:
+ * eup pointer to device unit structure
+ *
+ * Returns:
+ * 0 successful
+ * error error condition
+ */
+int
+eni_init ( eup )
+ Eni_unit *eup;
+{
+ u_long words, order;
+
+ /*
+ * Allocate one large TX buffer. Currently we use only one
+ * channel with full cell rate which all VCs will use.
+ * This will (probably) have to change (alot) when we
+ * implement QoS.
+ */
+ /*
+ * Server cards, which have more then 512KB of RAM, will
+ * allocate a 128KB TX buffer, while client cards, with
+ * 512KB or less will allocate a 32KB TX buffer.
+ */
+ words = ( eup->eu_ramsize > MAX_CLIENT_RAM * ENI_BUF_PGSZ ?
+ TX_LARGE_BSIZE : TX_SMALL_BSIZE ) * ENI_BUF_PGSZ;
+ if ( ( eup->eu_txbuf = eni_allocate_buffer ( eup, &words ) ) ==
+ (caddr_t)NULL ) {
+ return ENOMEM;
+ }
+ eup->eu_txsize = words >> 2; /* Bytes to words */
+ words >>= ENI_LOC_PREDIV; /* Predivide by 256 words */
+ for ( order = -1; words; order++ )
+ words >>= 1;
+ eup->eu_midway[MIDWAY_TXPLACE] =
+ (order << TXSIZE_SHIFT) | ((int)eup->eu_txbuf >> ENI_LOC_PREDIV);
+ eup->eu_txpos = eup->eu_midway[MIDWAY_DESCR] & 0x7FFF;
+ /*
+ * Set first word of unack'ed data to start
+ */
+ eup->eu_txfirst = eup->eu_txpos;
+
+ /*
+ * Set initial values of local DMA pointer used to prevent wraps
+ */
+ eup->eu_txdmawr = 0;
+ eup->eu_rxdmawr = 0;
+
+ /*
+ * Initialize queue's for receive/transmit pdus
+ */
+ eup->eu_txqueue.ifq_maxlen = ENI_IFQ_MAXLEN;
+ eup->eu_rxqueue.ifq_maxlen = ENI_IFQ_MAXLEN;
+
+ /*
+ * Acknowledge any interrupts
+ */
+ (void) eup->eu_midway[MIDWAY_ISA];
+
+ /*
+ * "Zero" Sonet error counters
+ */
+ eni_zero_stats ( eup );
+
+ /*
+ * Set master control register
+ *
+ * IntSel1 | LOCK_MODE | DMA_ENABLE | TX_ENABLE | RX_ENABLE
+ *
+ */
+ eup->eu_midway[MIDWAY_MASTER] = 1 << ENI_ISEL_SHIFT |
+ ENI_M_DMAENABLE | ENI_M_TXENABLE | ENI_M_RXENABLE;
+
+ /*
+ * Enable interrupts
+ */
+ eup->eu_midway[MIDWAY_IE] = ENI_INT_SERVICE | ENI_INT_RX_DMA |
+ ENI_INT_TX_DMA | ENI_INT_DMA_ERR | ENI_INT_DMA_LERR |
+ ENI_INT_IDEN | ENI_INT_DMA_OVFL;
+
+ /*
+ * Last thing to do is to indicate that we've finished initializing
+ * this unit.
+ */
+ eup->eu_flags |= CUF_INITED;
+
+ return 0;
+}
+
diff --git a/sys/dev/hea/eni_intr.c b/sys/dev/hea/eni_intr.c
new file mode 100644
index 0000000..e0a5642
--- /dev/null
+++ b/sys/dev/hea/eni_intr.c
@@ -0,0 +1,230 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_intr.c,v 1.4 1998/08/26 23:28:54 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Interrupt processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_intr.c,v 1.4 1998/08/26 23:28:54 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_suni.h>
+#include <dev/hea/eni_var.h>
+
+static void eni_suni_intr __P((Eni_unit *));
+
+/*
+ * SUNI Interrupt processing
+ *
+ * Currently, we don't do anything more then clear the interrupt
+ * for the SUNI chip.
+ *
+ * Arguments:
+ * eup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+eni_suni_intr ( eup )
+ Eni_unit *eup;
+{
+ int SuniInt;
+ int val;
+
+ SuniInt = eup->eu_suni[SUNI_IS_REG];
+
+ /* RSOPI */
+ if ( SuniInt & SUNI_RSOPI )
+ val = eup->eu_suni[SUNI_RSOP_REG];
+
+ /* RLOPI */
+ if ( SuniInt & SUNI_RLOPI )
+ val = eup->eu_suni[SUNI_RLOP_REG];
+
+ /* RPOPI */
+ if ( SuniInt & SUNI_RPOPI )
+ val = eup->eu_suni[SUNI_RPOP_IS_REG];
+
+ /* RACPI */
+ if ( SuniInt & SUNI_RACPI )
+ val = eup->eu_suni[SUNI_RACP_REG];
+
+ /* TACPI */
+ if ( SuniInt & SUNI_TACPI )
+ val = eup->eu_suni[SUNI_TACP_REG];
+
+ /* TROOLI */
+ if ( SuniInt & SUNI_TROOLI )
+ val = eup->eu_suni[SUNI_CLOCK_REG];
+
+ /* LCDI */
+ /* Cleared when reading Master Interrupt Status Reg */
+
+ /* RDOOLI */
+ if ( SuniInt & SUNI_RDOOLI )
+ val = eup->eu_suni[SUNI_CLOCK_REG];
+
+ return;
+}
+
+/*
+ * Device interrupt routine
+ *
+ * Service an interrupt from this device
+ *
+ * Arguments:
+ * eup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+#if defined(BSD) && BSD < 199506
+int
+#else
+void
+#endif
+eni_intr ( arg )
+ void *arg;
+{
+ Eni_unit *eup = (Eni_unit *)arg;
+#if defined(BSD) && BSD < 199506
+ int serviced = 1;
+#endif /* BSD < 199506 */
+
+ /*
+ * Read and acknowledge any interrupts
+ */
+ u_long mask = eup->eu_midway[MIDWAY_ISA];
+ /*
+ * Read the error statistics counter
+ */
+ u_long sval = eup->eu_midway[MIDWAY_STAT];
+
+ /*
+ * Update statistics from adapter
+ */
+ eup->eu_trash += ( sval >> 16 );
+ eup->eu_ovfl += ( sval & 0xffff );
+
+ /*
+ * We handle any DMA completes first so
+ * that we can free resources for use
+ * during transmit and especially receive
+ */
+ /*
+ * Handle RX DMA Complete
+ */
+ if ( mask & ENI_INT_RX_DMA ) {
+ eni_recv_drain ( eup );
+ }
+
+ /*
+ * Handle TX DMA Complete
+ */
+ if ( mask & ENI_INT_TX_DMA ) {
+ eni_xmit_drain ( eup );
+ }
+
+ /*
+ * Look for any PDUs in service list
+ */
+ if ( mask & ENI_INT_SERVICE ) {
+ eni_do_service ( eup );
+ }
+
+ /*
+ * Handle miscelaneous interrupts
+ */
+ if ( mask & ENI_INT_STAT ) { /* STAT_OVFL */
+ log ( LOG_INFO, "eni_intr: stat_ovfl: 0x%x\n", sval );
+ }
+ if ( mask & ENI_INT_SUNI ) { /* SUNI_INTR */
+ eni_suni_intr ( eup );
+ }
+ if ( mask & ENI_INT_DMA_ERR ) { /* DMA Error */
+ log ( LOG_ERR,
+ "eni_intr: DMA Error\n" );
+ /*
+ * We don't know how to recover from DMA errors
+ * yet. The adapter has disabled any further
+ * processing and we're going to leave it like
+ * that.
+ */
+#if defined(BSD) && BSD < 199506
+ return serviced; /* Leave now */
+#else
+ return; /* Leave now */
+#endif
+ }
+ if ( mask & ENI_INT_IDEN ) {
+ log ( LOG_ERR,
+ "eni_intr: TX DMA Ident mismatch\n" );
+ /*
+ * Something in the TX buffer has really gotten messed
+ * up. Since this is most likely a driver bug, and
+ * the adapter has shut everything down, leave it
+ * like that.
+ */
+#if BSD < 199506
+ return 0; /* Leave now */
+#else
+ return; /* Leave now */
+#endif
+ }
+ if ( mask & ENI_INT_DMA_OVFL )
+ eup->eu_stats.eni_st_drv.drv_xm_dmaovfl++;
+ if ( mask & ENI_INT_DMA_LERR ) {
+ log ( LOG_ERR,
+ "eni_intr: DMA LERR\n" );
+#if BSD < 199506
+ return 0;
+#else
+ return;
+#endif
+ }
+
+#if BSD < 199506
+ return 0;
+#else
+ return;
+#endif
+}
+
diff --git a/sys/dev/hea/eni_receive.c b/sys/dev/hea/eni_receive.c
new file mode 100644
index 0000000..097f297
--- /dev/null
+++ b/sys/dev/hea/eni_receive.c
@@ -0,0 +1,871 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_receive.c,v 1.13 1998/08/07 22:14:13 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Receive management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_receive.c,v 1.13 1998/08/07 22:14:13 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+static void eni_recv_stack __P((void *, KBuffer *));
+
+#ifdef DIAGNOSTIC
+extern int eni_pdu_print;
+#endif
+
+/*
+ * Procedure to remove VCs from the Service List and generate DMA
+ * requests to move the associated PDUs into host memory. As PDUs
+ * are completed in adapter memory, the adapter examines the IN_SERVICE
+ * bit for the VC in the VC table. If this bit is not set, the adapter
+ * will place the VC number at the end of the service list queue, set
+ * the IN_SERVICE bit in the VC table, and interrupt the host. The host
+ * will remove VCs from the service list, clear the IN_SERVICE bit in
+ * the VC table, and create a DMA list to move the PDU into host buffers.
+ *
+ * Arguments:
+ * eup pointer to per unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+eni_do_service ( eup )
+ Eni_unit *eup;
+{
+ int vcc;
+ Eni_vcc *evp;
+ u_long servwrite;
+ VCI_Table *vct;
+ u_long rdptr;
+ u_long *rxp;
+ KBuffer *m;
+ u_long dma[TEMP_DMA_SIZE];
+ u_long i, j;
+ u_long dma_rd, dma_wr;
+ u_long dma_avail;
+ int pdulen;
+ int mask;
+ u_long *upp;
+
+ /*
+ * Where is the adapter currently inserting entries?
+ */
+ servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK;
+ /*
+ * As long as we're not caught up with the adapter, keep
+ * removing VCs from the service list.
+ */
+ while ( servwrite != eup->eu_servread ) {
+ int vci_hdr;
+ u_long descr;
+
+ /*
+ * Get VC number and find VC table entry.
+ */
+ vcc = eup->eu_svclist[eup->eu_servread];
+ vct = &eup->eu_vcitbl[vcc];
+ vci_hdr = vct->vci_control; /* Current status */
+
+ /*
+ * Check that this VCC still needs servicing. We
+ * might have closed this VCC down in between
+ * the adapter setting the flag and our checking
+ * the flag. Also check that we haven't placed the
+ * VCC into TRASH mode.
+ */
+ if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 ||
+ ( vci_hdr & ~VCI_MODE_MASK ==
+ VCI_MODE_TRASH << VCI_MODE_SHIFT ) )
+ goto next_vcc;
+
+ /*
+ * Find the size of this VCs buffer
+ */
+ mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK;
+ mask = 1 << (ENI_LOC_PREDIV + mask);
+ /* Turn byte count into word count */
+ mask >>= 2;
+ /*
+ * Find the start of the adapter buffer for this VC.
+ */
+ rxp = (u_long *)
+ ((int)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK)
+ << ENI_LOC_PREDIV) + (int)eup->eu_ram);
+ /*
+ * Locate incoming VCC for this PDU and find where we
+ * should next read from.
+ */
+ evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,
+ 0, vcc, VCC_IN );
+ if ( evp == (Eni_vcc *)NULL )
+ goto next_vcc; /* VCI no longer active */
+ rdptr = evp->ev_rxpos;
+ /*
+ * Find out where the adapter is currently reassembling.
+ * The PDU which starts at descr is not yet complete so we
+ * must stop there.
+ */
+ descr = ( vct->vci_descr >> 16 ) & 0x7FFF;
+ /*
+ * As long as we haven't processed all the completed PDUs on
+ * this VC, keep going...
+ */
+ while ( rdptr != descr )
+ {
+ int n_cells;
+ int pdu_descr;
+ int aal5;
+
+ /*
+ * Ensure that the following are reset for every new
+ * PDU.
+ */
+ upp = NULL;
+ m = NULL;
+
+ /*
+ * Fisrt build a DMA with JK to skip the descriptor word.
+ * We must always skip the descriptor even if it turns out
+ * that there isn't any PDU here.
+ */
+ j = 0;
+ dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) |
+ ( vcc << DMA_VCC_SHIFT ) | DMA_JK;
+ dma[j++] = 0;
+
+ /*
+ * We'll use some of the values below for skipping
+ * bad PDUs or counting statistics so compute them
+ * now.
+ */
+
+ /*
+ * Grab a copy of the descriptor word
+ */
+ pdu_descr = rxp[rdptr];
+
+ /*
+ * Strip out cell count from descriptor word.
+ * At this point, we still don't know if there
+ * is any real data until after we check for
+ * TRASH mode.
+ */
+ n_cells = pdu_descr & DESCR_CELL_COUNT;
+
+ /*
+ * Is this an AAL5 PDU? Check MODE in vci_hdr.
+ */
+ aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) ==
+ VCI_MODE_AAL5 << VCI_MODE_SHIFT );
+
+ /*
+ * Now check to see if we're trashing on this vcc.
+ * If so, there is no data with this VC and the
+ * next word after the current descriptor is the
+ * descriptor for the next PDU.
+ */
+ if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) {
+ if ( aal5 )
+ /*
+ * Count as number of AAL5 cells dropped
+ */
+ eup->eu_stats.eni_st_aal5.aal5_drops += n_cells;
+ else
+ /*
+ * Count as number of AAL0 cells dropped
+ */
+ eup->eu_stats.eni_st_aal0.aal0_drops += n_cells;
+ eup->eu_pif.pif_ierrors++;
+ /*
+ * When cells have been trashed, all we have in the
+ * buffer is a descriptor word. There are no data
+ * words. Set the number of cells to zero so that
+ * we correctly skip to the next word which will
+ * be the descriptor for the next PDU.
+ */
+ n_cells = 0;
+ /*
+ * Go issue the DMA to skip this descriptor word.
+ */
+ goto send_dma;
+ }
+
+ /*
+ * Data length: number of cells * cell size
+ */
+ pdulen = n_cells * BYTES_PER_CELL;
+
+ /*
+ * If this is an AAL5 PDU, then we need to check
+ * for the presence of any CRC errors. If there
+ * is one or more CRC errors, then we are going to
+ * drop this PDU.
+ */
+ if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) {
+ /*
+ * Count the stat
+ */
+ eup->eu_pif.pif_ierrors++;
+ eup->eu_stats.eni_st_aal5.aal5_pdu_crc++;
+ if ( evp->ev_connvc->cvc_vcc )
+ evp->ev_connvc->cvc_vcc->vc_ierrors++;
+ /*
+ * Build a DMA entry to skip the rest of this
+ * PDU.
+ */
+ dma[j++] =
+ (((rdptr + n_cells*WORDS_PER_CELL + 1)
+ & (mask-1)) << DMA_COUNT_SHIFT ) |
+ (vcc << DMA_VCC_SHIFT ) | DMA_JK;
+ dma[j++] = 0;
+ /*
+ * All done with this PDU. Get a buffer to save some
+ * data for reclamation services.
+ */
+ KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT,
+ KB_T_DATA );
+ if ( m ) {
+ u_long *up;
+
+ KB_DATASTART ( m, up, u_long * );
+ /*
+ * Indicate no PDU
+ */
+ KB_PLENSET ( m, 0 );
+ /*
+ * Set buffer length - only driver overhead
+ */
+ KB_LEN ( m ) = 3 * sizeof ( u_long );
+ /*
+ * Insert vcc, space for DMA pointers,
+ * and pdulen
+ */
+ *up++ = vcc;
+ upp = up; /* Remember location */
+ up++; /* And skip it */
+ /* - to be filled later */
+ *up = pdulen; /* Actual PDU length if it */
+ /* were valid */
+ } else {
+ /*
+ * We've a real problem here as now we can't
+ * reclaim/advance resources/safety pointers.
+ */
+ eup->eu_stats.eni_st_drv.drv_rv_norsc++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_do_service: No drain buffers available. Receiver about to lock.\n" );
+#endif
+ }
+ goto send_dma;
+ }
+
+ /*
+ * Do we need to strip the AAL layer? Yes if this
+ * is an AAL5 PDU.
+ */
+ if ( aal5 ) {
+ /*
+ * Grab the CS-PDU length. Find the address of the
+ * last word, back up one word to skip CRC, and
+ * then mask the whole thing to handle circular wraps.
+ */
+ pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1)
+ & (mask-1)]
+ & 0xFFFF;
+ }
+
+ /*
+ * We now have a valid PDU of some length. Build
+ * the necessary DMA list to move it into host
+ * memory.
+ */
+
+ /*
+ * Get an initial buffer.
+ */
+ KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
+ /*
+ * Do we have a valid buffer?
+ */
+ if ( m != (KBuffer *)NULL )
+ {
+ int len;
+ u_long *up;
+ KBuffer *m0;
+
+ KB_DATASTART ( m, up, u_long * );
+ /*
+ * Fill in pdulen in PKTHDR structure (for IP).
+ */
+ KB_PLENSET ( m, pdulen );
+ /*
+ * We're going to save the VCI nuber, the start
+ * and stop DMA pointers, and the PDU length at
+ * the head of the buffer. We'll pull this out
+ * later after the DMA has completed.
+ *
+ * Insert VCI number as first word in first buffer,
+ * remeber where we want to store the start/stop
+ * pointers, and store the PDU length.
+ */
+ *up++ = vcc; /* PDU's VCC */
+ upp = up; /* Remember where we are */
+ up++; /* To stuff start/stop pointers in */
+ *up++ = pdulen; /* PDU's length */
+ /*
+ * Leave some extra room in case a higher protocol
+ * (IP) wants to do a pullup. Maybe we can keep
+ * someone from having to allocate another buffer
+ * a do a larger memory copy.
+ */
+ len = MIN ( ENI_SMALL_BSIZE, pdulen );
+ (void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j,
+ vcc, (u_long)up, len );
+ /*
+ * Adjust length of remaining data in PDU
+ */
+ pdulen -= len;
+ /*
+ * Set buffer length, including our overhead
+ */
+ KB_LEN ( m ) = len + 3 * sizeof ( u_long );
+ /*
+ * Finish by moving anything which won't fit in
+ * first buffer
+ */
+ m0 = m;
+ while ( pdulen ) {
+ KBuffer *m1;
+ u_long data_addr;
+
+ /*
+ * Get another buffer
+ */
+ KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT,
+ KB_T_DATA );
+
+ /*
+ * If we succeeded...
+ */
+ if ( m1 ) {
+ /*
+ * Figure out how much we can move into
+ * this buffer.
+ */
+ len = MIN ( ENI_LARGE_BSIZE, pdulen );
+ /*
+ * Setup DMA list for this buffer
+ */
+ KB_DATASTART ( m1, data_addr, u_long );
+ (void) eni_set_dma
+ ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc,
+ data_addr, len );
+ /*
+ * Adjust remaining length
+ */
+ pdulen -= len;
+ /*
+ * Set buffer length
+ */
+ KB_LEN ( m1 ) = len;
+ /*
+ * Link new buffer onto end and advance
+ * pointer
+ */
+ KB_NEXT ( m0 ) = m1;
+ m0 = m1;
+ } else {
+ /*
+ * Either we were unable to grab another
+ * buffer or there are no large buffers
+ * available. We know that the first
+ * buffer is valid, so drop everything
+ * else, build a JK DMA to skip/drop this
+ * PDU, set the pointers to reclaim
+ * resources/advance pointers, and
+ * finish this PDU now.
+ */
+ if ( KB_NEXT ( m ) )
+ KB_FREEALL ( KB_NEXT ( m ) );
+ eup->eu_pif.pif_ierrors++;
+ j = 2;
+ dma[j++] =
+ (((rdptr + n_cells*WORDS_PER_CELL + 1)
+ & (mask-1)) << DMA_COUNT_SHIFT ) |
+ (vcc << DMA_VCC_SHIFT ) |
+ DMA_JK;
+ dma[j++] = 0;
+ /*
+ * Reset PDU length to zero
+ */
+ KB_PLENSET ( m, 0 );
+ /*
+ * Count some statistics
+ */
+ /*
+ * Count this as dropped cells
+ */
+ if ( aal5 ) {
+ eup->eu_stats.eni_st_aal5.aal5_drops +=
+ n_cells;
+ eup->eu_stats.eni_st_aal5.aal5_pdu_drops++;
+ } else
+ eup->eu_stats.eni_st_aal0.aal0_drops +=
+ n_cells;
+ /*
+ * Drop it
+ */
+ goto send_dma;
+ }
+ }
+ /*
+ * If necessary, skip AAL layer
+ */
+ if ( aal5 ) {
+ dma[j++] =
+ (((rdptr + n_cells*WORDS_PER_CELL + 1)
+ & (mask-1)) << DMA_COUNT_SHIFT)
+ | (vcc << DMA_VCC_SHIFT) | DMA_JK;
+ dma[j++] = 0;
+ }
+ } else {
+ /*
+ * We failed to get an initial buffer. Since we
+ * haven't changed anything for this PDU yet and the
+ * PDU is still valid, exit now and try to service it
+ * next time around. We're not very likely to get
+ * another buffer right now anyways.
+ */
+ eup->eu_stats.eni_st_drv.drv_rv_nobufs++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+"eni_do_service: No buffers available. Exiting without servicing service list.\n" );
+#endif
+ /*
+ * Clear the IN_SERVICE indicator for this VCC
+ */
+ vct->vci_control &= ~VCI_IN_SERVICE;
+ return;
+ }
+
+send_dma:
+ /*
+ * Set the end bit on the last DMA for this PDU
+ */
+ dma[j-2] |= DMA_END_BIT;
+
+ /*
+ * Where are the current DMA pointers
+ */
+ dma_rd = eup->eu_midway[MIDWAY_RX_RD];
+ dma_wr = eup->eu_midway[MIDWAY_RX_WR];
+
+ /*
+ * Check how much space is available
+ */
+ if ( dma_rd == dma_wr )
+ dma_avail = DMA_LIST_SIZE;
+ else
+ dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
+ & (DMA_LIST_SIZE-1);
+
+ /*
+ * Check for queue full or wrap past write okay pointer
+ */
+ if ( dma_avail < j ||
+ ( dma_wr + j > eup->eu_rxdmawr + DMA_LIST_SIZE ) ) {
+ /*
+ * There's no room in the DMA list to insert
+ * this request. Since we haven't changed anything
+ * yet and the PDU is good, exit now and service
+ * it next time around. What we really need to do
+ * is wait for the RX list to drain and that won't
+ * happen if we keep trying to process PDUs here.
+ */
+ eup->eu_stats.eni_st_drv.drv_rv_nodma++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+"eni_do_service: No room in receive DMA list. Postponing service request.\n" );
+#endif
+ /*
+ * Free the local buffer chain
+ */
+ KB_FREEALL ( m );
+ /*
+ * Clear the IN_SERVICE indicator for this VCC.
+ */
+ vct->vci_control &= ~VCI_IN_SERVICE;
+ return;
+ }
+
+ /*
+ * If we have a buffer chain, save the starting
+ * dma_list location.
+ */
+ if ( upp ) {
+ *upp = dma_wr << 16;
+ }
+
+ /*
+ * Stuff the DMA list
+ */
+ j >>= 1;
+ for ( i = 0; i < j; i++ ) {
+ eup->eu_rxdma[dma_wr*2] = dma[i*2];
+ eup->eu_rxdma[dma_wr*2+1] = dma[i*2+1];
+ dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1);
+ }
+ /*
+ * If we have a buffer chain, save the location of
+ * the ending dma_list location and queue the chain
+ * so that we can recover the resources later.
+ */
+ if ( upp ) {
+ *upp |= dma_wr;
+ /*
+ * Place buffer on receive queue waiting for RX_DMA
+ */
+ if ( IF_QFULL ( &eup->eu_rxqueue ) ) {
+ /*
+ * We haven't done anything we can't back out
+ * of. Drop request and service it next time.
+ * We've inserted the DMA list but it's not
+ * valid until we advance the RX_WR pointer,
+ * thus it's okay to bail here...
+ */
+ eup->eu_stats.eni_st_drv.drv_rv_rxq++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_do_service: RX drain queue full. Postponing servicing.\n" );
+#endif
+ KB_FREEALL ( m );
+ /*
+ * Clear the IN_SERVICE indicator for this VCC.
+ */
+ vct->vci_control &= ~VCI_IN_SERVICE;
+ return;
+ } else {
+ IF_ENQUEUE ( &eup->eu_rxqueue, m );
+ /*
+ * Advance the RX_WR pointer to cause
+ * the adapter to work on this DMA list.
+ */
+ eup->eu_midway[MIDWAY_RX_WR] = dma_wr;
+ }
+ }
+ /*
+ * Advance our notion of where the next PDU
+ * should start.
+ */
+ rdptr = (rdptr + n_cells*WORDS_PER_CELL + 1)
+ & (mask-1);
+ evp->ev_rxpos = rdptr;
+
+ /*
+ * Increment cells/pdu received stats.
+ */
+ eup->eu_stats.eni_st_atm.atm_rcvd += n_cells;
+ if ( aal5 ) {
+ eup->eu_stats.eni_st_aal5.aal5_rcvd += n_cells;
+ eup->eu_stats.eni_st_aal5.aal5_pdu_rcvd++;
+ } else {
+ eup->eu_stats.eni_st_aal0.aal0_rcvd += n_cells;
+ }
+
+ /*
+ * Continue processing PDUs on this same VCI
+ */
+ }
+
+next_vcc:
+ /*
+ * Advance to next entry in the service_list.
+ */
+ eup->eu_servread = (eup->eu_servread + 1) & SVC_SIZE_MASK;
+
+ /*
+ * And clear the IN_SERVICE indicator for this VCC.
+ */
+ vct->vci_control &= ~VCI_IN_SERVICE;
+ }
+ return;
+}
+
+/*
+ * Drain Receive queue
+ *
+ * As we build DMA lists to move PDUs from adapter buffers into host
+ * buffers, we place the request on a private ifqueue so that we can
+ * free any resources AFTER we know they've been successfully DMAed.
+ * As part of the service processing, we record the PDUs start and stop
+ * entries in the DMA list, and prevent wrapping. When we pull the top
+ * entry off, we simply check that the current DMA location is outside
+ * this PDU and if so, it's okay to free things.
+ *
+ * Arguments:
+ * eup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+eni_recv_drain ( eup )
+ Eni_unit *eup;
+{
+ KBuffer *m;
+ Eni_vcc *evp;
+ struct vccb *vcp;
+ u_long vcc;
+ u_long DMA_Rdptr;
+ u_long dma_wrp;
+ u_long start, stop;
+ int que = 0;
+ int s;
+
+ s = splimp();
+ /* Pop first buffer */
+ IF_DEQUEUE ( &eup->eu_rxqueue, m );
+ while ( m ) {
+ u_long *up;
+ u_long pdulen;
+
+ KB_DATASTART ( m, up, u_long * );
+
+ /*
+ * Grab the VCI number
+ */
+ vcc = *up++;
+
+ /*
+ * Check to see if we can process this buffer yet.
+ */
+ /* Get current DMA_Rdptr */
+ DMA_Rdptr = eup->eu_midway[MIDWAY_RX_RD];
+ /* Boundaries for first buffer */
+ dma_wrp = *up++;
+ start = dma_wrp >> 16;
+ stop = dma_wrp & 0xffff;
+ /*
+ * Start should not equal stop because that would
+ * mean we tried inserting a NULL DMA list.
+ */
+ if ( start > stop ) { /* We wrapped */
+ if ( !(DMA_Rdptr >= stop && DMA_Rdptr < start) ) {
+ IF_PREPEND ( &eup->eu_rxqueue, m );
+ goto finish;
+ }
+ } else {
+ if ( DMA_Rdptr < stop && DMA_Rdptr >= start ) {
+ IF_PREPEND ( &eup->eu_rxqueue, m );
+ goto finish;
+ }
+ }
+ /*
+ * Adapter is finished with this buffer, we can
+ * continue processing it now.
+ */
+
+ /*
+ * Locate incoming VCC for this PDU
+ */
+ evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,
+ 0, vcc, VCC_IN );
+
+ if ( evp == NULL ) {
+ eup->eu_stats.eni_st_drv.drv_rv_novcc++;
+ KB_FREEALL ( m );
+ goto next_buffer;
+ }
+
+#ifdef DIAGNOSTIC
+ if ( eni_pdu_print )
+ atm_dev_pdu_print ( (Cmn_unit *)eup, (Cmn_vcc *)evp, m,
+ "eni_stack_drain" );
+#endif
+
+ /*
+ * Grab theoretical PDU length
+ */
+ pdulen = *up++;
+
+ /*
+ * Quick, count the PDU
+ */
+ eup->eu_pif.pif_ipdus++;
+ eup->eu_pif.pif_ibytes += pdulen;
+ if ( evp ) {
+ vcp = evp->ev_connvc->cvc_vcc;
+ if ( vcp ) {
+ vcp->vc_ipdus++;
+ vcp->vc_ibytes += pdulen;
+ if ( vcp->vc_nif ) {
+ vcp->vc_nif->nif_ibytes += pdulen;
+ vcp->vc_nif->nif_if.if_ipackets++;
+#if (defined(BSD) && (BSD >= 199103))
+ vcp->vc_nif->nif_if.if_ibytes += pdulen;
+#endif
+ }
+ }
+ }
+
+ /*
+ * Advance DMA write allowable pointer
+ */
+ eup->eu_rxdmawr = stop;
+
+ /*
+ * Get packet PDU length
+ */
+ KB_PLENGET ( m, pdulen );
+
+ /*
+ * Only try queueing this if there is data
+ * to be handed up to the next layer. Errors
+ * such as CRC and VC trashing will get us this
+ * far to advance pointers, etc., but the PDU
+ * length will be zero.
+ */
+ if ( pdulen ) {
+ /*
+ * We saved three words back in eni_do_service()
+ * to use for callback. Since the core only
+ * expects two words, skip over the first one.
+ * Then, reset up pointer to start of buffer data
+ * area and write the callback info.
+ */
+ KB_HEADADJ ( m, -sizeof(u_long) );
+ KB_DATASTART ( m, up, u_long * );
+ *((int *)up) = (int)eni_recv_stack;
+ up++;
+ *((int *)up) = (int)evp;
+ /*
+ * Schedule callback
+ */
+ if ( !IF_QFULL ( &atm_intrq ) ) {
+ que++;
+ IF_ENQUEUE ( &atm_intrq, m );
+ } else {
+ eup->eu_stats.eni_st_drv.drv_rv_intrq++;
+ eup->eu_pif.pif_ierrors++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+"eni_receive_drain: ATM_INTRQ is full. Unable to pass up stack.\n" );
+#endif
+ KB_FREEALL ( m );
+ }
+ } else {
+ /*
+ * Free zero-length buffer
+ */
+ KB_FREEALL(m);
+ }
+
+next_buffer:
+ /*
+ * Look for next buffer
+ */
+ IF_DEQUEUE ( &eup->eu_rxqueue, m );
+ }
+finish:
+ (void) splx(s);
+
+ /*
+ * If we found any completed buffers, schedule a call into
+ * the kernel to process the atm_intrq.
+ */
+ if ( que )
+ SCHED_ATM;
+
+ return;
+
+}
+
+/*
+ * Pass incoming PDU up Stack
+ *
+ * This function is called via the core ATM interrupt queue callback
+ * set in eni_recv_drain(). It will pass the supplied incoming
+ * PDU up the incoming VCC's stack.
+ *
+ * Arguments:
+ * tok token to identify stack instantiation
+ * m pointer to incoming PDU buffer chain
+ *
+ * Returns:
+ * none
+ */
+static void
+eni_recv_stack ( tok, m )
+ void *tok;
+ KBuffer *m;
+{
+ Eni_vcc *evp = (Eni_vcc *)tok;
+ int err;
+
+ /*
+ * This should never happen now but if it does and we don't stop it,
+ * we end up panic'ing in netatm when trying to pull a function
+ * pointer and token value out of a buffer with address zero.
+ */
+ if ( !m ) {
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_recv_stack: NULL buffer, tok = 0x%x\n", tok );
+#endif
+ return;
+ }
+
+ /*
+ * Send the data up the stack
+ */
+ STACK_CALL ( CPCS_UNITDATA_SIG, evp->ev_upper,
+ (void *)evp->ev_toku, evp->ev_connvc, (int)m, 0, err );
+ if ( err ) {
+ KB_FREEALL ( m );
+ }
+
+ return;
+}
+
diff --git a/sys/dev/hea/eni_stats.h b/sys/dev/hea/eni_stats.h
new file mode 100644
index 0000000..1f2a413
--- /dev/null
+++ b/sys/dev/hea/eni_stats.h
@@ -0,0 +1,134 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_stats.h,v 1.6 1998/08/26 23:28:54 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Defines for statistics
+ *
+ */
+
+#ifndef _ENI_ENI_STATS_H
+#define _ENI_ENI_STATS_H
+
+struct eni_stats_oc3 {
+ u_long oc3_sect_bip8; /* Section 8-bit intrlv parity errors */
+ u_long oc3_path_bip8; /* Path 8-bit intrlv parity errors */
+ u_long oc3_line_bip24; /* Line 24-bit intrlv parity errors */
+ u_long oc3_line_febe; /* Line far-end block errors */
+ u_long oc3_path_febe; /* Path far-end block errors */
+ u_long oc3_hec_corr; /* Correctable HEC errors */
+ u_long oc3_hec_uncorr; /* Uncorrectable HEC errors */
+ u_long oc3_pad; /* Pad to quad-word boundary */
+};
+typedef struct eni_stats_oc3 Eni_Stats_oc3;
+
+struct eni_stats_atm {
+ u_long atm_xmit; /* Cells transmitted */
+ u_long atm_rcvd; /* Cells received */
+ u_long atm_pad[2]; /* Pad to quad-word boundary */
+};
+typedef struct eni_stats_atm Eni_Stats_atm;
+
+struct eni_stats_aal0 {
+ u_long aal0_xmit; /* Cells transmitted */
+ u_long aal0_rcvd; /* Cells received */
+ u_long aal0_drops; /* Cells dropped */
+ u_long aal0_pad; /* Pad to quad-word boundary */
+};
+typedef struct eni_stats_aal0 Eni_Stats_aal0;
+
+struct eni_stats_aal5 {
+ u_long aal5_xmit; /* Cells transmitted */
+ u_long aal5_rcvd; /* Cells received */
+ u_long aal5_crc_len; /* Cells with CRC/length errors */
+ u_long aal5_drops; /* Cell drops */
+ u_long aal5_pdu_xmit; /* CS PDUs transmitted */
+ u_long aal5_pdu_rcvd; /* CS PDUs received */
+ u_long aal5_pdu_crc; /* CS PDUs with CRC errors */
+ u_long aal5_pdu_errs; /* CS layer protocol errors */
+ u_long aal5_pdu_drops; /* CS PDUs dropped */
+ u_long aal5_pad[3]; /* Pad to quad-word boundary */
+};
+typedef struct eni_stats_aal5 Eni_Stats_aal5;
+
+struct eni_stats_driver {
+ /*
+ * Adapter memory allocator stats
+ */
+ u_long drv_mm_toobig; /* Size larger then adapter supports */
+ u_long drv_mm_nodesc; /* No memory area descriptor avail */
+ u_long drv_mm_nobuf; /* No memory buffer available */
+ u_long drv_mm_notuse; /* Calling free() on free buffer */
+ u_long drv_mm_notfnd; /* Couldn't find descr for free() */
+
+ /*
+ * VCM sats
+ */
+ u_long drv_vc_maxpdu; /* Requested PDU size too large */
+ u_long drv_vc_badrng; /* VPI and/or VCI too large */
+
+ /*
+ * Receive stats
+ */
+ u_long drv_rv_norsc; /* No buffer for resource pointers */
+ u_long drv_rv_nobufs; /* No buffers for PDU */
+ u_long drv_rv_nodma; /* No room in RXDMA list */
+ u_long drv_rv_rxq; /* No room in local rxqueue */
+ u_long drv_rv_novcc; /* Draining PDU on closed VCC */
+ u_long drv_rv_intrq; /* No room in atm_intrq */
+ u_long drv_rv_null; /* Trying to pass null PDU up stack */
+ u_long drv_rv_segdma; /* No DMA address */
+
+ /*
+ * Transmit stats
+ */
+ u_long drv_xm_segdma; /* No DMA address */
+ u_long drv_xm_segnoal; /* Non-aligned segment */
+ u_long drv_xm_seglen; /* Padded length segment */
+ u_long drv_xm_maxpdu; /* Too many segments - dropped */
+ u_long drv_xm_nobuf; /* No space in TX buffer - dropped */
+ u_long drv_xm_norsc; /* No buffers for resource pointers */
+ u_long drv_xm_nodma; /* No space in TXDMA list */
+ u_long drv_xm_dmaovfl; /* DMA overflow */
+
+};
+typedef struct eni_stats_driver Eni_Stats_drv;
+
+struct eni_stats {
+ Eni_Stats_oc3 eni_st_oc3; /* OC3 layer stats */
+ Eni_Stats_atm eni_st_atm; /* ATM layer stats */
+ Eni_Stats_aal0 eni_st_aal0; /* AAL0 layer stats */
+ Eni_Stats_aal5 eni_st_aal5; /* AAL5 layer stats */
+ Eni_Stats_drv eni_st_drv; /* Driver stats */
+};
+typedef struct eni_stats Eni_stats;
+
+#endif /* _ENI_ENI_STATS_H */
diff --git a/sys/dev/hea/eni_suni.h b/sys/dev/hea/eni_suni.h
new file mode 100644
index 0000000..3a0b0f7
--- /dev/null
+++ b/sys/dev/hea/eni_suni.h
@@ -0,0 +1,78 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_suni.h,v 1.2 1997/05/06 22:08:17 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Defines for SUNI chip
+ *
+ */
+
+#ifndef _ENI_ENI_SUNI_H
+#define _ENI_ENI_SUNI_H
+
+/*
+ * Interrupt bits in SUNI Master Interrupt Status Reg
+ */
+#define SUNI_RSOPI 0x01
+#define SUNI_RLOPI 0x02
+#define SUNI_RPOPI 0x04
+#define SUNI_RACPI 0x08
+#define SUNI_TACPI 0x10
+#define SUNI_RDOOLI 0x20
+#define SUNI_LCDI 0x40
+#define SUNI_TROOLI 0x80
+
+/*
+ * SUNI Register numbers
+ */
+#define SUNI_MASTER_REG 0x00 /* Master reset and ID */
+#define SUNI_IS_REG 0x02 /* Master Interrupt Status */
+#define SUNI_CLOCK_REG 0x06 /* Clock synth/control/status */
+#define SUNI_RSOP_REG 0x10 /* RSOP control/Interrupt Status */
+#define SUNI_SECT_BIP_REG 0x12
+#define SUNI_RLOP_REG 0x18 /* RLOP control/Interrupt Status */
+#define SUNI_LINE_BIP_REG 0x1A
+#define SUNI_LINE_FEBE_REG 0x1D
+#define SUNI_RPOP_IS_REG 0x31 /* RPOP Interrupt Status */
+#define SUNI_PATH_BIP_REG 0x38
+#define SUNI_PATH_FEBE_REG 0x3A
+#define SUNI_RACP_REG 0x50 /* RACP control/status */
+#define SUNI_HECS_REG 0x54
+#define SUNI_UHECS_REG 0x55
+#define SUNI_TACP_REG 0x60 /* TACP control/status */
+
+/*
+ * Delay timer to allow SUNI statistic registers to load
+ */
+#define SUNI_DELAY 10
+
+#endif /* _ENI_ENI_SUNI_H */
+
diff --git a/sys/dev/hea/eni_transmit.c b/sys/dev/hea/eni_transmit.c
new file mode 100644
index 0000000..25066df
--- /dev/null
+++ b/sys/dev/hea/eni_transmit.c
@@ -0,0 +1,823 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_transmit.c,v 1.20 1998/07/17 20:20:16 root Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Transmit queue management and PDU output processing
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_transmit.c,v 1.20 1998/07/17 20:20:16 root Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+/*
+ * Make a variable which controls printing of PDUs
+ * as they travel through the driver.
+ */
+#ifdef DIAGNOSTIC
+int eni_pdu_print = 0;
+#endif
+
+/*
+ * Some PCI chipsets do not handle one or more of the 8WORD or
+ * 4WORD DMA transfer sizes. Default to using only 1WORD transfer
+ * sizes unless the user wishes to experiment.
+ *
+ * Make sure that these have to be changed here in this module.
+ */
+#define DMA_USE_8WORD
+#define DMA_USE_4WORD
+
+/*
+ * Create a DMA list entry
+ *
+ * DMA entries consist of a control word and a physical address.
+ * Control words are comprised of a DMA type, a count of type transfers
+ * to occur, and a variable which for TX requests is the TX channel
+ * number and for RX requests is the VCC number.
+ *
+ * Arguments:
+ * eup pointer to unit structure
+ * rx set if receiving
+ * dma_list pointer to DMA list structure
+ * list_size length of DMA list structure
+ * idx pointer to current list entry
+ * val TX channel or RX vcc
+ * addr virtual DMA address of data buffer
+ * size size in bytes of DMA request to be built
+ *
+ * Returns:
+ * dma_list updated with new entries
+ * idx points to next list entry
+ * -1 no room in DMA list structure or DMA_GET_ADDR failed
+ */
+int
+eni_set_dma ( eup, rx, dma_list, list_size, idx, val, addr, size )
+Eni_unit *eup;
+u_long *dma_list;
+int list_size;
+long *idx;
+int val;
+u_long addr;
+int size;
+{
+ int dsize; /* Size of current DMA request */
+
+ /*
+ * Round up to multiple of word and convert to number
+ * of words rather then number of bytes.
+ */
+ size = ( size + 3 ) >> 2;
+
+#ifdef DMA_USE_8WORD
+ /*
+ * Check for room in DMA list - we need two entires
+ */
+ if ( *idx + 2 >= list_size )
+ return ( -1 );
+
+ /*
+ * Here is the big win. Move as much data possible with
+ * n 8WORD DMAs.
+ */
+ /*
+ * Check if we can do one or more 8WORD DMAs
+ */
+ dsize = size & ~7;
+ if ( dsize ) {
+ dma_list[(*idx)++] = ( dsize >> 3 ) << DMA_COUNT_SHIFT |
+ val << DMA_VCC_SHIFT | DMA_8WORD;
+ dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, dsize, 0, 0 );
+ if ( dma_list[*idx] == NULL ) {
+ if ( rx )
+ eup->eu_stats.eni_st_drv.drv_rv_segdma++;
+ else
+ eup->eu_stats.eni_st_drv.drv_xm_segdma++;
+ return ( -1 ); /* DMA_GET_ADDR failed */
+ } else
+ (*idx)++; /* increment index */
+ /*
+ * Adjust addr and size
+ */
+ addr += dsize << 2;
+ size &= 7;
+ }
+#endif /* DMA_USE_8WORD */
+
+#ifdef DMA_USE_4WORD
+ /*
+ * Check for room in DMA list - we need two entries
+ */
+ if ( *idx + 2 >= list_size )
+ return ( -1 );
+
+ /*
+ * Kindof a tossup from this point on. Since we hacked as many
+ * 8WORD DMAs off as possible, we are left with 0-7 words
+ * of remaining data. We could do upto one 4WORD with 0-3
+ * words left, or upto three 2WORDS with 0-1 words left,
+ * or upto seven WORDS with nothing left. Someday we should
+ * experiment with performance and see if any particular
+ * combination is a better win then some other...
+ */
+ /*
+ * Check if we can do one or more 4WORD DMAs
+ */
+ dsize = size & ~3;
+ if ( dsize ) {
+ dma_list[(*idx)++] = ( dsize >> 2 ) << DMA_COUNT_SHIFT |
+ val << DMA_VCC_SHIFT | DMA_4WORD;
+ dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, dsize, 0, 0 );
+ if ( dma_list[*idx] == NULL ) {
+ if ( rx )
+ eup->eu_stats.eni_st_drv.drv_rv_segdma++;
+ else
+ eup->eu_stats.eni_st_drv.drv_xm_segdma++;
+ return ( -1 ); /* DMA_GET_ADDR failed */
+ } else
+ (*idx)++; /* increment index */
+ /*
+ * Adjust addr and size
+ */
+ addr += dsize << 2;
+ size &= 3;
+ }
+#endif /* DMA_USE_4WORD */
+
+ /*
+ * Check for room in DMA list - we need two entries
+ */
+ if ( *idx + 2 >= list_size )
+ return ( -1 );
+
+ /*
+ * Hard to know if one 2WORD and 0/1 WORD DMA would be better
+ * then 2/3 WORD DMAs. For now, skip 2WORD DMAs in favor of
+ * WORD DMAs.
+ */
+
+ /*
+ * Finish remaining size a 1WORD DMAs
+ */
+ if ( size ) {
+ dma_list[(*idx)++] = ( size ) << DMA_COUNT_SHIFT |
+ val << DMA_VCC_SHIFT | DMA_WORD;
+ dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, size, 0, 0 );
+ if ( dma_list[*idx] == NULL ) {
+ if ( rx )
+ eup->eu_stats.eni_st_drv.drv_rv_segdma++;
+ else
+ eup->eu_stats.eni_st_drv.drv_xm_segdma++;
+ return ( -1 ); /* DMA_GET_ADDR failed */
+ } else
+ (*idx)++; /* increment index */
+ }
+
+ /*
+ * Inserted descriptor okay
+ */
+ return 0;
+}
+
+/*
+ * Drain Transmit queue
+ *
+ * As PDUs are given to the adapter to be transmitted, we
+ * place them into a private ifqueue so that we can free
+ * any resources AFTER we know they've been successfully DMAed.
+ * As part of the output processing, we record the PDUs start
+ * and stop entries in the DMA list, and prevent wrapping. When
+ * we pull the top element off, we simply check that the current
+ * DMA location is outside this PDU and if so, it's okay to free
+ * things.
+ *
+ * PDUs are always in ascending order in the queue.
+ *
+ * Arguments:
+ * eup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+eni_xmit_drain ( eup )
+ Eni_unit *eup;
+{
+ KBuffer *m;
+ Eni_vcc *evp;
+ struct vccb *vcp;
+ u_long pdulen;
+ u_long start, stop;
+ u_long dmap;
+ int s = splimp();
+
+ /*
+ * Pull the top element (PDU) off
+ */
+ IF_DEQUEUE ( &eup->eu_txqueue, m );
+ /*
+ * As long as there are valid elements
+ */
+ while ( m ) {
+ u_long *up;
+
+ /*
+ * Find start of buffer
+ */
+ KB_DATASTART ( m, up, u_long * );
+
+ /*
+ * First word is the VCC for this PDU
+ */
+ /*
+ * NOTE: There is a potential problem here in that
+ * if the VCC is closed after this buffer was transmitted
+ * but before we get here, that while evp is non-null,
+ * it will not reference a valid vccb. We need to either
+ * delay closing the VCC until all references are removed
+ * from the drain stacks, actually go through the drain
+ * stacks and remove any references, or find someway of
+ * indicating that this vccb is nolonger usable.
+ */
+ evp = (Eni_vcc *)*up++;
+ /*
+ * Second word is the start and stop DMA pointers
+ */
+ start = *up >> 16;
+ stop = *up++ & 0xffff;
+ /*
+ * Find out where the TX engine is at
+ */
+ dmap = eup->eu_midway[MIDWAY_TX_RD];
+ /*
+ * Check to see if TX engine has processed this
+ * PDU yet. Remember that everything is circular
+ * and that stop might be less than start numerically.
+ */
+ if ( start > stop ) {
+ if ( !(dmap >= stop && dmap < start) ) {
+ /*
+ * Haven't finished this PDU yet - replace
+ * it as the head of list.
+ */
+ IF_PREPEND ( &eup->eu_txqueue, m );
+ /*
+ * If this one isn't done, none of the others
+ * are either.
+ */
+ (void) splx(s);
+ return;
+ }
+ } else {
+ if ( dmap < stop && dmap >= start ) {
+ /*
+ * Haven't finished this PDU yet - replace
+ * it as the head of list.
+ */
+ IF_PREPEND ( &eup->eu_txqueue, m );
+ /*
+ * If this one isn't done, none of the others
+ * are either.
+ */
+ (void) splx(s);
+ return;
+ }
+ }
+
+ /*
+ * Count the PDU stats for this interface
+ */
+ eup->eu_pif.pif_opdus++;
+ /*
+ * Third word is PDU length from eni_output().
+ */
+ pdulen = *up++;
+ eup->eu_txfirst = (eup->eu_txfirst + *up) &
+ (eup->eu_txsize - 1);
+ eup->eu_pif.pif_obytes += pdulen;
+
+ /*
+ * Now lookup the VCC entry and counts the stats for
+ * this VC.
+ */
+ if ( evp ) {
+ vcp = evp->ev_connvc->cvc_vcc;
+ if ( vcp ) {
+ vcp->vc_opdus++;
+ vcp->vc_obytes += pdulen;
+ /*
+ * If we also have a network interface, count the PDU
+ * there also.
+ */
+ if ( vcp->vc_nif ) {
+ vcp->vc_nif->nif_obytes += pdulen;
+ vcp->vc_nif->nif_if.if_opackets++;
+#if (defined(BSD) && (BSD >= 199103))
+ vcp->vc_nif->nif_if.if_obytes += pdulen;
+#endif
+ }
+ }
+ }
+ /*
+ * Free the buffer chain
+ */
+ KB_FREEALL ( m );
+
+ /*
+ * Advance DMA write okay pointer
+ */
+ eup->eu_txdmawr = stop;
+
+ /*
+ * Look for next completed transmit PDU
+ */
+ IF_DEQUEUE ( &eup->eu_txqueue, m );
+ }
+ /*
+ * We've drained the queue...
+ */
+ (void) splx(s);
+ return;
+}
+
+/*
+ * Output a PDU
+ *
+ * This function is called via the common driver code after receiving a
+ * stack *_DATA* command. The common code has already validated most of
+ * the request so we just need to check a few more ENI-specific details.
+ * Then we just build a segmentation structure for the PDU and place the
+ * address into the DMA_Transmit_queue.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ * m pointer to output PDU buffer chain head
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+eni_output ( cup, cvp, m )
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+ KBuffer *m;
+{
+ Eni_unit *eup = (Eni_unit *)cup;
+ Eni_vcc *evp = (Eni_vcc *)cvp;
+ int s, s2;
+ int pdulen = 0;
+ u_long size;
+ u_long buf_avail;
+ u_long dma_rd, dma_wr;
+ u_long dma[TEMP_DMA_SIZE];
+ int aal5, i;
+ long j;
+ u_long dma_avail;
+ u_long dma_start;
+ Eni_mem tx_send;
+ u_long *up;
+ KBuffer *m0 = m, *m1, *mprev = NULL;
+ caddr_t cp, bfr;
+ u_int len, align;
+ int compressed = 0;
+
+#ifdef DIAGNOSTIC
+ if ( eni_pdu_print )
+ atm_dev_pdu_print ( cup, cvp, m, "eni output" );
+#endif
+
+ /*
+ * Re-entry point for after buffer compression (if needed)
+ */
+retry:
+
+ /*
+ * We can avoid traversing the buffer list twice by building
+ * the middle (minus header and trailer) dma list at the
+ * same time we massage address and size alignments. Since
+ * this list remains local until we determine we've enough
+ * room, we're not going to trash anything by not checking
+ * sizes, etc. yet. Skip first entry to be used later to skip
+ * descriptor word.
+ */
+ j = 2;
+ /*
+ * Do data positioning for address and length alignment
+ */
+ while ( m ) {
+ u_long buf_addr; /* For passing addr to eni_set_dma() */
+
+ /*
+ * Get rid of any zero length buffers
+ */
+ if ( KB_LEN ( m ) == 0 ) {
+ if ( mprev ) {
+ KB_UNLINK ( m, mprev, m1 );
+ } else {
+ KB_UNLINKHEAD ( m, m1 );
+ m0 = m1;
+ }
+ m = m1;
+ continue;
+ }
+ /*
+ * Get start of data onto full-word alignment
+ */
+ KB_DATASTART ( m, cp, caddr_t );
+ if ( align = ((u_int)cp) & (sizeof(u_long)-1)) {
+ /*
+ * Gotta slide the data up
+ */
+ eup->eu_stats.eni_st_drv.drv_xm_segnoal;
+ bfr = cp - align;
+ KM_COPY ( cp, bfr, KB_LEN ( m ) );
+ KB_HEADMOVE ( m, -align );
+ } else {
+ /*
+ * Data already aligned
+ */
+ bfr = cp;
+ }
+ /*
+ * Now work on getting the data length correct
+ */
+ len = KB_LEN ( m );
+ while ( ( align = ( len & (sizeof(u_long)-1))) &&
+ (m1 = KB_NEXT ( m ) ) ) {
+
+ /*
+ * Have to move some data from following buffer(s)
+ * to word-fill this buffer
+ */
+ u_int ncopy = MIN ( sizeof(u_long) - align,
+ KB_LEN ( m1 ) );
+
+ if ( ncopy ) {
+ /*
+ * Move data to current buffer
+ */
+ caddr_t dest;
+
+ eup->eu_stats.eni_st_drv.drv_xm_seglen++;
+ KB_DATASTART ( m1, cp, caddr_t );
+ dest = bfr + len;
+ KB_HEADADJ ( m1, -ncopy );
+ KB_TAILADJ ( m, ncopy );
+ len += ncopy;
+ while ( ncopy-- ) {
+ *dest++ = *cp++;
+ }
+ }
+
+ /*
+ * If we've drained the buffer, free it
+ */
+ if ( KB_LEN ( m1 ) == 0 ) {
+ KBuffer *m2;
+
+ KB_UNLINK ( m1, m, m2 );
+ }
+ }
+
+ /*
+ * Address and size are now aligned. Build dma list
+ * using TX channel 0. Also, round length up to a word
+ * size which should only effect the last buffer in the
+ * chain. This works because the PDU length is maintained
+ * seperately and we're not really adjusting the buffer's
+ * idea of its length.
+ */
+ KB_DATASTART ( m, buf_addr, u_long );
+ if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0,
+ buf_addr, KB_LEN ( m ) ) < 0 ) {
+ /*
+ * Failed to build DMA list. First, we'll try to
+ * compress the buffer chain into a smaller number
+ * of buffers. After compressing, we'll try to send
+ * the new buffer chain. If we still fail, then
+ * we'll drop the pdu.
+ */
+ if ( compressed ) {
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_output: eni_set_dma failed\n" );
+#endif
+ eup->eu_pif.pif_oerrors++;
+ KB_FREEALL ( m0 );
+ return;
+ }
+
+ eup->eu_stats.eni_st_drv.drv_xm_maxpdu++;
+
+ m = atm_dev_compress ( m0 );
+ if ( m == NULL ) {
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_output: atm_dev_compress() failed\n" );
+#endif
+ eup->eu_pif.pif_oerrors++;
+ return;
+ }
+
+ /*
+ * Reset to new head of buffer chain
+ */
+ m0 = m;
+
+ /*
+ * Indicate we've been through here
+ */
+ compressed = 1;
+
+ /*
+ * Retry to build the DMA descriptors for the newly
+ * compressed buffer chain
+ */
+ goto retry;
+
+ }
+
+ /*
+ * Now count the length
+ */
+ pdulen += KB_LEN ( m );
+
+ /*
+ * Bump counters and get ready for next buffer
+ */
+ mprev = m;
+ m = KB_NEXT ( m );
+ }
+
+ /*
+ * Get a buffer to use in a private queue so that we can
+ * reclaim resources after the DMA has finished.
+ */
+ KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
+ if ( m ) {
+ /*
+ * Link the PDU onto our new head
+ */
+ KB_NEXT ( m ) = m0;
+ } else {
+ /*
+ * Drop this PDU and let the sender try again.
+ */
+ eup->eu_stats.eni_st_drv.drv_xm_norsc++;
+#ifdef DO_LOG
+ log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n");
+#endif
+ eup->eu_pif.pif_oerrors++;
+ KB_FREEALL ( m0 );
+ return;
+ }
+
+ s = splnet();
+
+ /*
+ * Calculate size of buffer necessary to store PDU. If this
+ * is an AAL5 PDU, we'll need to know where to stuff the length
+ * value in the trailer.
+ */
+ /*
+ * AAL5 PDUs need an extra two words for control/length and
+ * CRC. Check for AAL5 and add requirements here.
+ */
+ if (aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5))
+ size = pdulen + 2 * sizeof(long);
+ else
+ size = pdulen;
+ /*
+ * Pad to next complete cell boundary
+ */
+ size += (BYTES_PER_CELL - 1);
+ size -= size % BYTES_PER_CELL;
+ /*
+ * Convert size to words and add 2 words overhead for every
+ * PDU (descriptor and cell header).
+ */
+ size = (size >> 2) + 2;
+
+ /*
+ * First, check to see if there's enough buffer space to
+ * store the PDU. We do this by checking to see if the size
+ * required crosses the eu_txfirst pointer. However, we don't
+ * want to exactly fill the buffer, because we won't be able to
+ * distinguish between a full and empty buffer.
+ */
+ if ( eup->eu_txpos == eup->eu_txfirst )
+ buf_avail = eup->eu_txsize;
+ else
+ if ( eup->eu_txpos > eup->eu_txfirst )
+ buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst );
+ else
+ buf_avail = eup->eu_txfirst - eup->eu_txpos;
+
+ if ( size >= buf_avail )
+ {
+ /*
+ * No buffer space in the adapter to store this PDU.
+ * Drop PDU and return.
+ */
+ eup->eu_stats.eni_st_drv.drv_xm_nobuf++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_output: not enough room in buffer\n" );
+#endif
+ eup->eu_pif.pif_oerrors++;
+ KB_FREEALL ( m );
+ (void) splx(s);
+ return;
+ }
+
+ /*
+ * Find out where current DMA pointers are at
+ */
+ dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR];
+ dma_rd = eup->eu_midway[MIDWAY_TX_RD];
+
+ /*
+ * Figure out how much DMA room we have available
+ */
+ if ( dma_rd == dma_wr ) { /* Queue is empty */
+ dma_avail = DMA_LIST_SIZE;
+ } else {
+ dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
+ & ( DMA_LIST_SIZE - 1 );
+ }
+ /*
+ * Check to see if we can describe this PDU or if we're:
+ * out of room, will wrap past recovered resources.
+ */
+ if ( dma_avail < (j / 2 + 4) ||
+ ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) {
+ /*
+ * No space to insert DMA list into queue. Drop this PDU.
+ */
+ eup->eu_stats.eni_st_drv.drv_xm_nodma++;
+#ifdef DO_LOG
+ log ( LOG_ERR,
+ "eni_output: not enough room in DMA queue\n" );
+#endif
+ eup->eu_pif.pif_oerrors++;
+ KB_FREEALL( m );
+ (void) splx(s);
+ return;
+ }
+
+ /*
+ * Create DMA descriptor for header. There is a descriptor word
+ * and also a cell header word which we'll set manually.
+ */
+ dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) <<
+ DMA_COUNT_SHIFT) | DMA_JK;
+ dma[1] = 0;
+
+ /*
+ * JK for AAL5 trailer. Set END bit as well.
+ */
+ if ( aal5 ) {
+ dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) <<
+ DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK;
+ dma[j++] = 0;
+ } else {
+ dma[j-2] |= DMA_END_BIT; /* Backup and set END bit */
+ }
+
+ /*
+ * Find out where in adapter memory this TX buffer starts.
+ */
+ tx_send = (Eni_mem)
+ ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) +
+ (int)eup->eu_ram);
+
+ /*
+ * Set descriptor word
+ */
+ tx_send[eup->eu_txpos] =
+ (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0)
+ | (size / WORDS_PER_CELL);
+ /*
+ * Set cell header
+ */
+ tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] =
+ evp->ev_connvc->cvc_vcc->vc_vci << 4;
+
+ /*
+ * We've got all our resources, count the stats
+ */
+ if ( aal5 ) {
+ /*
+ * If this is an AAL5 PDU, we need to set the length
+ */
+ tx_send[(eup->eu_txpos+size-2) &
+ (eup->eu_txsize-1)] = pdulen;
+ /*
+ * Increment AAL5 stats
+ */
+ eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++;
+ eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL;
+ } else {
+ /*
+ * Increment AAL0 stats
+ */
+ eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL;
+ }
+ /*
+ * Increment ATM stats
+ */
+ eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL;
+
+ /*
+ * Store the DMA list
+ */
+ j = j >> 1;
+ for ( i = 0; i < j; i++ ) {
+ eup->eu_txdma[dma_wr*2] = dma[i*2];
+ eup->eu_txdma[dma_wr*2+1] = dma[i*2+1];
+ dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1);
+ }
+
+ /*
+ * Build drain buffer
+ *
+ * We toss four words in to help keep track of this
+ * PDU. The first is a pointer to the VC control block
+ * so we can find which VCI this went out on, the second
+ * is the start and stop pointers for the DMA list which
+ * describes this PDU, the third is the PDU length
+ * since we'll want to know that for stats gathering,
+ * and the fourth is the number of DMA words.
+ */
+ KB_DATASTART ( m, up, u_long * );
+ *up++ = (u_long)cvp;
+ *up++ = dma_start << 16 | dma_wr;
+ *up++ = pdulen;
+ *up = size;
+
+ /*
+ * Set length of our buffer
+ */
+ KB_LEN ( m ) = 4 * sizeof ( long );
+
+ /*
+ * Place buffers onto transmit queue for draining
+ */
+ s2 = splimp();
+ IF_ENQUEUE ( &eup->eu_txqueue, m );
+ (void) splx(s2);
+
+ /*
+ * Update next word to be stored
+ */
+ eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1));
+
+ /*
+ * Update MIDWAY_TX_WR pointer
+ */
+ eup->eu_midway[MIDWAY_TX_WR] = dma_wr;
+
+ (void) splx ( s );
+
+ return;
+}
+
diff --git a/sys/dev/hea/eni_var.h b/sys/dev/hea/eni_var.h
new file mode 100644
index 0000000..cc83a12
--- /dev/null
+++ b/sys/dev/hea/eni_var.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_var.h,v 1.4 1998/06/29 19:55:26 jpt Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Local driver include files and global declarations
+ *
+ */
+
+#ifndef _ENI_ENI_VAR_H
+#define _ENI_ENI_VAR_H
+
+/*
+ * Global function declarations
+ */
+ /* eni_buffer.c */
+int eni_init_memory __P((Eni_unit *));
+caddr_t eni_allocate_buffer __P((Eni_unit *, u_long *));
+void eni_free_buffer __P((Eni_unit *, caddr_t));
+
+ /* eni_if.c */
+int eni_atm_ioctl __P((int, caddr_t, caddr_t));
+void eni_zero_stats __P((Eni_unit *));
+
+ /* eni_init.c */
+int eni_init __P((Eni_unit *));
+
+ /* eni_intr.c */
+#if defined(BSD) && BSD < 199506
+int eni_intr __P((void *));
+#else
+void eni_intr __P((void *));
+#endif
+
+ /* eni_receive.c */
+void eni_do_service __P((Eni_unit *));
+void eni_recv_drain __P((Eni_unit *));
+
+ /* eni_transmit.c */
+int eni_set_dma __P((Eni_unit *, int, u_long *, int, long *, int, u_long, int ));
+void eni_output __P((Cmn_unit *, Cmn_vcc *, KBuffer *));
+void eni_xmit_drain __P((Eni_unit *));
+
+ /* eni_vcm.c */
+int eni_instvcc __P((Cmn_unit *, Cmn_vcc *));
+int eni_openvcc __P((Cmn_unit *, Cmn_vcc *));
+int eni_closevcc __P((Cmn_unit *, Cmn_vcc *));
+
+/*
+ * Global variable declarations
+ */
+extern Eni_unit *eni_units[];
+extern struct stack_defn *eni_services;
+extern struct sp_info eni_nif_pool;
+extern struct sp_info eni_vcc_pool;
+
+#endif /* _ENI_ENI_VAR_H */
diff --git a/sys/dev/hea/eni_vcm.c b/sys/dev/hea/eni_vcm.c
new file mode 100644
index 0000000..36c2a3d
--- /dev/null
+++ b/sys/dev/hea/eni_vcm.c
@@ -0,0 +1,283 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: eni_vcm.c,v 1.8 1998/06/29 23:03:18 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Virtual Channel Managment
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_vcm.c,v 1.8 1998/06/29 23:03:18 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+
+/*
+ * VCC Stack Instantiation
+ *
+ * This function is called via the common driver code during a device VCC
+ * stack instantiation. The common code has already validated some of
+ * the request so we just need to check a few more ENI-specific details.
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 instantiation successful
+ * err instantiation failed - reason indicated
+ *
+ */
+int
+eni_instvcc(cup, cvp)
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+{
+ Eni_unit *eup = (Eni_unit *)cup;
+ Eni_vcc *evp = (Eni_vcc *)cvp;
+ Atm_attributes *ap = &evp->ev_connvc->cvc_attr;
+
+ /*
+ * Validate requested AAL
+ */
+ switch (ap->aal.type) {
+
+ case ATM_AAL0:
+ break;
+
+ case ATM_AAL5:
+ if ((ap->aal.v.aal5.forward_max_SDU_size > ENI_IFF_MTU) ||
+ (ap->aal.v.aal5.backward_max_SDU_size > ENI_IFF_MTU)) {
+ eup->eu_stats.eni_st_drv.drv_vc_maxpdu++;
+ return (EINVAL);
+ }
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Open a VCC
+ *
+ * This function is called via the common driver code after receiving a
+ * stack *_INIT* command. The common code has already validated most of
+ * the request so we just need to check a few more ENI-specific details.
+ *
+ * Called at splimp.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 open sucessful
+ * err open failed
+ *
+ */
+int
+eni_openvcc ( cup, cvp )
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+{
+ Eni_unit *eup = (Eni_unit *)cup;
+ Eni_vcc *evp = (Eni_vcc *)cvp;
+ struct vccb *vcp = evp->ev_connvc->cvc_vcc;
+ int err = 0;
+
+ VCI_Table *vct;
+ int size;
+ int mode;
+ int nsize;
+
+ /*
+ * Validate the VPI and VCI values
+ */
+ if ( (vcp->vc_vpi > eup->eu_pif.pif_maxvpi) ||
+ (vcp->vc_vci > eup->eu_pif.pif_maxvci) ) {
+ eup->eu_stats.eni_st_drv.drv_vc_badrng++;
+ return ( EFAULT );
+ }
+
+ /*
+ * Check if this VCI is already active
+ */
+ vct = &eup->eu_vcitbl[ vcp->vc_vci ];
+ if ( vct->vci_control >> VCI_MODE_SHIFT != VCI_MODE_TRASH ) {
+ return ( EEXIST );
+ }
+
+ /*
+ * Allocate some permanent adapter memory for the reassembly
+ * buffer. Special case the signalling channel(s) buffer size.
+ * Otherwise, the buffer size will be based on whether this is
+ * a server or client card.
+ */
+ if ( vcp->vc_vci == UNI_SIG_VCI ) /* HACK */
+ size = RX_SIG_BSIZE;
+ else
+ size = (eup->eu_ramsize > MAX_CLIENT_RAM * ENI_BUF_PGSZ) ?
+ RX_SERVER_BSIZE * ENI_BUF_PGSZ :
+ RX_CLIENT_BSIZE * ENI_BUF_PGSZ;
+
+ if ( ( evp->ev_rxbuf = eni_allocate_buffer ( eup, (u_long *)&size ) )
+ == (caddr_t)NULL ) {
+ return ( ENOMEM );
+ }
+ evp->ev_rxpos = 0;
+
+ /*
+ * We only need to open incoming VCI's so outbound VCI's
+ * just get set to CVS_ACTIVE state.
+ */
+ if ( ( vcp->vc_type & VCC_IN ) == 0 ) {
+ /*
+ * Set the state and return - nothing else needs to be done.
+ */
+ evp->ev_state = CVS_ACTIVE;
+ return ( 0 );
+ }
+
+ /*
+ * Set the VCI Table entry to start receiving
+ */
+ mode = ( evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5
+ ? VCI_MODE_AAL5 : VCI_MODE_AAL0 );
+ size >>= ENI_LOC_PREDIV; /* Predivide by 256 WORDS */
+ for ( nsize = -1; size; nsize++ )
+ size >>= 1;
+
+ vct->vci_control = mode << VCI_MODE_SHIFT |
+ PTI_MODE_TRASH << VCI_PTI_SHIFT |
+ ( (u_int)(evp->ev_rxbuf) >> ENI_LOC_PREDIV ) << VCI_LOC_SHIFT |
+ nsize << VCI_SIZE_SHIFT;
+ vct->vci_descr = 0; /* Descr = Rdptr = 0 */
+ vct->vci_write = 0; /* WritePtr = CellCount = 0 */
+
+ /*
+ * Indicate VC active
+ */
+ evp->ev_state = CVS_ACTIVE;
+
+ return ( err );
+}
+
+/*
+ * Close a VCC
+ *
+ * This function is called via the common driver code after receiving a
+ * stack *_TERM* command. The common code has already validated most of
+ * the request so we just need to check a few more ENI-specific details.
+ *
+ * Called at splimp.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 close sucessful
+ * err close failed
+ *
+ */
+int
+eni_closevcc ( cup, cvp )
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+{
+ Eni_unit *eup = (Eni_unit *)cup;
+ Eni_vcc *evp = (Eni_vcc *)cvp;
+ struct vccb *vcp = evp->ev_connvc->cvc_vcc;
+ int err = 0;
+
+ VCI_Table *vct;
+
+ /*
+ * Clear any references to this VCC in our transmit queue
+ */
+ /*
+ * We'll simply allow any existing TX requests to be
+ * sent as that's easier then pulling them out of
+ * everywhere. Besides, they should be ignored at the
+ * receiver whenever the other end shuts down.
+ */
+
+ /*
+ * Free the adapter receive buffer
+ */
+ (void) eni_free_buffer ( eup, (caddr_t)evp->ev_rxbuf );
+
+ /*
+ * If this is an outbound only VCI, then we can close
+ * immediately.
+ */
+ if ( ( vcp->vc_type & VCC_IN ) == 0 ) {
+ /*
+ * The state will be set to TERM when we return
+ * to the *_TERM caller.
+ */
+ return ( 0 );
+ }
+
+ /*
+ * Find VCI entry in VCI Table
+ */
+ vct = &eup->eu_vcitbl[ vcp->vc_vci ];
+
+ /*
+ * Reset the VCI state
+ */
+ vct->vci_control = ( vct->vci_control & VCI_MODE_MASK )
+ /* | VCI_MODE_TRASH */;
+ DELAY ( MIDWAY_DELAY ); /* Give the adapter time to */
+ /* make the transition */
+
+ /*
+ * Reset everything
+ */
+ KM_ZERO ( (caddr_t)vct, sizeof(VCI_Table) );
+
+ return ( err );
+}
+
OpenPOWER on IntegriCloud