summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-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
-rw-r--r--sys/dev/hfa/fore.h144
-rw-r--r--sys/dev/hfa/fore_aali.h604
-rw-r--r--sys/dev/hfa/fore_buffer.c772
-rw-r--r--sys/dev/hfa/fore_command.c445
-rw-r--r--sys/dev/hfa/fore_globals.c119
-rw-r--r--sys/dev/hfa/fore_if.c205
-rw-r--r--sys/dev/hfa/fore_include.h139
-rw-r--r--sys/dev/hfa/fore_init.c314
-rw-r--r--sys/dev/hfa/fore_intr.c268
-rw-r--r--sys/dev/hfa/fore_load.c1618
-rw-r--r--sys/dev/hfa/fore_output.c415
-rw-r--r--sys/dev/hfa/fore_receive.c582
-rw-r--r--sys/dev/hfa/fore_slave.h191
-rw-r--r--sys/dev/hfa/fore_stats.c164
-rw-r--r--sys/dev/hfa/fore_stats.h83
-rw-r--r--sys/dev/hfa/fore_timer.c97
-rw-r--r--sys/dev/hfa/fore_transmit.c371
-rw-r--r--sys/dev/hfa/fore_var.h269
-rw-r--r--sys/dev/hfa/fore_vcm.c321
32 files changed, 11767 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 );
+}
+
diff --git a/sys/dev/hfa/fore.h b/sys/dev/hfa/fore.h
new file mode 100644
index 0000000..42f44f0
--- /dev/null
+++ b/sys/dev/hfa/fore.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * ===================================
+ * 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: fore.h,v 1.8 1998/08/26 23:28:57 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Protocol and implementation definitions
+ *
+ */
+
+#ifndef _FORE_H
+#define _FORE_H
+
+#ifndef FORE_DEV_NAME
+#define FORE_DEV_NAME "hfa"
+#endif
+
+#define FORE_MAX_UNITS 8 /* Maximum number of devices we support */
+#define FORE_MIN_UCODE 0x20300 /* Minimum microcode version we support */
+
+#define FORE_IFF_MTU 9188 /* Network interface MTU */
+#define FORE_MAX_VCC 1024 /* Maximum number of open VCCs */
+#define FORE_MAX_VPI 0 /* Maximum VPI value */
+#define FORE_MAX_VCI 1023 /* Maximum VCI value */
+#define FORE_DEF_RATE 0x00000000 /* Default rate control = disabled */
+
+#define XMIT_QUELEN 32 /* Length of transmit queue */
+#define RECV_QUELEN 32 /* Length of receive queue */
+#define CMD_QUELEN 8 /* Length of command queue */
+
+#define FORE_TIME_TICK 5 /* Watchdog timer tick (seconds) */
+#define FORE_WATCHDOG 3 /* Device watchdog timeout (ticks) */
+#define FORE_RECV_RETRY 3 /* Wait for receive queue entry retry count */
+#define FORE_RECV_DELAY 10 /* Wait for receive queue entry delay (usec) */
+
+
+/*
+ * Receive Buffer strategies
+ */
+#define BUF_MIN_VCC 4 /* Minimum for buffer supply calculations */
+
+#ifdef FORE_SBUS
+#if defined(sun4c)
+#define BUF_DATA_ALIGN 32 /* Fore-required data alignment */
+#elif defined(sun4m)
+#define BUF_DATA_ALIGN 64 /* Fore-required data alignment */
+#endif
+#endif
+#ifdef FORE_PCI
+#define BUF_DATA_ALIGN 4 /* Fore-required data alignment */
+#endif
+
+#if defined(BSD)
+/*
+ * Strategy 1 Small - mbuf
+ * Strategy 1 Large - cluster mbuf
+ *
+ * XXX buffer controls - the RECV_MAX_SEGS calculation comes out wrong
+ * using the true buffer size values if the CP really only does full-cell
+ * filling of a particular buffer - we must clarify this...it also appears
+ * the minimum buffer size is 64, even if the CP can only fit in 1 cell.
+ */
+#define SIZEOF_Buf_handle 16 /* XXX sizeof(Buf_handle) */
+
+#if BSD >= 199103
+#undef m_ext
+typedef struct m_ext M_ext;
+#define m_ext M_dat.MH.MH_dat.MH_ext
+#define BUF1_SM_HOFF (sizeof(struct m_hdr)) /* Buffer-to-handle offset */
+#define BUF1_SM_HDR (sizeof(struct m_hdr) + sizeof(struct pkthdr))
+#define BUF1_SM_LEN (MHLEN)
+#define BUF1_LG_HOFF (sizeof(struct m_hdr) + sizeof(struct pkthdr) \
+ + sizeof(M_ext)) /* Buffer-to-handle offset */
+#else
+#define BUF1_SM_HOFF (MMINOFF) /* Buffer-to-handle offset */
+#define BUF1_SM_HDR (MMINOFF)
+#define BUF1_SM_LEN (MLEN)
+#define BUF1_LG_HOFF (MMINOFF + 16) /* Buffer-to-handle offset */
+#endif
+
+/*
+ * BUF1_SM_DOFF - CP data offset into buffer data space
+ * BUF1_SM_SIZE - Buffer size
+ *
+ * These should be defined as follows, but we need compile-time constants:
+ *
+ * #define BUF1_SM_DOFF (roundup(BUF1_SM_HOFF + SIZEOF_Buf_handle,
+ * BUF_DATA_ALIGN) - BUF1_SM_HDR)
+ * #define BUF1_SM_SIZE MAX(BUF1_SM_LEN - BUF1_SM_DOFF, 64)
+ *
+ */
+#if ((BSD >= 199103) && defined(FORE_PCI))
+#define BUF1_SM_DOFF ((BUF1_SM_HOFF + SIZEOF_Buf_handle) - BUF1_SM_HDR)
+#define BUF1_SM_SIZE (BUF1_SM_LEN - BUF1_SM_DOFF)
+#endif
+#if ((BSD < 199103) && defined(FORE_SBUS) && defined(sun4c))
+#define BUF1_SM_DOFF (BUF_DATA_ALIGN - BUF1_SM_HDR)
+#define BUF1_SM_SIZE (BUF1_SM_LEN - BUF1_SM_DOFF)
+#endif
+#if ((BSD < 199103) && defined(FORE_SBUS) && defined(sun4m))
+#define BUF1_SM_DOFF (BUF_DATA_ALIGN - BUF1_SM_HDR)
+#define BUF1_SM_SIZE (64)
+#endif
+
+#define BUF1_SM_QUELEN 16 /* Entries in supply queue */
+#define BUF1_SM_CPPOOL 256 /* Buffers in CP-resident pool */
+#define BUF1_SM_ENTSIZE 8 /* Buffers in each supply queue entry */
+
+#define BUF1_LG_DOFF 0 /* CP data offset into mbuf data space */
+#define BUF1_LG_SIZE MCLBYTES /* Buffer size */
+#define BUF1_LG_QUELEN 16 /* Entries in supply queue */
+#define BUF1_LG_CPPOOL 512 /* Buffers in CP-resident pool */
+#define BUF1_LG_ENTSIZE 8 /* Buffers in each supply queue entry */
+
+#endif /* defined(BSD) */
+
+#endif /* _FORE_H */
diff --git a/sys/dev/hfa/fore_aali.h b/sys/dev/hfa/fore_aali.h
new file mode 100644
index 0000000..d59dcfc
--- /dev/null
+++ b/sys/dev/hfa/fore_aali.h
@@ -0,0 +1,604 @@
+/*
+ *
+ * ===================================
+ * 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: fore_aali.h,v 1.5 1997/05/09 00:42:25 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * ATM Adaptation Layer Interface (AALI) definitions
+ *
+ */
+
+#ifndef _FORE_AALI_H
+#define _FORE_AALI_H
+
+/*
+ * This file contains the definitions required by the FORE ATM Adaptation
+ * Layer Interface (AALI) specification.
+ */
+
+
+/*
+ * Addressing/Pointer definitions
+ *
+ * The CP memory only supports 32-bit word accesses (read and write) - thus,
+ * all memory must be defined and accessed as 32-bit words. Also, since the
+ * data transfers are word-sized, we must take care of byte-swapping issues
+ * from/to little-endian hosts (the CP is an i960 processor, ie big-endian).
+ *
+ * All pointers to CP memory areas are actually offsets from the start of
+ * the adapter RAM address space.
+ *
+ * All CP-resident data structures are declared volatile.
+ */
+typedef void * H_addr; /* Host-resident address */
+typedef unsigned long H_dma; /* Host-resident DMA address */
+typedef unsigned long CP_word; /* CP-resident word */
+typedef unsigned long CP_addr; /* CP-resident CP memory offset */
+typedef unsigned long CP_dma; /* CP-resident DMA address */
+
+
+/*
+ * Structure defining the CP's shared memory interface to the mon960 program
+ */
+struct mon960 {
+ CP_word mon_xmitmon; /* Uart - host to mon960 (see below) */
+ CP_word mon_xmithost; /* Uart - mon960 to host (see below) */
+ CP_word mon_bstat; /* Boot status word (see below) */
+ CP_addr mon_appl; /* Pointer to application memory area */
+ CP_word mon_ver; /* Mon960 firmware version */
+};
+typedef volatile struct mon960 Mon960;
+
+/*
+ * Pseudo-UART usage
+ */
+#define UART_READY 0x00000000 /* UART is ready for more data */
+#define UART_VALID 0x01000000 /* UART character is valid */
+#define UART_DATAMASK 0x000000ff /* UART character data mask */
+
+/*
+ * Boot Status Word
+ */
+#define BOOT_COLDSTART 0xc01dc01d /* CP is performing cold start */
+#define BOOT_MONREADY 0x02201958 /* Monitor is waiting for commands */
+#define BOOT_FAILTEST 0xadbadbad /* Monitor failed self-test */
+#define BOOT_RUNNING 0xce11feed /* Microcode downloaded and running */
+
+#define BOOT_LOOPS 20 /* Loops to wait for CP to boot */
+#define BOOT_DELAY 100000 /* Delay (us) for each boot loop */
+
+
+/*
+ * Supported AALs
+ */
+enum fore_aal {
+ FORE_AAL_0 = 0, /* Cell Service */
+ FORE_AAL_4 = 4, /* AAL 3/4 */
+ FORE_AAL_5 = 5 /* AAL 5 */
+};
+typedef enum fore_aal Fore_aal;
+
+
+/*
+ * Buffer strategy definition
+ */
+struct buf_strategy {
+ CP_word bfs_quelen; /* Buffer supply queue entries */
+ CP_word bfs_bufsize; /* Buffer size */
+ CP_word bfs_cppool; /* Buffers in CP-resident pool */
+ CP_word bfs_entsize; /* Buffers in each supply queue entry */
+};
+typedef volatile struct buf_strategy Buf_strategy;
+
+/*
+ * Buffer strategy id
+ */
+#define BUF_STRAT_1 0 /* Buffer strategy one */
+#define BUF_STRAT_2 1 /* Buffer strategy two */
+
+
+
+#ifdef ATM_KERNEL
+/*
+ * Common Queue Element
+ *
+ * Used for Transmit, Receive and Buffer Supply Queues
+ */
+struct com_queue {
+ CP_dma cq_descr; /* Pointer to element descriptor */
+ CP_dma cq_status; /* Pointer to element status word */
+};
+typedef volatile struct com_queue Com_queue;
+
+
+/*
+ * Queue element status word
+ */
+typedef volatile unsigned long Q_status;
+
+#define QSTAT_PENDING 0x01 /* Operation is pending */
+#define QSTAT_COMPLETED 0x02 /* Operation successfully completed */
+#define QSTAT_FREE 0x04 /* Queue element is free/unused */
+#define QSTAT_ERROR 0x08 /* Operation encountered an error */
+
+#define QSTAT_ALIGN 4
+
+
+/*
+ * PDU Transmit Queue
+ */
+
+/*
+ * PDU Transmit Queue Element
+ */
+typedef volatile struct com_queue Xmit_queue;
+
+
+/*
+ * PDU Transmit buffer segment descriptor
+ */
+struct xmit_seg_descr {
+ H_dma xsd_buffer; /* Buffer's DMA address */
+ u_int xsd_len; /* Data length in buffer */
+};
+typedef struct xmit_seg_descr Xmit_seg_descr;
+
+#define XMIT_SEG_ALIGN 4
+
+
+/*
+ * PDU Transmit descriptor header
+ */
+struct xmit_descr_hdr {
+ u_long xdh_cell_hdr; /* Cell header (minus HEC) */
+ u_long xdh_spec; /* Transmit specification (see below) */
+ u_long xdh_rate; /* Rate control (data/idle cell ratio)*/
+ u_long xdh_pad; /* Pad to quad-word boundary */
+};
+typedef struct xmit_descr_hdr Xmit_descr_hdr;
+
+
+#define XMIT_BLK_BITS 5 /* Bits to encode block size */
+#define XMIT_MAX_BLK_BITS 4 /* Max bits we can use */
+#define XMIT_BLK_SIZE (1 << XMIT_BLK_BITS)
+#define XMIT_SEGS_TO_BLKS(nseg) \
+ ((((nseg) * sizeof(Xmit_seg_descr)) \
+ + sizeof(Xmit_descr_hdr) + (XMIT_BLK_SIZE - 1)) \
+ >> XMIT_BLK_BITS)
+#define XMIT_MAX_BLKS ((1 << XMIT_MAX_BLK_BITS) - 1)
+#define XMIT_HDR_SEGS ((XMIT_BLK_SIZE - sizeof(Xmit_descr_hdr)) \
+ / sizeof(Xmit_seg_descr))
+#define XMIT_BLK_SEGS (XMIT_BLK_SIZE / sizeof(Xmit_seg_descr))
+#define XMIT_EXTRA_SEGS ((XMIT_MAX_BLKS - 1) * XMIT_BLK_SEGS)
+#define XMIT_MAX_SEGS (XMIT_EXTRA_SEGS + XMIT_HDR_SEGS)
+
+
+/*
+ * PDU Transmit descriptor
+ */
+struct xmit_descr {
+ Xmit_descr_hdr xd_hdr; /* Descriptor header */
+ Xmit_seg_descr xd_seg[XMIT_MAX_SEGS]; /* PDU segments */
+};
+typedef struct xmit_descr Xmit_descr;
+
+#define xd_cell_hdr xd_hdr.xdh_cell_hdr
+#define xd_spec xd_hdr.xdh_spec
+#define xd_rate xd_hdr.xdh_rate
+
+/*
+ * Transmit specification
+ *
+ * Bits 0-15 - Total PDU length
+ * Bits 16-23 - Number of transmit segments
+ * Bits 24-27 - AAL type
+ * Bits 28-31 - Interrupt flag
+ */
+#define XDS_SET_SPEC(i,a,n,l) (((i) << 28) | ((a) << 24) | ((n) << 16) | (l))
+#define XDS_GET_LEN(s) ((s) & 0xffff)
+#define XDS_GET_SEGS(s) (((s) >> 16) & 0xff)
+#define XDS_GET_AAL(s) (((s) >> 24) & 0xf)
+#define XDS_GET_INTR(s) (((s) >> 28) & 0xf)
+
+#define XMIT_MAX_PDULEN 65535
+#define XMIT_DESCR_ALIGN 32
+
+
+
+/*
+ * PDU Receive Queue
+ */
+
+/*
+ * PDU Receive Queue Element
+ */
+typedef volatile struct com_queue Recv_queue;
+
+
+/*
+ * Receive PDU buffer segment description
+ */
+struct recv_seg_descr {
+ H_addr rsd_handle; /* Buffer handle (from supply) */
+ u_int rsd_len; /* Data length in buffer */
+};
+typedef struct recv_seg_descr Recv_seg_descr;
+
+
+/*
+ * PDU Receive descriptor header
+ */
+struct recv_descr_hdr {
+ u_long rdh_cell_hdr; /* Cell header (minus HEC) */
+ u_long rdh_nsegs; /* Number of receive segments */
+};
+typedef struct recv_descr_hdr Recv_descr_hdr;
+
+
+#define RECV_BLK_SIZE 32
+#define RECV_HDR_SEGS ((RECV_BLK_SIZE - sizeof(Recv_descr_hdr)) \
+ / sizeof(Recv_seg_descr))
+#define RECV_BLK_SEGS (RECV_BLK_SIZE / sizeof(Recv_seg_descr))
+#define RECV_MAX_LG_SEGS ((FORE_IFF_MTU - BUF1_SM_SIZE \
+ + (BUF1_LG_SIZE - 1)) / BUF1_LG_SIZE)
+#define RECV_EXTRA_BLKS (((RECV_MAX_LG_SEGS + 1 - RECV_HDR_SEGS) \
+ + (RECV_BLK_SEGS - 1)) / RECV_BLK_SEGS)
+#define RECV_EXTRA_SEGS (RECV_EXTRA_BLKS * RECV_BLK_SEGS)
+#define RECV_MAX_SEGS (RECV_EXTRA_SEGS + RECV_HDR_SEGS)
+
+
+/*
+ * PDU Receive descriptor
+ */
+struct recv_descr {
+ Recv_descr_hdr rd_hdr; /* Descriptor header */
+ Recv_seg_descr rd_seg[RECV_MAX_SEGS]; /* PDU segments */
+};
+typedef struct recv_descr Recv_descr;
+
+#define rd_cell_hdr rd_hdr.rdh_cell_hdr
+#define rd_nsegs rd_hdr.rdh_nsegs
+
+#define RECV_DESCR_ALIGN 32
+
+
+
+/*
+ * Buffer Supply Queue
+ */
+
+/*
+ * Buffer Supply Queue Element
+ */
+typedef volatile struct com_queue Buf_queue;
+
+
+/*
+ * Buffer supply descriptor for supplying receive buffers
+ */
+struct buf_descr {
+ H_addr bsd_handle; /* Host-specific buffer handle */
+ H_dma bsd_buffer; /* Buffer DMA address */
+};
+typedef struct buf_descr Buf_descr;
+
+#define BUF_DESCR_ALIGN 32
+
+
+
+/*
+ * Command Queue
+ */
+
+/*
+ * Command Codes
+ */
+typedef volatile unsigned long Cmd_code;
+
+#define CMD_INIT 0x01 /* Initialize microcode */
+#define CMD_ACT_VCCIN 0x02 /* Activate incoming VCC */
+#define CMD_ACT_VCCOUT 0x03 /* Activate outgoing VCC */
+#define CMD_DACT_VCCIN 0x04 /* Deactivate incoming VCC */
+#define CMD_DACT_VCCOUT 0x05 /* Deactivate outgoing VCC */
+#define CMD_GET_STATS 0x06 /* Get adapter statistics */
+#define CMD_SET_OC3_REG 0x07 /* Set SUNI OC3 registers */
+#define CMD_GET_OC3_REG 0x08 /* Get SUNI OC3 registers */
+#define CMD_GET_PROM 0x09 /* Get PROM data */
+#define CMD_INTR_REQ 0x80 /* Request host interrupt */
+
+#endif /* ATM_KERNEL */
+
+
+/*
+ * Structure defining the parameters for the Initialize command
+ */
+struct init_parms {
+ CP_word init_cmd; /* Command code */
+ CP_word init_status; /* Completion status */
+ CP_word init_indisc; /* Not used */
+ CP_word init_numvcc; /* Number of VCC's supported */
+ CP_word init_cmd_elem; /* # of command queue elements */
+ CP_word init_xmit_elem; /* # of transmit queue elements */
+ CP_word init_recv_elem; /* # of receive queue elements */
+ CP_word init_recv_ext; /* # of extra receive descr SEGMENTS */
+ CP_word init_xmit_ext; /* # of extra transmit descr SEGMENTS */
+ CP_word init_cls_vcc; /* Not used */
+ CP_word init_pad[2]; /* Pad to quad-word boundary */
+ Buf_strategy init_buf1s; /* Buffer strategy - 1 small */
+ Buf_strategy init_buf1l; /* Buffer strategy - 1 large */
+ Buf_strategy init_buf2s; /* Buffer strategy - 2 small */
+ Buf_strategy init_buf2l; /* Buffer strategy - 2 large */
+};
+typedef volatile struct init_parms Init_parms;
+
+
+#ifdef ATM_KERNEL
+/*
+ * Structure defining the parameters for the Activate commands
+ */
+struct activate_parms {
+ CP_word act_spec; /* Command specification (see below) */
+ CP_word act_vccid; /* VCC id (VPI=0,VCI=id) */
+ CP_word act_batch; /* # cells in batch (AAL=NULL) */
+ CP_word act_pad; /* Pad to quad-word boundary */
+};
+typedef volatile struct activate_parms Activate_parms;
+
+/*
+ * Activate command specification
+ *
+ * Bits 0-7 - command code
+ * Bits 8-15 - AAL type
+ * Bits 16-23 - buffer strategy
+ * Bits 24-31 - reserved
+ */
+#define ACT_SET_SPEC(b,a,c) (((b) << 16) | ((a) << 8) | (c))
+#define ACT_GET_CMD(s) ((s) & 0xff)
+#define ACT_GET_AAL(s) (((s) >> 8) & 0xff)
+#define ACT_GET_STRAT(s) (((s) >> 16) & 0xff)
+
+
+/*
+ * Structure defining the parameters for the Deactivate commands
+ */
+struct dactivate_parms {
+ CP_word dact_cmd; /* Command code */
+ CP_word dact_vccid; /* VCC id (VPI=0,VCI=id) */
+ CP_word dact_pad[2]; /* Pad to quad-word boundary */
+};
+typedef volatile struct dactivate_parms Dactivate_parms;
+
+
+/*
+ * Structure defining the parameters for the Get Statistics command
+ */
+struct stats_parms {
+ CP_word stats_cmd; /* Command code */
+ CP_dma stats_buffer; /* DMA address of host stats buffer */
+ CP_word stats_pad[2]; /* Pad to quad-word boundary */
+};
+typedef volatile struct stats_parms Stats_parms;
+
+
+/*
+ * Structure defining the parameters for the SUNI OC3 commands
+ */
+struct suni_parms {
+ CP_word suni_spec; /* Command specification (see below) */
+ CP_dma suni_buffer; /* DMA address of host SUNI buffer */
+ CP_word suni_pad[2]; /* Pad to quad-word boundary */
+};
+typedef volatile struct suni_parms Suni_parms;
+
+/*
+ * SUNI OC3 command specification
+ *
+ * Bits 0-7 - command code
+ * Bits 8-15 - SUNI register number
+ * Bits 16-23 - Value(s) to set in register
+ * Bits 24-31 - Mask selecting value bits
+ */
+#define SUNI_SET_SPEC(m,v,r,c) (((m) << 24) | ((v) << 16) | ((r) << 8) | (c))
+#define SUNI_GET_CMD(s) ((s) & 0xff)
+#define SUNI_GET_REG(s) (((s) >> 8) & 0xff)
+#define SUNI_GET_VALUE(s) (((s) >> 16) & 0xff)
+#define SUNI_GET_MASK(s) (((s) >> 24) & 0xff)
+
+
+/*
+ * Structure defining the parameters for the Get Prom command
+ */
+struct prom_parms {
+ CP_word prom_cmd; /* Command code */
+ CP_dma prom_buffer; /* DMA address of host prom buffer */
+ CP_word prom_pad[2]; /* Pad to quad-word boundary */
+};
+typedef volatile struct prom_parms Prom_parms;
+
+
+/*
+ * Command Queue Element
+ */
+struct cmd_queue {
+ union { /* Command-specific parameters */
+ Activate_parms cmdqu_act;
+ Dactivate_parms cmdqu_dact;
+ Stats_parms cmdqu_stats;
+ Suni_parms cmdqu_suni;
+ Prom_parms cmdqu_prom;
+ } cmdq_u;
+ CP_dma cmdq_status; /* Pointer to element status word */
+ CP_word cmdq_pad[3]; /* Pad to quad-word boundary */
+};
+#define cmdq_act cmdq_u.cmdqu_act
+#define cmdq_dact cmdq_u.cmdqu_dact
+#define cmdq_stats cmdq_u.cmdqu_stats
+#define cmdq_suni cmdq_u.cmdqu_suni
+#define cmdq_prom cmdq_u.cmdqu_prom
+typedef volatile struct cmd_queue Cmd_queue;
+
+#endif /* ATM_KERNEL */
+
+
+
+/*
+ * Structure defining the CP's shared memory interface to the
+ * AALI firmware program (downloaded microcode)
+ */
+struct aali {
+ CP_addr aali_cmd_q; /* Pointer to command queue */
+ CP_addr aali_xmit_q; /* Pointer to transmit queue */
+ CP_addr aali_recv_q; /* Pointer to receive queue */
+ CP_addr aali_buf1s_q; /* Pointer to strategy-1 small queue */
+ CP_addr aali_buf1l_q; /* Pointer to strategy-1 large queue */
+ CP_addr aali_buf2s_q; /* Pointer to strategy-2 small queue */
+ CP_addr aali_buf2l_q; /* Pointer to strategy-2 large queue */
+ CP_word aali_intr_ena; /* Enables interrupts if non-zero */
+ CP_word aali_intr_sent; /* Interrupt issued if non-zero */
+ CP_addr aali_heap; /* Pointer to application heap */
+ CP_word aali_heaplen; /* Length of application heap */
+ CP_word aali_hostlog; /* FORE internal use */
+ CP_word aali_heartbeat; /* Monitor microcode health */
+ CP_word aali_ucode_ver; /* Microcode firmware version */
+ CP_word aali_mon_ver; /* Mon960 version */
+ CP_word aali_xmit_tput; /* FORE internal use */
+
+ /* This must be on a quad-word boundary */
+ Init_parms aali_init; /* Initialize command parameters */
+};
+typedef volatile struct aali Aali;
+
+
+/*
+ * CP maintained statistics - DMA'd to host with CMD_GET_STATS command
+ */
+struct stats_taxi {
+ u_long taxi_bad_crc; /* Bad header CRC errors */
+ u_long taxi_framing; /* Framing errors */
+ u_long taxi_pad[2]; /* Pad to quad-word boundary */
+};
+typedef struct stats_taxi Stats_taxi;
+
+struct 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; /* Correctible HEC errors */
+ u_long oc3_hec_uncorr; /* Uncorrectible HEC errors */
+ u_long oc3_pad; /* Pad to quad-word boundary */
+};
+typedef struct stats_oc3 Stats_oc3;
+
+struct stats_atm {
+ u_long atm_xmit; /* Cells transmitted */
+ u_long atm_rcvd; /* Cells received */
+ u_long atm_vpi_range; /* Cell drops - VPI out of range */
+ u_long atm_vpi_noconn; /* Cell drops - no connect for VPI */
+ u_long atm_vci_range; /* Cell drops - VCI out of range */
+ u_long atm_vci_noconn; /* Cell drops - no connect for VCI */
+ u_long atm_pad[2]; /* Pad to quad-word boundary */
+};
+typedef struct stats_atm Stats_atm;
+
+struct stats_aal0 {
+ u_long aal0_xmit; /* Cells transmitted */
+ u_long aal0_rcvd; /* Cells received */
+ u_long aal0_drops; /* Cell drops */
+ u_long aal0_pad; /* Pad to quad-word boundary */
+};
+typedef struct stats_aal0 Stats_aal0;
+
+struct stats_aal4 {
+ u_long aal4_xmit; /* Cells transmitted */
+ u_long aal4_rcvd; /* Cells received */
+ u_long aal4_crc; /* Cells with payload CRC errors */
+ u_long aal4_sar_cs; /* Cells with SAR/CS errors */
+ u_long aal4_drops; /* Cell drops */
+ u_long aal4_pdu_xmit; /* CS PDUs transmitted */
+ u_long aal4_pdu_rcvd; /* CS PDUs received */
+ u_long aal4_pdu_errs; /* CS layer protocol errors */
+ u_long aal4_pdu_drops; /* CS PDUs dropped */
+ u_long aal4_pad[3]; /* Pad to quad-word boundary */
+};
+typedef struct stats_aal4 Stats_aal4;
+
+struct 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 stats_aal5 Stats_aal5;
+
+struct stats_misc {
+ u_long buf1_sm_fail; /* Alloc fail: buffer strat 1 small */
+ u_long buf1_lg_fail; /* Alloc fail: buffer strat 1 large */
+ u_long buf2_sm_fail; /* Alloc fail: buffer strat 2 small */
+ u_long buf2_lg_fail; /* Alloc fail: buffer strat 2 large */
+ u_long rcvd_pdu_fail; /* Received PDU allocation failure */
+ u_long carrier_status; /* Carrier status */
+ u_long misc_pad[2]; /* Pad to quad-word boundary */
+};
+typedef struct stats_misc Stats_misc;
+
+struct fore_cp_stats {
+ Stats_taxi st_cp_taxi; /* TAXI layer statistics */
+ Stats_oc3 st_cp_oc3; /* OC3 layer statistics */
+ Stats_atm st_cp_atm; /* ATM layer statistics */
+ Stats_aal0 st_cp_aal0; /* AAL0 layer statistics */
+ Stats_aal4 st_cp_aal4; /* AAL3/4 layer statistics */
+ Stats_aal5 st_cp_aal5; /* AAL5 layer statistics */
+ Stats_misc st_cp_misc; /* Miscellaneous statistics */
+};
+typedef struct fore_cp_stats Fore_cp_stats;
+
+#define FORE_STATS_ALIGN 32
+
+/*
+ * CP PROM data - DMA'd to host with CMD_GET_PROM command
+ */
+struct fore_prom {
+ u_long pr_hwver; /* Hardware version number */
+ u_long pr_serno; /* Serial number */
+ u_char pr_mac[8]; /* MAC address */
+};
+typedef struct fore_prom Fore_prom;
+
+#define FORE_PROM_ALIGN 32
+
+#endif /* _FORE_AALI_H */
diff --git a/sys/dev/hfa/fore_buffer.c b/sys/dev/hfa/fore_buffer.c
new file mode 100644
index 0000000..d8bdce4
--- /dev/null
+++ b/sys/dev/hfa/fore_buffer.c
@@ -0,0 +1,772 @@
+/*
+ *
+ * ===================================
+ * 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: fore_buffer.c,v 1.6 1997/05/06 22:09:21 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Buffer Supply queue management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_buffer.c,v 1.6 1997/05/06 22:09:21 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Local functions
+ */
+static void fore_buf_drain __P((Fore_unit *));
+static void fore_buf_supply_1s __P((Fore_unit *));
+static void fore_buf_supply_1l __P((Fore_unit *));
+
+
+/*
+ * Allocate Buffer Supply Queues Data Structures
+ *
+ * Here we are allocating memory for both Strategy 1 Small and Large
+ * structures contiguously.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * 0 allocations successful
+ * else allocation failed
+ */
+int
+fore_buf_allocate(fup)
+ Fore_unit *fup;
+{
+ caddr_t memp;
+
+ /*
+ * Allocate non-cacheable memory for buffer supply status words
+ */
+ memp = atm_dev_alloc(
+ sizeof(Q_status) * (BUF1_SM_QUELEN + BUF1_LG_QUELEN),
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_buf1s_stat = (Q_status *) memp;
+ fup->fu_buf1l_stat = ((Q_status *) memp) + BUF1_SM_QUELEN;
+
+ memp = DMA_GET_ADDR(fup->fu_buf1s_stat,
+ sizeof(Q_status) * (BUF1_SM_QUELEN + BUF1_LG_QUELEN),
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_buf1s_statd = (Q_status *) memp;
+ fup->fu_buf1l_statd = ((Q_status *) memp) + BUF1_SM_QUELEN;
+
+ /*
+ * Allocate memory for buffer supply descriptors
+ */
+ memp = atm_dev_alloc(sizeof(Buf_descr) *
+ ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) +
+ (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)),
+ BUF_DESCR_ALIGN, 0);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_buf1s_desc = (Buf_descr *) memp;
+ fup->fu_buf1l_desc = ((Buf_descr *) memp) +
+ (BUF1_SM_QUELEN * BUF1_SM_ENTSIZE);
+
+ memp = DMA_GET_ADDR(fup->fu_buf1s_desc, sizeof(Buf_descr) *
+ ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) +
+ (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)),
+ BUF_DESCR_ALIGN, 0);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_buf1s_descd = (Buf_descr *) memp;
+ fup->fu_buf1l_descd = ((Buf_descr *) memp) +
+ (BUF1_SM_QUELEN * BUF1_SM_ENTSIZE);
+
+ return (0);
+}
+
+
+/*
+ * Buffer Supply Queues Initialization
+ *
+ * Allocate and initialize the host-resident buffer supply queue structures
+ * and then initialize the CP-resident queue structures.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_buf_initialize(fup)
+ Fore_unit *fup;
+{
+ Aali *aap = fup->fu_aali;
+ Buf_queue *cqp;
+ H_buf_queue *hbp;
+ Buf_descr *bdp;
+ Buf_descr *bdp_dma;
+ Q_status *qsp;
+ Q_status *qsp_dma;
+ int i;
+
+ /*
+ * Initialize Strategy 1 Small Queues
+ */
+
+ /*
+ * Point to CP-resident buffer supply queue
+ */
+ cqp = (Buf_queue *)(fup->fu_ram + CP_READ(aap->aali_buf1s_q));
+
+ /*
+ * Point to host-resident buffer supply queue structures
+ */
+ hbp = fup->fu_buf1s_q;
+ qsp = fup->fu_buf1s_stat;
+ qsp_dma = fup->fu_buf1s_statd;
+ bdp = fup->fu_buf1s_desc;
+ bdp_dma = fup->fu_buf1s_descd;
+
+ /*
+ * Loop thru all queue entries and do whatever needs doing
+ */
+ for (i = 0; i < BUF1_SM_QUELEN; i++) {
+
+ /*
+ * Set queue status word to free
+ */
+ *qsp = QSTAT_FREE;
+
+ /*
+ * Set up host queue entry and link into ring
+ */
+ hbp->hbq_cpelem = cqp;
+ hbp->hbq_status = qsp;
+ hbp->hbq_descr = bdp;
+ hbp->hbq_descr_dma = bdp_dma;
+ if (i == (BUF1_SM_QUELEN - 1))
+ hbp->hbq_next = fup->fu_buf1s_q;
+ else
+ hbp->hbq_next = hbp + 1;
+
+ /*
+ * Now let the CP into the game
+ */
+ cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
+
+ /*
+ * Bump all queue pointers
+ */
+ hbp++;
+ qsp++;
+ qsp_dma++;
+ bdp += BUF1_SM_ENTSIZE;
+ bdp_dma += BUF1_SM_ENTSIZE;
+ cqp++;
+ }
+
+ /*
+ * Initialize queue pointers
+ */
+ fup->fu_buf1s_head = fup->fu_buf1s_tail = fup->fu_buf1s_q;
+
+
+ /*
+ * Initialize Strategy 1 Large Queues
+ */
+
+ /*
+ * Point to CP-resident buffer supply queue
+ */
+ cqp = (Buf_queue *)(fup->fu_ram + CP_READ(aap->aali_buf1l_q));
+
+ /*
+ * Point to host-resident buffer supply queue structures
+ */
+ hbp = fup->fu_buf1l_q;
+ qsp = fup->fu_buf1l_stat;
+ qsp_dma = fup->fu_buf1l_statd;
+ bdp = fup->fu_buf1l_desc;
+ bdp_dma = fup->fu_buf1l_descd;
+
+ /*
+ * Loop thru all queue entries and do whatever needs doing
+ */
+ for (i = 0; i < BUF1_LG_QUELEN; i++) {
+
+ /*
+ * Set queue status word to free
+ */
+ *qsp = QSTAT_FREE;
+
+ /*
+ * Set up host queue entry and link into ring
+ */
+ hbp->hbq_cpelem = cqp;
+ hbp->hbq_status = qsp;
+ hbp->hbq_descr = bdp;
+ hbp->hbq_descr_dma = bdp_dma;
+ if (i == (BUF1_LG_QUELEN - 1))
+ hbp->hbq_next = fup->fu_buf1l_q;
+ else
+ hbp->hbq_next = hbp + 1;
+
+ /*
+ * Now let the CP into the game
+ */
+ cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
+
+ /*
+ * Bump all queue pointers
+ */
+ hbp++;
+ qsp++;
+ qsp_dma++;
+ bdp += BUF1_LG_ENTSIZE;
+ bdp_dma += BUF1_LG_ENTSIZE;
+ cqp++;
+ }
+
+ /*
+ * Initialize queue pointers
+ */
+ fup->fu_buf1l_head = fup->fu_buf1l_tail = fup->fu_buf1l_q;
+
+ return;
+}
+
+
+/*
+ * Supply Buffers to CP
+ *
+ * This function will resupply the CP with buffers to be used to
+ * store incoming data.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_buf_supply(fup)
+ Fore_unit *fup;
+{
+
+ /*
+ * First, clean out the supply queues
+ */
+ fore_buf_drain(fup);
+
+ /*
+ * Then, supply the buffers for each queue
+ */
+ fore_buf_supply_1s(fup);
+ fore_buf_supply_1l(fup);
+
+ return;
+}
+
+
+/*
+ * Supply Strategy 1 Small Buffers to CP
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+static void
+fore_buf_supply_1s(fup)
+ Fore_unit *fup;
+{
+ H_buf_queue *hbp;
+ Buf_queue *cqp;
+ Buf_descr *bdp;
+ Buf_handle *bhp;
+ KBuffer *m;
+ int nvcc, nbuf, i;
+
+ /*
+ * Figure out how many buffers we should be giving to the CP.
+ * We're basing this calculation on the current number of open
+ * VCCs thru this device, with certain minimum and maximum values
+ * enforced. This will then allow us to figure out how many more
+ * buffers we need to supply to the CP. This will be rounded up
+ * to fill a supply queue entry.
+ */
+ nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC);
+ nbuf = nvcc * 4;
+ nbuf = MIN(nbuf, BUF1_SM_CPPOOL);
+ nbuf -= fup->fu_buf1s_cnt;
+ nbuf = roundup(nbuf, BUF1_SM_ENTSIZE);
+
+ /*
+ * OK, now supply the buffers to the CP
+ */
+ while (nbuf > 0) {
+
+ /*
+ * Acquire a supply queue entry
+ */
+ hbp = fup->fu_buf1s_tail;
+ if (!((*hbp->hbq_status) & QSTAT_FREE))
+ break;
+ bdp = hbp->hbq_descr;
+
+ /*
+ * Get a buffer for each descriptor in the queue entry
+ */
+ for (i = 0; i < BUF1_SM_ENTSIZE; i++, bdp++) {
+ caddr_t cp;
+
+ /*
+ * Get a small buffer
+ */
+ KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
+ if (m == 0) {
+ break;
+ }
+ KB_HEADSET(m, BUF1_SM_DOFF);
+
+ /*
+ * Point to buffer handle structure
+ */
+ bhp = (Buf_handle *)((caddr_t)m + BUF1_SM_HOFF);
+ bhp->bh_type = BHT_S1_SMALL;
+
+ /*
+ * Setup buffer descriptor
+ */
+ bdp->bsd_handle = bhp;
+ KB_DATASTART(m, cp, caddr_t);
+ bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR(
+ cp, BUF1_SM_SIZE, BUF_DATA_ALIGN, 0);
+ if (bdp->bsd_buffer == NULL) {
+ /*
+ * Unable to assign dma address - free up
+ * this descriptor's buffer
+ */
+ fup->fu_stats->st_drv.drv_bf_segdma++;
+ KB_FREEALL(m);
+ break;
+ }
+
+ /*
+ * All set, so queue buffer (handle)
+ */
+ ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq);
+ }
+
+ /*
+ * If we we're not able to fill all the descriptors for
+ * an entry, free up what's been partially built
+ */
+ if (i != BUF1_SM_ENTSIZE) {
+
+ /*
+ * Clean up each used descriptor
+ */
+ for (bdp = hbp->hbq_descr; i; i--, bdp++) {
+
+ bhp = bdp->bsd_handle;
+
+ DEQUEUE(bhp, Buf_handle, bh_qelem,
+ fup->fu_buf1s_bq);
+
+ m = (KBuffer *)
+ ((caddr_t)bhp - BUF1_SM_HOFF);
+ KB_FREEALL(m);
+ }
+ break;
+ }
+
+ /*
+ * Finally, we've got an entry ready for the CP.
+ * So claim the host queue entry and setup the CP-resident
+ * queue entry. The CP will (potentially) grab the supplied
+ * buffers when the descriptor pointer is set.
+ */
+ fup->fu_buf1s_tail = hbp->hbq_next;
+ (*hbp->hbq_status) = QSTAT_PENDING;
+ cqp = hbp->hbq_cpelem;
+ cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma);
+
+ /*
+ * Update counters, etc for supplied buffers
+ */
+ fup->fu_buf1s_cnt += BUF1_SM_ENTSIZE;
+ nbuf -= BUF1_SM_ENTSIZE;
+ }
+
+ return;
+}
+
+
+/*
+ * Supply Strategy 1 Large Buffers to CP
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+static void
+fore_buf_supply_1l(fup)
+ Fore_unit *fup;
+{
+ H_buf_queue *hbp;
+ Buf_queue *cqp;
+ Buf_descr *bdp;
+ Buf_handle *bhp;
+ KBuffer *m;
+ int nvcc, nbuf, i;
+
+ /*
+ * Figure out how many buffers we should be giving to the CP.
+ * We're basing this calculation on the current number of open
+ * VCCs thru this device, with certain minimum and maximum values
+ * enforced. This will then allow us to figure out how many more
+ * buffers we need to supply to the CP. This will be rounded up
+ * to fill a supply queue entry.
+ */
+ nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC);
+ nbuf = nvcc * 4 * RECV_MAX_SEGS;
+ nbuf = MIN(nbuf, BUF1_LG_CPPOOL);
+ nbuf -= fup->fu_buf1l_cnt;
+ nbuf = roundup(nbuf, BUF1_LG_ENTSIZE);
+
+ /*
+ * OK, now supply the buffers to the CP
+ */
+ while (nbuf > 0) {
+
+ /*
+ * Acquire a supply queue entry
+ */
+ hbp = fup->fu_buf1l_tail;
+ if (!((*hbp->hbq_status) & QSTAT_FREE))
+ break;
+ bdp = hbp->hbq_descr;
+
+ /*
+ * Get a buffer for each descriptor in the queue entry
+ */
+ for (i = 0; i < BUF1_LG_ENTSIZE; i++, bdp++) {
+ caddr_t cp;
+
+ /*
+ * Get a cluster buffer
+ */
+ KB_ALLOCEXT(m, BUF1_LG_SIZE, KB_F_NOWAIT, KB_T_DATA);
+ if (m == 0) {
+ break;
+ }
+ KB_HEADSET(m, BUF1_LG_DOFF);
+
+ /*
+ * Point to buffer handle structure
+ */
+ bhp = (Buf_handle *)((caddr_t)m + BUF1_LG_HOFF);
+ bhp->bh_type = BHT_S1_LARGE;
+
+ /*
+ * Setup buffer descriptor
+ */
+ bdp->bsd_handle = bhp;
+ KB_DATASTART(m, cp, caddr_t);
+ bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR(
+ cp, BUF1_LG_SIZE, BUF_DATA_ALIGN, 0);
+ if (bdp->bsd_buffer == NULL) {
+ /*
+ * Unable to assign dma address - free up
+ * this descriptor's buffer
+ */
+ fup->fu_stats->st_drv.drv_bf_segdma++;
+ KB_FREEALL(m);
+ break;
+ }
+
+ /*
+ * All set, so queue buffer (handle)
+ */
+ ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq);
+ }
+
+ /*
+ * If we we're not able to fill all the descriptors for
+ * an entry, free up what's been partially built
+ */
+ if (i != BUF1_LG_ENTSIZE) {
+
+ /*
+ * Clean up each used descriptor
+ */
+ for (bdp = hbp->hbq_descr; i; i--, bdp++) {
+ bhp = bdp->bsd_handle;
+
+ DEQUEUE(bhp, Buf_handle, bh_qelem,
+ fup->fu_buf1l_bq);
+
+ m = (KBuffer *)
+ ((caddr_t)bhp - BUF1_LG_HOFF);
+ KB_FREEALL(m);
+ }
+ break;
+ }
+
+ /*
+ * Finally, we've got an entry ready for the CP.
+ * So claim the host queue entry and setup the CP-resident
+ * queue entry. The CP will (potentially) grab the supplied
+ * buffers when the descriptor pointer is set.
+ */
+ fup->fu_buf1l_tail = hbp->hbq_next;
+ (*hbp->hbq_status) = QSTAT_PENDING;
+ cqp = hbp->hbq_cpelem;
+ cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma);
+
+ /*
+ * Update counters, etc for supplied buffers
+ */
+ fup->fu_buf1l_cnt += BUF1_LG_ENTSIZE;
+ nbuf -= BUF1_LG_ENTSIZE;
+ }
+
+ return;
+}
+
+
+/*
+ * Drain Buffer Supply Queues
+ *
+ * This function will free all completed entries at the head of each
+ * buffer supply queue. Since we consider the CP to "own" the buffers
+ * once we put them on a supply queue and since a completed supply queue
+ * entry is only telling us that the CP has accepted the buffers that we
+ * gave to it, there's not much to do here.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+static void
+fore_buf_drain(fup)
+ Fore_unit *fup;
+{
+ H_buf_queue *hbp;
+
+ /*
+ * Drain Strategy 1 Small Queue
+ */
+
+ /*
+ * Process each completed entry
+ */
+ while (*fup->fu_buf1s_head->hbq_status & QSTAT_COMPLETED) {
+
+ hbp = fup->fu_buf1s_head;
+
+ if (*hbp->hbq_status & QSTAT_ERROR) {
+ /*
+ * XXX - what does this mean???
+ */
+ log(LOG_ERR, "fore_buf_drain: buf1s queue error\n");
+ }
+
+ /*
+ * Mark this entry free for use and bump head pointer
+ * to the next entry in the queue
+ */
+ *hbp->hbq_status = QSTAT_FREE;
+ fup->fu_buf1s_head = hbp->hbq_next;
+ }
+
+
+ /*
+ * Drain Strategy 1 Large Queue
+ */
+
+ /*
+ * Process each completed entry
+ */
+ while (*fup->fu_buf1l_head->hbq_status & QSTAT_COMPLETED) {
+
+ hbp = fup->fu_buf1l_head;
+
+ if (*hbp->hbq_status & QSTAT_ERROR) {
+ /*
+ * XXX - what does this mean???
+ */
+ log(LOG_ERR, "fore_buf_drain: buf1l queue error\n");
+ }
+
+ /*
+ * Mark this entry free for use and bump head pointer
+ * to the next entry in the queue
+ */
+ *hbp->hbq_status = QSTAT_FREE;
+ fup->fu_buf1l_head = hbp->hbq_next;
+ }
+
+ return;
+}
+
+
+/*
+ * Free Buffer Supply Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_buf_free(fup)
+ Fore_unit *fup;
+{
+ Buf_handle *bhp;
+ KBuffer *m;
+
+ /*
+ * Free any previously supplied and not returned buffers
+ */
+ if (fup->fu_flags & CUF_INITED) {
+
+ /*
+ * Run through Strategy 1 Small queue
+ */
+ while (bhp = Q_HEAD(fup->fu_buf1s_bq, Buf_handle)) {
+ caddr_t cp;
+
+ /*
+ * Back off to buffer
+ */
+ m = (KBuffer *)((caddr_t)bhp - BUF1_SM_HOFF);
+
+ /*
+ * Dequeue handle and free buffer
+ */
+ DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq);
+
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);
+
+ KB_FREEALL(m);
+ }
+
+ /*
+ * Run through Strategy 1 Large queue
+ */
+ while (bhp = Q_HEAD(fup->fu_buf1l_bq, Buf_handle)) {
+ caddr_t cp;
+
+ /*
+ * Back off to buffer
+ */
+ m = (KBuffer *)((caddr_t)bhp - BUF1_LG_HOFF);
+
+ /*
+ * Dequeue handle and free buffer
+ */
+ DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq);
+
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);
+
+ KB_FREEALL(m);
+ }
+ }
+
+ /*
+ * Free the status words
+ */
+ if (fup->fu_buf1s_stat) {
+ if (fup->fu_buf1s_statd) {
+ DMA_FREE_ADDR(fup->fu_buf1s_stat, fup->fu_buf1s_statd,
+ sizeof(Q_status) *
+ (BUF1_SM_QUELEN + BUF1_LG_QUELEN),
+ ATM_DEV_NONCACHE);
+ }
+ atm_dev_free((void *)fup->fu_buf1s_stat);
+ fup->fu_buf1s_stat = NULL;
+ fup->fu_buf1s_statd = NULL;
+ fup->fu_buf1l_stat = NULL;
+ fup->fu_buf1l_statd = NULL;
+ }
+
+ /*
+ * Free the transmit descriptors
+ */
+ if (fup->fu_buf1s_desc) {
+ if (fup->fu_buf1s_descd) {
+ DMA_FREE_ADDR(fup->fu_buf1s_desc, fup->fu_buf1s_descd,
+ sizeof(Buf_descr) *
+ ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) +
+ (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)),
+ 0);
+ }
+ atm_dev_free(fup->fu_buf1s_desc);
+ fup->fu_buf1s_desc = NULL;
+ fup->fu_buf1s_descd = NULL;
+ fup->fu_buf1l_desc = NULL;
+ fup->fu_buf1l_descd = NULL;
+ }
+
+ return;
+}
+
diff --git a/sys/dev/hfa/fore_command.c b/sys/dev/hfa/fore_command.c
new file mode 100644
index 0000000..29b99c6
--- /dev/null
+++ b/sys/dev/hfa/fore_command.c
@@ -0,0 +1,445 @@
+/*
+ *
+ * ===================================
+ * 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: fore_command.c,v 1.10 1998/06/29 21:42:09 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Command queue management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_command.c,v 1.10 1998/06/29 21:42:09 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+/*
+ * Local variables
+ */
+static struct t_atm_cause fore_cause = {
+ T_ATM_ITU_CODING,
+ T_ATM_LOC_USER,
+ T_ATM_CAUSE_TEMPORARY_FAILURE,
+ {0, 0, 0, 0}
+};
+
+
+/*
+ * Allocate Command Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * 0 allocations successful
+ * else allocation failed
+ */
+int
+fore_cmd_allocate(fup)
+ Fore_unit *fup;
+{
+ caddr_t memp;
+
+ /*
+ * Allocate non-cacheable memory for command status words
+ */
+ memp = atm_dev_alloc(sizeof(Q_status) * CMD_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_cmd_stat = (Q_status *) memp;
+
+ memp = DMA_GET_ADDR(fup->fu_cmd_stat, sizeof(Q_status) * CMD_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_cmd_statd = (Q_status *) memp;
+
+ /*
+ * Allocate memory for statistics buffer
+ */
+ memp = atm_dev_alloc(sizeof(Fore_stats), FORE_STATS_ALIGN, 0);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_stats = (Fore_stats *) memp;
+
+#ifdef FORE_PCI
+ /*
+ * Allocate memory for PROM buffer
+ */
+ memp = atm_dev_alloc(sizeof(Fore_prom), FORE_PROM_ALIGN, 0);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_prom = (Fore_prom *) memp;
+#endif
+
+ return (0);
+}
+
+
+/*
+ * Command Queue Initialization
+ *
+ * Allocate and initialize the host-resident command queue structures
+ * and then initialize the CP-resident queue structures.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_cmd_initialize(fup)
+ Fore_unit *fup;
+{
+ Aali *aap = fup->fu_aali;
+ Cmd_queue *cqp;
+ H_cmd_queue *hcp;
+ Q_status *qsp;
+ Q_status *qsp_dma;
+ int i;
+
+ /*
+ * Point to CP-resident command queue
+ */
+ cqp = (Cmd_queue *)(fup->fu_ram + CP_READ(aap->aali_cmd_q));
+
+ /*
+ * Point to host-resident command queue structures
+ */
+ hcp = fup->fu_cmd_q;
+ qsp = fup->fu_cmd_stat;
+ qsp_dma = fup->fu_cmd_statd;
+
+ /*
+ * Loop thru all queue entries and do whatever needs doing
+ */
+ for (i = 0; i < CMD_QUELEN; i++) {
+
+ /*
+ * Set queue status word to free
+ */
+ *qsp = QSTAT_FREE;
+
+ /*
+ * Set up host queue entry and link into ring
+ */
+ hcp->hcq_cpelem = cqp;
+ hcp->hcq_status = qsp;
+ if (i == (CMD_QUELEN - 1))
+ hcp->hcq_next = fup->fu_cmd_q;
+ else
+ hcp->hcq_next = hcp + 1;
+
+ /*
+ * Now let the CP into the game
+ */
+ cqp->cmdq_status = (CP_dma) CP_WRITE(qsp_dma);
+
+ /*
+ * Bump all queue pointers
+ */
+ hcp++;
+ qsp++;
+ qsp_dma++;
+ cqp++;
+ }
+
+ /*
+ * Initialize queue pointers
+ */
+ fup->fu_cmd_head = fup->fu_cmd_tail = fup->fu_cmd_q;
+
+ return;
+}
+
+
+/*
+ * Drain Command Queue
+ *
+ * This function will process and free all completed entries at the head
+ * of the command queue.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_cmd_drain(fup)
+ Fore_unit *fup;
+{
+ H_cmd_queue *hcp;
+ Fore_vcc *fvp;
+
+ /*
+ * Process each completed entry
+ */
+ while (*fup->fu_cmd_head->hcq_status & QSTAT_COMPLETED) {
+
+ hcp = fup->fu_cmd_head;
+
+ /*
+ * Process command completion
+ */
+ switch (hcp->hcq_code) {
+
+ case CMD_ACT_VCCIN:
+ case CMD_ACT_VCCOUT:
+ fvp = hcp->hcq_arg;
+ if (*hcp->hcq_status & QSTAT_ERROR) {
+ /*
+ * VCC activation failed - just abort vcc
+ */
+ if (fvp)
+ atm_cm_abort(fvp->fv_connvc,
+ &fore_cause);
+ fup->fu_pif.pif_cmderrors++;
+ } else {
+ /*
+ * Successful VCC activation
+ */
+ if (fvp) {
+ fvp->fv_state = CVS_ACTIVE;
+ fup->fu_open_vcc++;
+ }
+ }
+ break;
+
+ case CMD_DACT_VCCIN:
+ case CMD_DACT_VCCOUT:
+ fvp = hcp->hcq_arg;
+ if (*hcp->hcq_status & QSTAT_ERROR) {
+ /*
+ * VCC dactivation failed - whine
+ */
+ log(LOG_ERR,
+ "fore_cmd_drain: DACT failed, vcc=(%d,%d)\n",
+ fvp->fv_connvc->cvc_vcc->vc_vpi,
+ fvp->fv_connvc->cvc_vcc->vc_vci);
+ fup->fu_pif.pif_cmderrors++;
+ } else {
+ /*
+ * Successful VCC dactivation - so what?
+ */
+ }
+ break;
+
+ case CMD_GET_STATS:
+ if (*hcp->hcq_status & QSTAT_ERROR) {
+ /*
+ * Couldn't get stats
+ */
+ fup->fu_pif.pif_cmderrors++;
+ fup->fu_stats_ret = EIO;
+ } else {
+ /*
+ * Stats are now in unit buffer
+ */
+ fup->fu_stats_ret = 0;
+ }
+ DMA_FREE_ADDR(fup->fu_stats, fup->fu_statsd,
+ sizeof(Fore_cp_stats), 0);
+ fup->fu_flags &= ~FUF_STATCMD;
+
+ /*
+ * Flush received stats data
+ */
+#ifdef VAC
+ if (vac)
+ vac_pageflush((addr_t)fup->fu_stats);
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /*
+ * Little endian machines receives the stats in
+ * wrong byte order. Instead of swapping in user
+ * land, swap here so that everything going out
+ * of the kernel is in correct host order.
+ */
+ {
+ u_long *bp = (u_long *)fup->fu_stats;
+ int loop;
+
+ for ( loop = 0; loop < sizeof(Fore_cp_stats)/
+ sizeof(long); loop++, bp++ )
+ *bp = ntohl(*bp);
+ }
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+ /*
+ * Poke whoever is waiting on the stats
+ */
+ wakeup((caddr_t)&fup->fu_stats);
+ break;
+
+#ifdef FORE_PCI
+ case CMD_GET_PROM:
+ if (*hcp->hcq_status & QSTAT_ERROR) {
+ /*
+ * Couldn't get PROM data
+ */
+ fup->fu_pif.pif_cmderrors++;
+ log(LOG_ERR,
+ "fore_cmd_drain: %s%d: GET_PROM failed\n",
+ fup->fu_pif.pif_name,
+ fup->fu_pif.pif_unit);
+ } else {
+ Fore_prom *fp = fup->fu_prom;
+
+ /*
+ * Flush received PROM data
+ */
+#ifdef VAC
+ if (vac)
+ vac_pageflush((addr_t)fp);
+#endif
+ /*
+ * Copy PROM info into config areas
+ */
+ KM_COPY(&fp->pr_mac[2],
+ &fup->fu_pif.pif_macaddr,
+ sizeof(struct mac_addr));
+ fup->fu_config.ac_macaddr =
+ fup->fu_pif.pif_macaddr;
+ sprintf(fup->fu_config.ac_hard_vers, "%d.%d.%d",
+ (fp->pr_hwver >> 16) & 0xff,
+ (fp->pr_hwver >> 8) & 0xff,
+ fp->pr_hwver & 0xff);
+ fup->fu_config.ac_serial = fp->pr_serno;
+ }
+
+ DMA_FREE_ADDR(fup->fu_prom, fup->fu_promd,
+ sizeof(Fore_prom), 0);
+ break;
+#endif /* FORE_PCI */
+
+ default:
+ log(LOG_ERR, "fore_cmd_drain: unknown command %d\n",
+ hcp->hcq_code);
+ }
+
+ /*
+ * Mark this entry free for use and bump head pointer
+ * to the next entry in the queue
+ */
+ *hcp->hcq_status = QSTAT_FREE;
+ fup->fu_cmd_head = hcp->hcq_next;
+ }
+
+ return;
+}
+
+
+/*
+ * Free Command Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_cmd_free(fup)
+ Fore_unit *fup;
+{
+ H_cmd_queue *hcp;
+
+ /*
+ * Deal with any commands left on the queue
+ */
+ if (fup->fu_flags & CUF_INITED) {
+ while (*fup->fu_cmd_head->hcq_status != QSTAT_FREE) {
+ hcp = fup->fu_cmd_head;
+
+ switch (hcp->hcq_code) {
+
+ case CMD_GET_STATS:
+ /*
+ * Just in case someone is sleeping on this
+ */
+ fup->fu_stats_ret = EIO;
+ wakeup((caddr_t)&fup->fu_stats);
+ break;
+ }
+
+ *hcp->hcq_status = QSTAT_FREE;
+ fup->fu_cmd_head = hcp->hcq_next;
+ }
+ }
+
+ /*
+ * Free the statistics buffer
+ */
+ if (fup->fu_stats) {
+ atm_dev_free(fup->fu_stats);
+ fup->fu_stats = NULL;
+ }
+
+#ifdef FORE_PCI
+ /*
+ * Free the PROM buffer
+ */
+ if (fup->fu_prom) {
+ atm_dev_free(fup->fu_prom);
+ fup->fu_prom = NULL;
+ }
+#endif
+
+ /*
+ * Free the status words
+ */
+ if (fup->fu_cmd_stat) {
+ if (fup->fu_cmd_statd) {
+ DMA_FREE_ADDR(fup->fu_cmd_stat, fup->fu_cmd_statd,
+ sizeof(Q_status) * CMD_QUELEN,
+ ATM_DEV_NONCACHE);
+ }
+ atm_dev_free((void *)fup->fu_cmd_stat);
+ fup->fu_cmd_stat = NULL;
+ fup->fu_cmd_statd = NULL;
+ }
+
+ return;
+}
+
diff --git a/sys/dev/hfa/fore_globals.c b/sys/dev/hfa/fore_globals.c
new file mode 100644
index 0000000..4abc5fa
--- /dev/null
+++ b/sys/dev/hfa/fore_globals.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * ===================================
+ * 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: fore_globals.c,v 1.6 1997/05/06 22:09:31 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Global variable definitions
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_globals.c,v 1.6 1997/05/06 22:09:31 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Supported device models
+ */
+Fore_device fore_devices[] = {
+#ifdef FORE_SBUS
+ {SBA200E_PROM_NAME, DEV_FORE_SBA200E},
+ {SBA200_PROM_NAME, DEV_FORE_SBA200},
+#endif
+ {""}
+};
+
+
+/*
+ * Device unit table
+ */
+Fore_unit *fore_units[FORE_MAX_UNITS] = {NULL};
+int fore_nunits = 0;
+
+
+/*
+ * ATM Interface services
+ */
+static struct stack_defn fore_svaal5 = {
+ NULL,
+ SAP_CPCS_AAL5,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+static struct stack_defn fore_svaal4 = {
+ &fore_svaal5,
+ SAP_CPCS_AAL3_4,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+static struct stack_defn fore_svaal0 = {
+ &fore_svaal4,
+ SAP_ATM,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+struct stack_defn *fore_services = &fore_svaal0;
+
+
+/*
+ * Storage pools
+ */
+struct sp_info fore_nif_pool = {
+ "fore nif pool", /* si_name */
+ sizeof(struct atm_nif), /* si_blksiz */
+ 5, /* si_blkcnt */
+ 20 /* si_maxallow */
+};
+
+struct sp_info fore_vcc_pool = {
+ "fore vcc pool", /* si_name */
+ sizeof(Fore_vcc), /* si_blksiz */
+ 10, /* si_blkcnt */
+ 100 /* si_maxallow */
+};
+
+
+/*
+ * Watchdog timer
+ */
+struct atm_time fore_timer = {0, 0};
+
diff --git a/sys/dev/hfa/fore_if.c b/sys/dev/hfa/fore_if.c
new file mode 100644
index 0000000..7d3b3b6
--- /dev/null
+++ b/sys/dev/hfa/fore_if.c
@@ -0,0 +1,205 @@
+/*
+ *
+ * ===================================
+ * 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: fore_if.c,v 1.6 1998/08/26 23:28:58 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Network interface layer support
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_if.c,v 1.6 1998/08/26 23:28:58 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * 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
+fore_atm_ioctl(code, data, arg)
+ int code;
+ caddr_t data;
+ caddr_t arg;
+{
+ struct atminfreq *aip = (struct atminfreq *)data;
+ struct atm_pif *pip;
+ Fore_unit *fup;
+ 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("fore_atm_ioctl: code=%d, opcode=%d\n",
+ code, aip->air_opcode);
+
+ switch ( aip->air_opcode ) {
+
+ case AIOCS_INF_VST:
+ /*
+ * Get vendor statistics
+ */
+ pip = (struct atm_pif *)arg;
+ fup = (Fore_unit *)pip;
+ if ( pip == 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 from the hardware
+ */
+ count = 0;
+ if ( ( err = fore_get_stats ( fup ) ) == 0 )
+ {
+ /*
+ * Stick as much of it as we have room for
+ * into the response
+ */
+ count = min ( sizeof(Fore_stats), buf_len );
+
+ /*
+ * Copy stats into user's buffer. Return value is
+ * amount of data copied.
+ */
+ if (err = copyout((caddr_t)fup->fu_stats, buf, count))
+ break;
+ buf += count;
+ buf_len -= count;
+ if ( count < sizeof(Fore_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 lengths
+ */
+ aip->air_buf_addr = buf;
+ aip->air_buf_len = buf_len;
+ break;
+
+ default:
+ err = ENOSYS; /* Operation not supported */
+ break;
+ }
+
+ return (err);
+}
+
+
+/*
+ * Free Fore-specific device resources
+ *
+ * Frees all dynamically acquired resources for a device unit. Before
+ * this function is called, the CP will have been reset and our interrupt
+ * vectors removed.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+fore_interface_free(fup)
+ Fore_unit *fup;
+{
+
+ /*
+ * Free up all of our allocated memory
+ */
+ fore_xmit_free(fup);
+ fore_recv_free(fup);
+ fore_buf_free(fup);
+ fore_cmd_free(fup);
+
+ /*
+ * Clear device initialized
+ */
+ if (fup->fu_flags & CUF_INITED) {
+ fup->fu_flags &= ~CUF_INITED;
+ }
+
+ if (fup->fu_flags & FUF_STATCMD) {
+ DMA_FREE_ADDR(fup->fu_stats, fup->fu_statsd,
+ sizeof(Fore_cp_stats), 0);
+ fup->fu_flags &= ~FUF_STATCMD;
+ }
+ return;
+}
+
diff --git a/sys/dev/hfa/fore_include.h b/sys/dev/hfa/fore_include.h
new file mode 100644
index 0000000..b146a3c
--- /dev/null
+++ b/sys/dev/hfa/fore_include.h
@@ -0,0 +1,139 @@
+/*
+ *
+ * ===================================
+ * 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: fore_include.h,v 1.8 1998/02/19 20:10:18 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Local driver include files and global declarations
+ *
+ */
+
+#ifndef _FORE_INCLUDE_H
+#define _FORE_INCLUDE_H
+
+#include <netatm/kern_include.h>
+
+/*
+ * If not specified elsewhere, guess which type of bus support we want
+ */
+#if !(defined(FORE_PCI) || defined(FORE_SBUS))
+#if defined(sparc)
+#define FORE_SBUS
+#elif defined(__i386__)
+#define FORE_PCI
+#endif
+#endif
+
+#ifdef FORE_PCI
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+#endif
+
+#include <dev/hfa/fore.h>
+#include <dev/hfa/fore_aali.h>
+#include <dev/hfa/fore_slave.h>
+#include <dev/hfa/fore_stats.h>
+#include <dev/hfa/fore_var.h>
+
+/*
+ * Global function declarations
+ */
+ /* fore_buffer.c */
+int fore_buf_allocate __P((Fore_unit *));
+void fore_buf_initialize __P((Fore_unit *));
+void fore_buf_supply __P((Fore_unit *));
+void fore_buf_free __P((Fore_unit *));
+
+ /* fore_command.c */
+int fore_cmd_allocate __P((Fore_unit *));
+void fore_cmd_initialize __P((Fore_unit *));
+void fore_cmd_drain __P((Fore_unit *));
+void fore_cmd_free __P((Fore_unit *));
+
+ /* fore_if.c */
+int fore_atm_ioctl __P((int, caddr_t, caddr_t));
+void fore_interface_free __P((Fore_unit *));
+
+ /* fore_init.c */
+void fore_initialize __P((Fore_unit *));
+void fore_initialize_complete __P((Fore_unit *));
+
+ /* fore_intr.c */
+#if defined(sun)
+int fore_poll __P((void));
+#endif
+#if (defined(BSD) && (BSD <= 199306))
+int fore_intr __P((void *));
+#else
+void fore_intr __P((void *));
+#endif
+void fore_watchdog __P((Fore_unit *));
+
+ /* fore_load.c */
+
+ /* fore_output.c */
+void fore_output __P((Cmn_unit *, Cmn_vcc *, KBuffer *));
+
+ /* fore_receive.c */
+int fore_recv_allocate __P((Fore_unit *));
+void fore_recv_initialize __P((Fore_unit *));
+void fore_recv_drain __P((Fore_unit *));
+void fore_recv_free __P((Fore_unit *));
+
+ /* fore_stats.c */
+int fore_get_stats __P((Fore_unit *));
+
+ /* fore_timer.c */
+void fore_timeout __P((struct atm_time *));
+
+ /* fore_transmit.c */
+int fore_xmit_allocate __P((Fore_unit *));
+void fore_xmit_initialize __P((Fore_unit *));
+void fore_xmit_drain __P((Fore_unit *));
+void fore_xmit_free __P((Fore_unit *));
+
+ /* fore_vcm.c */
+int fore_instvcc __P((Cmn_unit *, Cmn_vcc *));
+int fore_openvcc __P((Cmn_unit *, Cmn_vcc *));
+int fore_closevcc __P((Cmn_unit *, Cmn_vcc *));
+
+
+/*
+ * Global variable declarations
+ */
+extern Fore_device fore_devices[];
+extern Fore_unit *fore_units[];
+extern int fore_nunits;
+extern struct stack_defn *fore_services;
+extern struct sp_info fore_nif_pool;
+extern struct sp_info fore_vcc_pool;
+extern struct atm_time fore_timer;
+
+#endif /* _FORE_INCLUDE_H */
diff --git a/sys/dev/hfa/fore_init.c b/sys/dev/hfa/fore_init.c
new file mode 100644
index 0000000..61f4f01
--- /dev/null
+++ b/sys/dev/hfa/fore_init.c
@@ -0,0 +1,314 @@
+/*
+ *
+ * ===================================
+ * 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: fore_init.c,v 1.7 1997/05/06 22:09:43 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Cell Processor (CP) initialization routines
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_init.c,v 1.7 1997/05/06 22:09:43 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Local functions
+ */
+#ifdef FORE_PCI
+static void fore_get_prom __P((Fore_unit *));
+#endif
+
+
+/*
+ * Begin CP Initialization
+ *
+ * This function will poll for the successful downloading and starting of
+ * the CP microcode program. After the microcode is running, we will allocate
+ * any needed kernel memory (must do it in non-interrupt mode), build the CP
+ * queue configurations and issue an Initialize command to the CP.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_initialize(fup)
+ Fore_unit *fup;
+{
+ Aali *aap;
+ Init_parms *inp;
+ caddr_t errmsg;
+ u_long vers;
+
+ /*
+ * Must wait until firmware has been downloaded and is running
+ */
+ if (CP_READ(fup->fu_mon->mon_bstat) != BOOT_RUNNING) {
+
+ /*
+ * Try again later
+ */
+ fup->fu_thandle =
+ timeout((KTimeout_ret(*) __P((void *)))fore_initialize,
+ (void *)fup, hz);
+ return;
+ } else
+ callout_handle_init(&fup->fu_thandle);
+
+ /*
+ * Allocate queues and whatever else is needed
+ */
+ if (fore_xmit_allocate(fup)) {
+ errmsg = "transmit queue allocation";
+ goto failed;
+ }
+ if (fore_recv_allocate(fup)) {
+ errmsg = "receive queue allocation";
+ goto failed;
+ }
+ if (fore_buf_allocate(fup)) {
+ errmsg = "buffer supply queue allocation";
+ goto failed;
+ }
+ if (fore_cmd_allocate(fup)) {
+ errmsg = "command queue allocation";
+ goto failed;
+ }
+
+ /*
+ * CP microcode is downloaded - locate shared memory interface
+ */
+ aap = (Aali *)(fup->fu_ram + CP_READ(fup->fu_mon->mon_appl));
+ fup->fu_aali = aap;
+
+ /*
+ * Pick out any interesting info from the microcode
+ */
+ vers = CP_READ(aap->aali_ucode_ver);
+ if (vers < FORE_MIN_UCODE) {
+ errmsg = "unsupported microcode version";
+ goto failed;
+ }
+ sprintf(fup->fu_config.ac_firm_vers, "%d.%d.%d",
+ (vers >> 16) & 0xff, (vers >> 8) & 0xff, vers & 0xff);
+
+#ifdef notdef
+ /*
+ * Turn on CP debugging
+ */
+ aap->aali_hostlog = 1;
+#endif
+
+ /*
+ * Build the initialization block
+ */
+ inp = &aap->aali_init;
+ inp->init_numvcc = CP_WRITE(FORE_MAX_VCC);
+ inp->init_cmd_elem = CP_WRITE(CMD_QUELEN);
+ inp->init_xmit_elem = CP_WRITE(XMIT_QUELEN);
+ inp->init_recv_elem = CP_WRITE(RECV_QUELEN);
+ inp->init_recv_ext = CP_WRITE(RECV_EXTRA_SEGS);
+ inp->init_xmit_ext = CP_WRITE(XMIT_EXTRA_SEGS);
+ inp->init_buf1s.bfs_quelen = CP_WRITE(BUF1_SM_QUELEN);
+ inp->init_buf1s.bfs_bufsize = CP_WRITE(BUF1_SM_SIZE);
+ inp->init_buf1s.bfs_cppool = CP_WRITE(BUF1_SM_CPPOOL);
+ inp->init_buf1s.bfs_entsize = CP_WRITE(BUF1_SM_ENTSIZE);
+ inp->init_buf1l.bfs_quelen = CP_WRITE(BUF1_LG_QUELEN);
+ inp->init_buf1l.bfs_bufsize = CP_WRITE(BUF1_LG_SIZE);
+ inp->init_buf1l.bfs_cppool = CP_WRITE(BUF1_LG_CPPOOL);
+ inp->init_buf1l.bfs_entsize = CP_WRITE(BUF1_LG_ENTSIZE);
+ inp->init_buf2s.bfs_quelen = CP_WRITE(0);
+ inp->init_buf2s.bfs_bufsize = CP_WRITE(0);
+ inp->init_buf2s.bfs_cppool = CP_WRITE(0);
+ inp->init_buf2s.bfs_entsize = CP_WRITE(0);
+ inp->init_buf2l.bfs_quelen = CP_WRITE(0);
+ inp->init_buf2l.bfs_bufsize = CP_WRITE(0);
+ inp->init_buf2l.bfs_cppool = CP_WRITE(0);
+ inp->init_buf2l.bfs_entsize = CP_WRITE(0);
+
+ /*
+ * Enable device interrupts
+ */
+ aap->aali_intr_ena = CP_WRITE(1);
+
+ /*
+ * Issue the Initialize command to the CP and wait for
+ * the CP to interrupt to signal completion
+ */
+ inp->init_status = CP_WRITE(QSTAT_PENDING);
+ inp->init_cmd = CP_WRITE(CMD_INIT | CMD_INTR_REQ);
+ return;
+
+failed:
+ /*
+ * Initialization failure
+ */
+ fore_interface_free(fup);
+ log(LOG_ERR, "fore initialization failed: intf=%s%d, err=%s\n",
+ fup->fu_pif.pif_name, fup->fu_pif.pif_unit, errmsg);
+ return;
+}
+
+
+/*
+ * Complete CP Initialization
+ *
+ * Called after the CP has successfully completed processing of the
+ * Initialize command. We will now finish off our part of the
+ * initialization process by setting up all the host-based queue
+ * management structures.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_initialize_complete(fup)
+ Fore_unit *fup;
+{
+ Aali *aap = fup->fu_aali;
+
+ /*
+ * Log an initialization failure
+ */
+ if (CP_READ(aap->aali_init.init_status) & QSTAT_ERROR) {
+
+ log(LOG_ERR,
+ "fore initialization failed: intf=%s%d, hbeat=0x%x\n",
+ fup->fu_pif.pif_name, fup->fu_pif.pif_unit,
+ CP_READ(aap->aali_heartbeat));
+ return;
+ }
+
+ ATM_DEBUG1("heap=0x%x\n", aap->aali_heap);
+ ATM_DEBUG1("heaplen=0x%x\n", aap->aali_heaplen);
+ ATM_DEBUG1("cmd_q=0x%x\n", aap->aali_cmd_q);
+ ATM_DEBUG1("xmit_q=0x%x\n", aap->aali_xmit_q);
+ ATM_DEBUG1("recv_q=0x%x\n", aap->aali_recv_q);
+ ATM_DEBUG1("buf1s_q=0x%x\n", aap->aali_buf1s_q);
+ ATM_DEBUG1("buf1l_q=0x%x\n", aap->aali_buf1l_q);
+ ATM_DEBUG1("buf2s_q=0x%x\n", aap->aali_buf2s_q);
+ ATM_DEBUG1("buf2l_q=0x%x\n", aap->aali_buf2l_q);
+
+ /*
+ * Initialize all of our queues
+ */
+ fore_xmit_initialize(fup);
+ fore_recv_initialize(fup);
+ fore_buf_initialize(fup);
+ fore_cmd_initialize(fup);
+
+ /*
+ * Mark device initialization completed
+ */
+ fup->fu_flags |= CUF_INITED;
+
+#ifdef FORE_PCI
+ fore_get_prom(fup);
+#endif
+ return;
+}
+
+
+#ifdef FORE_PCI
+/*
+ * Get device PROM values from CP
+ *
+ * This function will issue a GET_PROM command to the CP in order to
+ * initiate the DMA transfer of the CP's PROM structure to the host.
+ * This will be called after CP initialization has completed.
+ * There is (currently) no retry if this fails.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+fore_get_prom(fup)
+ Fore_unit *fup;
+{
+ H_cmd_queue *hcp;
+ Cmd_queue *cqp;
+
+ /*
+ * Queue command at end of command queue
+ */
+ hcp = fup->fu_cmd_tail;
+ if ((*hcp->hcq_status) & QSTAT_FREE) {
+
+ /*
+ * Queue entry available, so set our view of things up
+ */
+ hcp->hcq_code = CMD_GET_PROM;
+ hcp->hcq_arg = NULL;
+ fup->fu_cmd_tail = hcp->hcq_next;
+
+ /*
+ * Now set the CP-resident queue entry - the CP will grab
+ * the command when the op-code is set.
+ */
+ cqp = hcp->hcq_cpelem;
+ (*hcp->hcq_status) = QSTAT_PENDING;
+
+ fup->fu_promd = DMA_GET_ADDR(fup->fu_prom, sizeof(Fore_prom),
+ FORE_PROM_ALIGN, 0);
+ if (fup->fu_promd == NULL) {
+ fup->fu_stats->st_drv.drv_cm_nodma++;
+ return;
+ }
+ cqp->cmdq_prom.prom_buffer = (CP_dma) CP_WRITE(fup->fu_promd);
+ cqp->cmdq_prom.prom_cmd = CP_WRITE(CMD_GET_PROM | CMD_INTR_REQ);
+
+ } else {
+ /*
+ * Command queue full
+ */
+ fup->fu_stats->st_drv.drv_cm_full++;
+ }
+
+ return;
+}
+#endif /* FORE_PCI */
+
diff --git a/sys/dev/hfa/fore_intr.c b/sys/dev/hfa/fore_intr.c
new file mode 100644
index 0000000..f295b62
--- /dev/null
+++ b/sys/dev/hfa/fore_intr.c
@@ -0,0 +1,268 @@
+/*
+ *
+ * ===================================
+ * 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: fore_intr.c,v 1.7 1997/05/06 22:09:48 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Interrupt processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_intr.c,v 1.7 1997/05/06 22:09:48 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+#if defined(sun)
+/*
+ * Polling interrupt routine
+ *
+ * Polling interrupts are handled by calling all interrupt service
+ * routines for a given level until someone claims to have "handled" the
+ * interrupt.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 1 an interrupt has been serviced
+ * 0 no interrupts serviced
+ *
+ */
+int
+fore_poll()
+{
+ int serviced = 0;
+ int unit;
+
+ /*
+ * See if any of our devices are interrupting
+ */
+ for ( unit = 0; unit < fore_nunits; unit++ )
+ {
+ Fore_unit *fup = fore_units[unit];
+
+ if (fup == NULL)
+ continue;
+
+ serviced += fore_intr((void *)fup);
+ }
+
+ /*
+ * Indicate if we handled an interrupt
+ */
+ return (serviced ? 1 : 0);
+}
+#endif /* defined(sun) */
+
+
+/*
+ * Device interrupt routine
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * arg pointer to device unit structure
+ *
+ * Returns:
+ * 1 device interrupt was serviced
+ * 0 no interrupts serviced
+ *
+ */
+#if (defined(BSD) && (BSD <= 199306))
+int
+#else
+void
+#endif
+fore_intr(arg)
+ void *arg;
+{
+ Fore_unit *fup = arg;
+ Aali *aap;
+#if (defined(BSD) && (BSD <= 199306))
+ int serviced = 0;
+#endif
+
+ /*
+ * Try to prevent stuff happening after we've paniced
+ */
+ if (panicstr) {
+ goto done;
+ }
+
+ /*
+ * Get to the microcode shared memory interface
+ */
+ if ((aap = fup->fu_aali) == NULL)
+ goto done;
+
+ /*
+ * Has this card issued an interrupt??
+ */
+#ifdef FORE_PCI
+ if (*fup->fu_psr) {
+#else
+ if (aap->aali_intr_sent) {
+#endif
+
+ /*
+ * Indicate that we've serviced an interrupt.
+ */
+#if (defined(BSD) && (BSD <= 199306))
+ serviced = 1;
+#endif
+
+ /*
+ * Clear the device interrupt
+ */
+ switch (fup->fu_config.ac_device) {
+
+#ifdef FORE_SBUS
+ case DEV_FORE_SBA200E:
+ SBA200E_HCR_SET(*fup->fu_ctlreg, SBA200E_CLR_SBUS_INTR);
+ break;
+
+ case DEV_FORE_SBA200:
+ *fup->fu_ctlreg = SBA200_CLR_SBUS_INTR;
+ break;
+#endif
+#ifdef FORE_PCI
+ case DEV_FORE_PCA200E:
+ PCA200E_HCR_SET(*fup->fu_ctlreg, PCA200E_CLR_HBUS_INT);
+ break;
+#endif
+
+ }
+ aap->aali_intr_sent = CP_WRITE(0);
+
+ /*
+ * Reset the watchdog timer
+ */
+ fup->fu_timer = FORE_WATCHDOG;
+
+ /*
+ * Device initialization handled separately
+ */
+ if ((fup->fu_flags & CUF_INITED) == 0) {
+
+ /*
+ * We're just initializing device now, so see if
+ * the initialization command has completed
+ */
+ if (CP_READ(aap->aali_init.init_status) &
+ QSTAT_COMPLETED)
+ fore_initialize_complete(fup);
+
+ /*
+ * If we're still not inited, none of the host
+ * queues are setup yet
+ */
+ if ((fup->fu_flags & CUF_INITED) == 0)
+ goto done;
+ }
+
+ /*
+ * Drain the queues of completed work
+ */
+ fore_cmd_drain(fup);
+ fore_recv_drain(fup);
+ fore_xmit_drain(fup);
+
+ /*
+ * Supply more buffers to the CP
+ */
+ fore_buf_supply(fup);
+ }
+
+done:
+#if (defined(BSD) && (BSD <= 199306))
+ return(serviced);
+#else
+ return;
+#endif
+}
+
+
+/*
+ * Watchdog timeout routine
+ *
+ * Called when we haven't heard from the card in a while. Just in case
+ * we missed an interrupt, we'll drain the queues and try to resupply the
+ * CP with more receive buffers. If the CP is partially wedged, hopefully
+ * this will be enough to get it going again.
+ *
+ * Called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+fore_watchdog(fup)
+ Fore_unit *fup;
+{
+ /*
+ * Try to prevent stuff happening after we've paniced
+ */
+ if (panicstr) {
+ return;
+ }
+
+ /*
+ * Reset the watchdog timer
+ */
+ fup->fu_timer = FORE_WATCHDOG;
+
+ /*
+ * If the device is initialized, nudge it (wink, wink)
+ */
+ if (fup->fu_flags & CUF_INITED) {
+
+ /*
+ * Drain the queues of completed work
+ */
+ fore_cmd_drain(fup);
+ fore_recv_drain(fup);
+ fore_xmit_drain(fup);
+
+ /*
+ * Supply more buffers to the CP
+ */
+ fore_buf_supply(fup);
+ }
+
+ return;
+}
diff --git a/sys/dev/hfa/fore_load.c b/sys/dev/hfa/fore_load.c
new file mode 100644
index 0000000..4250ddc
--- /dev/null
+++ b/sys/dev/hfa/fore_load.c
@@ -0,0 +1,1618 @@
+/*
+ *
+ * ===================================
+ * 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: fore_load.c,v 1.12 1998/06/29 21:42:14 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Loadable kernel module and device identification support
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_load.c,v 1.12 1998/06/29 21:42:14 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Local functions
+ */
+static int fore_start __P((void));
+static int fore_stop __P((void));
+static int fore_doload __P((void));
+static int fore_dounload __P((void));
+#ifdef sun
+static int fore_identify __P((char *));
+static int fore_attach __P((struct devinfo *));
+#endif
+#ifdef __FreeBSD__
+static char * fore_pci_probe __P((pcici_t, pcidi_t));
+static void fore_pci_attach __P((pcici_t, int));
+#if BSD < 199506
+static int fore_pci_shutdown __P((struct kern_devconf *, int));
+#else
+static void fore_pci_shutdown __P((int, void *));
+#endif
+#endif
+static void fore_unattach __P((Fore_unit *));
+static void fore_reset __P((Fore_unit *));
+
+
+/*
+ * Local variables
+ */
+static int fore_inited = 0;
+
+/*
+ * Driver entry points
+ */
+#ifdef sun
+static struct dev_ops fore_ops = {
+ 1, /* revision */
+ fore_identify, /* identify */
+ fore_attach, /* attach */
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* strategy */
+ NULL, /* dump */
+ NULL, /* psize */
+ NULL, /* ioctl */
+ NULL, /* reset */
+ NULL /* mmap */
+};
+#endif
+
+#ifdef __FreeBSD__
+static u_long fore_pci_count = 0;
+
+static struct pci_device fore_pci_device = {
+ FORE_DEV_NAME,
+ fore_pci_probe,
+ fore_pci_attach,
+ &fore_pci_count,
+#if BSD < 199506
+ fore_pci_shutdown
+#else
+ NULL
+#endif
+};
+
+DATA_SET(pcidevice_set, fore_pci_device);
+#endif
+
+
+/*
+ * Initialize driver processing
+ *
+ * This will be called during module loading. Not much to do here, as
+ * we must wait for our identify/attach routines to get called before
+ * we know what we're in for.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 startup was successful
+ * errno startup failed - reason indicated
+ *
+ */
+static int
+fore_start()
+{
+
+ /*
+ * Verify software version
+ */
+ if (atm_version != ATM_VERSION) {
+ log(LOG_ERR, "version mismatch: fore=%d.%d kernel=%d.%d\n",
+ ATM_VERS_MAJ(ATM_VERSION), ATM_VERS_MIN(ATM_VERSION),
+ ATM_VERS_MAJ(atm_version), ATM_VERS_MIN(atm_version));
+ return (EINVAL);
+ }
+
+ /*
+ * Initialize DMA mapping
+ */
+ DMA_INIT();
+
+ /*
+ * Start up watchdog timer
+ */
+ atm_timeout(&fore_timer, ATM_HZ * FORE_TIME_TICK, fore_timeout);
+
+ fore_inited = 1;
+
+ return (0);
+}
+
+
+/*
+ * Halt driver processing
+ *
+ * This will be called just prior to unloading the module from memory.
+ * Everything we've setup since we've been loaded must be undone here.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 shutdown was successful
+ * errno shutdown failed - reason indicated
+ *
+ */
+static int
+fore_stop()
+{
+ int err = 0;
+ int s = splimp();
+ int i;
+
+ /*
+ * Stop the watchdog timer
+ */
+ (void) atm_untimeout(&fore_timer);
+
+ /*
+ * Clean up each device (if any)
+ */
+ for ( i = 0; i < fore_nunits; i++ ) {
+ Fore_unit *fup = fore_units[i];
+
+ if (fup == NULL)
+ continue;
+
+ /*
+ * Deregister device from kernel services
+ */
+ if (err = atm_physif_deregister((Cmn_unit *)fup)) {
+ (void) splx(s);
+ return (err);
+ }
+
+ /*
+ * Unattach the device from the system
+ */
+ fore_unattach(fup);
+
+ /*
+ * Free any Fore-specific device resources
+ */
+ fore_interface_free(fup);
+
+ /*
+ * Free the unit structure
+ */
+ atm_dev_free(fup);
+ fore_units[i] = NULL;
+ }
+
+ fore_nunits = 0;
+
+ /*
+ * Now free our global resources
+ */
+
+ /*
+ * Release our storage pools
+ */
+ atm_release_pool(&fore_vcc_pool);
+ atm_release_pool(&fore_nif_pool);
+
+ /*
+ * Release all DMA mappings
+ */
+ DMA_RELEASE();
+
+ fore_inited = 0;
+
+ (void) splx(s);
+
+ return (0);
+}
+
+
+#ifdef sun
+/*
+ * Device identify routine
+ *
+ * Determine if this driver will support the named device. If we claim to
+ * support the device, our attach routine will (later) be called for the
+ * device.
+ *
+ * Arguments:
+ * name pointer to identifier string from device
+ *
+ * Returns:
+ * 1 driver claims support for this device
+ * 0 device not claimed by this driver
+ *
+ */
+static int
+fore_identify(name)
+ char *name;
+{
+ int ret = 0;
+ int i = 0;
+
+ /*
+ * Initialize driver stuff
+ */
+ if (fore_inited == 0) {
+ if (fore_start())
+ return (0);
+ }
+
+ while (fore_devices[i].fd_name) {
+ if (strcmp(fore_devices[i].fd_name, name) == 0) {
+
+ /*
+ * We support this device!!
+ */
+ if (fore_nunits < FORE_MAX_UNITS) {
+ fore_nunits++;
+ ret = 1;
+ } else {
+ log(LOG_ERR,
+ "fore_identify: Too many devices\n");
+ }
+ break;
+ }
+ i++;
+ }
+ return (ret);
+}
+
+
+/*
+ * Device attach routine
+ *
+ * Attach a device we've previously claimed to support. Walk through its
+ * register set and map, as required. Determine what level the device will
+ * be interrupting at and then register an interrupt handler for it. If we
+ * succeed, then reset the adapter and read useful info from its PROM.
+ * Last, register the interface with the kernel ATM services.
+ *
+ * Arguments:
+ * devinfo_p pointer to device information structure
+ *
+ * Returns:
+ * 0 attach was successful
+ * -1 attach failed
+ *
+ */
+static int
+fore_attach(devinfo_p)
+ struct dev_info *devinfo_p;
+{
+ struct dev_reg *dev_reg_p;
+ struct dev_intr *dev_intr_p;
+ Fore_unit *fup;
+ Atm_config *fcp;
+ addr_t valp;
+ int val;
+ int i;
+ int err_count = BOOT_LOOPS;
+ static int unit = 0;
+
+ /*
+ * Sanity check
+ */
+ if (devinfo_p == NULL)
+ return (-1);
+
+ /*
+ * Make sure this isn't a duplicate unit
+ */
+ if (fore_units[unit] != NULL)
+ return (-1);
+
+ /*
+ * Allocate a new unit structure
+ */
+ fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0);
+ if (fup == NULL)
+ return (-1);
+
+ /*
+ * Start initializing it
+ */
+ fup->fu_unit = unit;
+ fup->fu_mtu = FORE_IFF_MTU;
+ fup->fu_devinfo = devinfo_p;
+ fup->fu_vcc_pool = &fore_vcc_pool;
+ fup->fu_nif_pool = &fore_nif_pool;
+ fup->fu_ioctl = fore_atm_ioctl;
+ fup->fu_instvcc = fore_instvcc;
+ fup->fu_openvcc = fore_openvcc;
+ fup->fu_closevcc = fore_closevcc;
+ fup->fu_output = fore_output;
+
+ /*
+ * Consider this unit assigned
+ */
+ fore_units[unit] = fup;
+ unit++;
+
+ ATM_DEBUG1("fore_attach: fup=0x%x\n", (int)fup);
+ ATM_DEBUG2("\tfu_xmit_q=0x%x fu_xmit_head=0x%x\n",
+ (int)fup->fu_xmit_q, (int)&fup->fu_xmit_head);
+ ATM_DEBUG2("\tfu_recv_q=0x%x fu_recv_head=0x%x\n",
+ (int)fup->fu_recv_q, (int)&fup->fu_recv_head);
+ ATM_DEBUG2("\tfu_buf1s_q=0x%x fu_buf1s_head=0x%x\n",
+ (int)fup->fu_buf1s_q, (int)&fup->fu_buf1s_head);
+ ATM_DEBUG2("\tfu_buf1l_q=0x%x fu_buf1l_head=0x%x\n",
+ (int)fup->fu_buf1l_q, (int)&fup->fu_buf1l_head);
+ ATM_DEBUG2("\tfu_cmd_q=0x%x fu_cmd_head=0x%x\n",
+ (int)fup->fu_cmd_q, (int)&fup->fu_cmd_head);
+ ATM_DEBUG1("\tfu_stats=0x%x\n",
+ (int)&fup->fu_stats);
+
+ /*
+ * Tell kernel our unit number
+ */
+ devinfo_p->devi_unit = fup->fu_unit;
+
+ /*
+ * Figure out what type of device we've got. This should always
+ * work since we've already done this at identify time!
+ */
+ i = 0;
+ while (fore_devices[i].fd_name) {
+ if (strcmp(fore_devices[i].fd_name, devinfo_p->devi_name) == 0)
+ break;
+ i++;
+ }
+ if (fore_devices[i].fd_name == NULL)
+ return (-1);
+
+ fup->fu_config.ac_device = fore_devices[i].fd_devtyp;
+
+ /*
+ * Walk through the OPENPROM register information
+ * mapping register banks as they are found.
+ */
+ for ( dev_reg_p = devinfo_p->devi_reg, i = 1;
+ i <= devinfo_p->devi_nreg; i++, ++dev_reg_p )
+ {
+ if ( dev_reg_p == NULL )
+ {
+ /*
+ * Can't happen...
+ */
+ return ( -1 );
+ }
+
+ /*
+ * Each device type has different register sets
+ */
+ switch (fup->fu_config.ac_device) {
+
+#ifdef FORE_SBUS
+ case DEV_FORE_SBA200E:
+
+ switch ( i )
+ {
+ /*
+ * Host Control Register (HCR)
+ */
+ case 1:
+ if ( sizeof(Fore_reg) != dev_reg_p->reg_size )
+ {
+ return ( -1 );
+ }
+ fup->fu_ctlreg = (Fore_reg *)
+ map_regs ( dev_reg_p->reg_addr,
+ sizeof(Fore_reg),
+ dev_reg_p->reg_bustype );
+ if ( fup->fu_ctlreg == NULL )
+ {
+ return ( -1 );
+ }
+ break;
+
+ /*
+ * SBus Burst Transfer Configuration Register
+ */
+ case 2:
+ /*
+ * Not used
+ */
+ break;
+
+ /*
+ * SBus Interrupt Level Select Register
+ */
+ case 3:
+ if ( sizeof (Fore_reg) != dev_reg_p->reg_size )
+ {
+ return ( -1 );
+ }
+ fup->fu_intlvl = (Fore_reg *)
+ map_regs ( dev_reg_p->reg_addr,
+ sizeof(Fore_reg),
+ dev_reg_p->reg_bustype );
+ if ( fup->fu_intlvl == NULL )
+ {
+ return ( -1 );
+ }
+ break;
+
+ /*
+ * i960 RAM
+ */
+ case 4:
+ fup->fu_ram = (Fore_mem *)
+ map_regs ( dev_reg_p->reg_addr,
+ dev_reg_p->reg_size,
+ dev_reg_p->reg_bustype );
+ if ( fup->fu_ram == NULL )
+ {
+ return ( -1 );
+ }
+ fup->fu_ramsize = dev_reg_p->reg_size;
+
+ /*
+ * Various versions of the Sun PROM mess with
+ * the reg_addr value in unpredictable (to me,
+ * at least) ways, so just use the "memoffset"
+ * property, which should give us the RAM
+ * offset directly.
+ */
+ val = getprop(devinfo_p->devi_nodeid,
+ "memoffset", -1);
+ if (val == -1) {
+ return (-1);
+ }
+ fup->fu_config.ac_ram = val;
+ fup->fu_config.ac_ramsize = fup->fu_ramsize;
+
+ /*
+ * Set monitor interface for initializing
+ */
+ fup->fu_mon = (Mon960 *)
+ (fup->fu_ram + MON960_BASE);
+ break;
+
+ default:
+ log(LOG_ERR,
+ "fore_attach: Too many registers\n");
+ return ( -1 );
+ }
+ break;
+
+ case DEV_FORE_SBA200:
+
+ switch ( i )
+ {
+ /*
+ * Board Control Register (BCR)
+ */
+ case 1:
+ if ( sizeof(Fore_reg) != dev_reg_p->reg_size )
+ {
+ return ( -1 );
+ }
+ fup->fu_ctlreg = (Fore_reg *)
+ map_regs ( dev_reg_p->reg_addr,
+ sizeof(Fore_reg),
+ dev_reg_p->reg_bustype );
+ if ( fup->fu_ctlreg == NULL )
+ {
+ return ( -1 );
+ }
+ break;
+
+ /*
+ * i960 RAM
+ */
+ case 2:
+ fup->fu_ram = (Fore_mem *)
+ map_regs ( dev_reg_p->reg_addr,
+ dev_reg_p->reg_size,
+ dev_reg_p->reg_bustype );
+ if ( fup->fu_ram == NULL )
+ {
+ return ( -1 );
+ }
+ fup->fu_ramsize = dev_reg_p->reg_size;
+
+ /*
+ * Various versions of the Sun PROM mess with
+ * the reg_addr value in unpredictable (to me,
+ * at least) ways, so just use the "memoffset"
+ * property, which should give us the RAM
+ * offset directly.
+ */
+ val = getprop(devinfo_p->devi_nodeid,
+ "memoffset", -1);
+ if (val == -1) {
+ return (-1);
+ }
+ fup->fu_config.ac_ram = val;
+ fup->fu_config.ac_ramsize = fup->fu_ramsize;
+
+ /*
+ * Set monitor interface for initializing
+ */
+ fup->fu_mon = (Mon960 *)
+ (fup->fu_ram + MON960_BASE);
+ break;
+
+ default:
+ log(LOG_ERR,
+ "fore_attach: Too many registers\n");
+ return ( -1 );
+ }
+ break;
+#endif /* FORE_SBUS */
+
+ default:
+ log(LOG_ERR,
+ "fore_attach: Unsupported device type %d\n",
+ fup->fu_config.ac_device);
+ return (-1);
+ }
+ }
+
+ /*
+ * Install the device in the interrupt chain.
+ *
+ * dev_intr_p may be null IFF devi_nintr is zero.
+ */
+ dev_intr_p = devinfo_p->devi_intr;
+ for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p )
+ {
+
+ if ( dev_intr_p == NULL )
+ {
+ /*
+ * Can't happen.
+ */
+ return ( -1 );
+ }
+
+ /*
+ * Convert hardware ipl (0-15) into spl level.
+ */
+ if ( ipltospl ( dev_intr_p->int_pri ) > fup->fu_intrpri )
+ {
+ fup->fu_intrpri = ipltospl ( dev_intr_p->int_pri );
+
+ /*
+ * If SBA-200E card, set SBus interrupt level
+ * into board register
+ */
+ if ( fup->fu_intlvl ) {
+#if defined(sun4c)
+ *(fup->fu_intlvl) = dev_intr_p->int_pri;
+#elif defined(sun4m)
+ extern int svimap[];
+
+ *(fup->fu_intlvl) =
+ svimap[dev_intr_p->int_pri & 0xf];
+#else
+ #error PORT ME;
+#endif
+ }
+ }
+
+ DEVICE_LOCK((Cmn_unit *)fup);
+
+ /*
+ * Register our interrupt routine.
+ */
+ (void) addintr ( dev_intr_p->int_pri, fore_poll,
+ devinfo_p->devi_name, devinfo_p->devi_unit );
+
+ /*
+ * If we can do DMA (we can), then DVMA routines need
+ * to know the highest IPL level we will interrupt at.
+ */
+ adddma ( dev_intr_p->int_pri );
+
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+ }
+
+ /*
+ * Poke the hardware...boot the CP and prepare it for downloading
+ */
+ fore_reset(fup);
+
+ switch (fup->fu_config.ac_device) {
+
+#ifdef FORE_SBUS
+ case DEV_FORE_SBA200E:
+ /*
+ * Enable interrupts
+ */
+ SBA200E_HCR_SET(*fup->fu_ctlreg, SBA200E_SBUS_ENA);
+ break;
+#endif /* FORE_SBUS */
+ }
+
+ /*
+ * Wait for monitor to perform self-test
+ */
+ while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) {
+ if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) {
+ log(LOG_ERR, "fore_attach: Unit %d failed self-test\n",
+ fup->fu_unit);
+ return (-1);
+
+ } else if ( --err_count == 0 ) {
+ log(LOG_ERR, "fore_attach: Unit %d unable to boot\n",
+ fup->fu_unit);
+ return (-1);
+ }
+ DELAY ( BOOT_DELAY );
+ }
+
+ /*
+ * Write a one line message to the console informing
+ * that we've attached the device.
+ */
+ report_dev ( devinfo_p );
+
+ /*
+ * Get the mac address from the card PROM
+ */
+ val = getprop ( devinfo_p->devi_nodeid, "macaddress1", -1 );
+ if ( val != -1 ) {
+ fup->fu_pif.pif_macaddr.ma_data[0] = val & 0xff;
+ val = getprop ( devinfo_p->devi_nodeid, "macaddress2", -1 );
+ fup->fu_pif.pif_macaddr.ma_data[1] = val & 0xff;
+ val = getprop ( devinfo_p->devi_nodeid, "macaddress3", -1 );
+ fup->fu_pif.pif_macaddr.ma_data[2] = val & 0xff;
+ val = getprop ( devinfo_p->devi_nodeid, "macaddress4", -1 );
+ fup->fu_pif.pif_macaddr.ma_data[3] = val & 0xff;
+ val = getprop ( devinfo_p->devi_nodeid, "macaddress5", -1 );
+ fup->fu_pif.pif_macaddr.ma_data[4] = val & 0xff;
+ val = getprop ( devinfo_p->devi_nodeid, "macaddress6", -1 );
+ fup->fu_pif.pif_macaddr.ma_data[5] = val & 0xff;
+ } else {
+ /*
+ * Newer PROM - mac addresses have been combined. Also,
+ * macaddrlo2 reflects the board serial number.
+ */
+ val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrlo2", -1));
+ KM_COPY ( (caddr_t)&val,
+ (caddr_t)&fup->fu_pif.pif_macaddr.ma_data[2],
+ sizeof(val) );
+ val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrhi4", -1));
+ KM_COPY ( (caddr_t)&val,
+ (caddr_t)fup->fu_pif.pif_macaddr.ma_data,
+ sizeof(val) );
+ }
+
+ /*
+ * Setup the adapter config info
+ */
+ fcp = &fup->fu_config;
+ fcp->ac_vendor = VENDOR_FORE;
+ fcp->ac_vendapi = VENDAPI_FORE_1;
+ fcp->ac_macaddr = fup->fu_pif.pif_macaddr;
+ val = getprop ( devinfo_p->devi_nodeid, "promversion", -1 );
+ if ( val == -1 ) {
+ val = getprop ( devinfo_p->devi_nodeid, "hw-version", -1 );
+ }
+ if (val != -1) {
+ sprintf(fcp->ac_hard_vers, "%d.%d.%d",
+ (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
+ } else
+ sprintf(fcp->ac_hard_vers, "Unknown");
+
+ val = getprop ( devinfo_p->devi_nodeid, "serialnumber", -1 );
+ if ( val != -1 )
+ fcp->ac_serial = val;
+
+ valp = (addr_t)getlongprop ( devinfo_p->devi_nodeid, "model" );
+ if ( valp )
+ {
+ /*
+ * Media Type
+ */
+ switch (fcp->ac_device) {
+
+#ifdef FORE_SBUS
+ case DEV_FORE_SBA200E:
+ fcp->ac_media = MEDIA_OC3C;
+ fup->fu_pif.pif_pcr = ATM_PCR_OC3C;
+ break;
+
+ case DEV_FORE_SBA200:
+ /*
+ * Look at the /SSS trailer to determine 4B5B speed
+ * TAXI-100 = 125; TAXI-140 = 175
+ * Assume that OC3 has no /SSS speed identifier.
+ */
+ while (*valp && *valp != '/')
+ valp++;
+ if (*valp == NULL) {
+ fcp->ac_media = MEDIA_OC3C;
+ fup->fu_pif.pif_pcr = ATM_PCR_OC3C;
+ } else if (strcmp(valp, "/125") == 0) {
+ fcp->ac_media = MEDIA_TAXI_100;
+ fup->fu_pif.pif_pcr = ATM_PCR_TAXI100;
+ } else {
+ fcp->ac_media = MEDIA_TAXI_140;
+ fup->fu_pif.pif_pcr = ATM_PCR_TAXI140;
+ }
+ break;
+#endif /* FORE_SBUS */
+ }
+
+ /*
+ * Free property space
+ */
+ KM_FREE(valp, getproplen(devinfo_p->devi_nodeid, "model"), 0);
+ }
+
+ /*
+ * Bus information
+ */
+ fcp->ac_busslot =
+#ifdef SBUS_SIZE
+ (long)(devinfo_p->devi_reg->reg_addr - SBUS_BASE) / SBUS_SIZE;
+#else
+ sbusslot((u_long)devinfo_p->devi_reg->reg_addr);
+#endif
+
+ val = getprop(devinfo_p->devi_parent->devi_nodeid, "burst-sizes", 0);
+ if (val & SBUS_BURST32)
+ fcp->ac_bustype = BUS_SBUS_B32;
+ else
+ fcp->ac_bustype = BUS_SBUS_B16;
+
+ /*
+ * Set device capabilities
+ */
+ fup->fu_pif.pif_maxvpi = FORE_MAX_VPI;
+ fup->fu_pif.pif_maxvci = FORE_MAX_VCI;
+
+ /*
+ * Register this interface with ATM core services
+ */
+ if ( atm_physif_register
+ ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 )
+ {
+ /*
+ * Registration failed - back everything out
+ */
+ /*
+ * Modload calls UNLOAD if it get's a failure - don't
+ * call fore_unload() here.
+ */
+ return ( -1 );
+ }
+
+ /*
+ * Initialize the CP microcode program.
+ */
+ fore_initialize(fup);
+
+ return (0);
+}
+#endif /* sun */
+
+
+#ifdef __FreeBSD__
+/*
+ * Device probe routine
+ *
+ * Determine if this driver will support the identified device. If we claim
+ * to support the device, our attach routine will (later) be called for the
+ * device.
+ *
+ * Arguments:
+ * config_id device's PCI configuration ID
+ * device_id device's PCI Vendor/Device ID
+ *
+ * Returns:
+ * name device identification string
+ * NULL device not claimed by this driver
+ *
+ */
+static char *
+fore_pci_probe(config_id, device_id)
+ pcici_t config_id;
+ pcidi_t device_id;
+{
+
+ /*
+ * Initialize driver stuff
+ */
+ if (fore_inited == 0) {
+ if (fore_start())
+ return (NULL);
+ }
+
+ if ((device_id & 0xffff) != FORE_VENDOR_ID)
+ return (NULL);
+
+ if (((device_id >> 16) & 0xffff) == FORE_PCA200E_ID)
+ return ("FORE Systems PCA-200E ATM");
+
+ return (NULL);
+}
+
+
+/*
+ * Device attach routine
+ *
+ * Attach a device we've previously claimed to support. Walk through its
+ * register set and map, as required. Determine what level the device will
+ * be interrupting at and then register an interrupt handler for it. If we
+ * succeed, then reset the adapter and initialize the microcode.
+ * Last, register the interface with the kernel ATM services.
+ *
+ * Arguments:
+ * config_id device's PCI configuration ID
+ * unit device unit number
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+fore_pci_attach(config_id, unit)
+ pcici_t config_id;
+ int unit;
+{
+ Fore_unit *fup;
+ vm_offset_t va;
+ vm_offset_t pa;
+ pcidi_t device_id;
+ long val;
+ int err_count = BOOT_LOOPS;
+
+ /*
+ * Just checking...
+ */
+ if (unit >= FORE_MAX_UNITS) {
+ log(LOG_ERR, "%s%d: too many devices\n",
+ FORE_DEV_NAME, unit);
+ return;
+ }
+
+ /*
+ * Make sure this isn't a duplicate unit
+ */
+ if (fore_units[unit] != NULL)
+ return;
+
+ /*
+ * Allocate a new unit structure
+ */
+ fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0);
+ if (fup == NULL)
+ return;
+
+ /*
+ * Start initializing it
+ */
+ fup->fu_unit = unit;
+ fup->fu_mtu = FORE_IFF_MTU;
+ fup->fu_pcitag = config_id;
+ fup->fu_vcc_pool = &fore_vcc_pool;
+ fup->fu_nif_pool = &fore_nif_pool;
+ fup->fu_ioctl = fore_atm_ioctl;
+ fup->fu_instvcc = fore_instvcc;
+ fup->fu_openvcc = fore_openvcc;
+ fup->fu_closevcc = fore_closevcc;
+ fup->fu_output = fore_output;
+ callout_handle_init(&fup->fu_thandle);
+
+ /*
+ * Get our device type
+ */
+ device_id = pci_conf_read ( config_id, PCI_ID_REG );
+ switch ((device_id >> 16) & 0xffff) {
+
+ case FORE_PCA200E_ID:
+ fup->fu_config.ac_device = DEV_FORE_PCA200E;
+ break;
+
+ default:
+ fup->fu_config.ac_device = DEV_UNKNOWN;
+ }
+
+ /*
+ * Map RAM
+ */
+ if ((pci_map_mem(config_id, PCA200E_PCI_MEMBASE, &va, &pa)) == 0) {
+ log(LOG_ERR, "%s%d: unable to map memory\n",
+ FORE_DEV_NAME, unit);
+ goto failed;
+ }
+ fup->fu_ram = (Fore_mem *)va;
+ fup->fu_ramsize = PCA200E_RAM_SIZE;
+ fup->fu_mon = (Mon960 *)(fup->fu_ram + MON960_BASE);
+ fup->fu_ctlreg = (Fore_reg *)(va + PCA200E_HCR_OFFSET);
+ fup->fu_imask = (Fore_reg *)(va + PCA200E_IMASK_OFFSET);
+ fup->fu_psr = (Fore_reg *)(va + PCA200E_PSR_OFFSET);
+
+ /*
+ * Convert Endianess of Slave RAM accesses
+ */
+ val = pci_conf_read(config_id, PCA200E_PCI_MCTL);
+ val |= PCA200E_MCTL_SWAP;
+ pci_conf_write(config_id, PCA200E_PCI_MCTL, val);
+
+ /*
+ * Map interrupt in
+ */
+ if ( !pci_map_int( config_id, fore_intr, fup, &net_imask ) ) {
+ log(LOG_ERR, "%s%d: unable to map interrupt\n",
+ FORE_DEV_NAME, unit);
+ goto failed;
+ }
+
+ /*
+ * Poke the hardware - boot the CP and prepare it for downloading
+ */
+ fore_reset(fup);
+
+ /*
+ * Wait for the monitor to perform self-test
+ */
+ while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) {
+ if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) {
+ log(LOG_ERR, "%s%d: failed self-test\n",
+ FORE_DEV_NAME, unit);
+ goto failed;
+ } else if ( --err_count == 0 ) {
+ log(LOG_ERR, "%s%d: unable to boot - status=0x%x\n",
+ FORE_DEV_NAME, unit,
+ CP_READ(fup->fu_mon->mon_bstat));
+ goto failed;
+ }
+ DELAY ( BOOT_DELAY );
+ }
+
+ /*
+ * Setup the adapter config info - at least as much as we can
+ */
+ fup->fu_config.ac_vendor = VENDOR_FORE;
+ fup->fu_config.ac_vendapi = VENDAPI_FORE_1;
+ fup->fu_config.ac_media = MEDIA_OC3C;
+ fup->fu_pif.pif_pcr = ATM_PCR_OC3C;
+ fup->fu_config.ac_bustype = BUS_PCI;
+ fup->fu_config.ac_busslot = config_id->bus << 8 | config_id->slot;
+
+ /*
+ * Save device ram info for user-level programs
+ */
+ fup->fu_config.ac_ram = (long)fup->fu_ram;
+ fup->fu_config.ac_ramsize = fup->fu_ramsize;
+
+ /*
+ * Set device capabilities
+ */
+ fup->fu_pif.pif_maxvpi = FORE_MAX_VPI;
+ fup->fu_pif.pif_maxvci = FORE_MAX_VCI;
+
+ /*
+ * Register this interface with ATM core services
+ */
+ if ( atm_physif_register
+ ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 )
+ {
+ /*
+ * Registration failed - back everything out
+ */
+ goto failed;
+ }
+
+ fore_units[unit] = fup;
+ fore_nunits++;
+
+#if BSD >= 199506
+ /*
+ * Add hook to our shutdown function
+ */
+ at_shutdown(fore_pci_shutdown, fup, SHUTDOWN_POST_SYNC);
+#endif
+
+ /*
+ * Initialize the CP microcode program.
+ */
+ fore_initialize(fup);
+
+ return;
+
+failed:
+ /*
+ * Unattach the device from the system
+ */
+ fore_unattach(fup);
+
+ /*
+ * Free any Fore-specific device resources
+ */
+ fore_interface_free(fup);
+
+ atm_dev_free(fup);
+
+ return;
+}
+
+
+#if BSD < 199506
+/*
+ * Device shutdown routine
+ *
+ * Arguments:
+ * kdc pointer to device's configuration table
+ * force forced shutdown flag
+ *
+ * Returns:
+ * none
+ *
+ */
+static int
+fore_pci_shutdown(kdc, force)
+ struct kern_devconf *kdc;
+ int force;
+{
+ Fore_unit *fup;
+
+ if (kdc->kdc_unit < fore_nunits) {
+
+ fup = fore_units[kdc->kdc_unit];
+ if (fup != NULL) {
+ fore_reset(fup);
+ }
+ }
+
+ (void) dev_detach(kdc);
+ return (0);
+}
+#else
+/*
+ * Device shutdown routine
+ *
+ * Arguments:
+ * howto type of shutdown
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+fore_pci_shutdown(howto, fup)
+ int howto;
+ void *fup;
+{
+
+ fore_reset((Fore_unit *) fup);
+
+ return;
+}
+#endif /* BSD < 199506 */
+#endif /* __FreeBSD__ */
+
+
+/*
+ * Device unattach routine
+ *
+ * Reset the physical device, remove any pending timeouts,
+ * unmap any register sets, and unregister any interrupts.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+static void
+fore_unattach(fup)
+ Fore_unit *fup;
+{
+#ifdef sun
+ struct dev_info *devinfo_p = fup->fu_devinfo;
+ struct dev_reg *dev_reg_p;
+ struct dev_intr *dev_intr_p;
+#endif
+ int i;
+
+
+ /*
+ * Reset the board and return it to cold_start state.
+ * Hopefully, this will prevent use of resources as
+ * we're trying to free things up.
+ */
+ fore_reset(fup);
+
+ /*
+ * Lock out all device interrupts
+ */
+ DEVICE_LOCK((Cmn_unit *)fup);
+
+ /*
+ * Remove any pending timeout()'s
+ */
+ (void)untimeout((KTimeout_ret(*) __P((void *)))fore_initialize,
+ (void *)fup, fup->fu_thandle);
+
+#ifdef sun
+ /*
+ * Remove any mappings of the device
+ */
+ for ( dev_reg_p = devinfo_p->devi_reg, i = 1;
+ i <= devinfo_p->devi_nreg; i++, ++dev_reg_p )
+ {
+ if ( dev_reg_p == NULL )
+ {
+ /*
+ * Can't happen...
+ */
+ break;
+ }
+
+ /*
+ * Each device type has different register sets
+ */
+ switch (fup->fu_config.ac_device) {
+
+#ifdef FORE_SBUS
+ case DEV_FORE_SBA200E:
+
+ switch ( i )
+ {
+ /*
+ * Host Control Register (HCR)
+ */
+ case 1:
+ unmap_regs((addr_t)fup->fu_ctlreg,
+ sizeof(Fore_reg));
+ break;
+
+ /*
+ * SBus Burst Transfer Configuration Register
+ */
+ case 2:
+ /*
+ * Not used
+ */
+ break;
+
+ /*
+ * SBus Interrupt Level Select Register
+ */
+ case 3:
+ unmap_regs((addr_t)fup->fu_intlvl,
+ sizeof(Fore_reg));
+ break;
+
+ /*
+ * i960 RAM
+ */
+ case 4:
+ unmap_regs((addr_t)fup->fu_ram,
+ fup->fu_ramsize);
+ break;
+ }
+ break;
+
+ case DEV_FORE_SBA200:
+
+ switch ( i )
+ {
+ /*
+ * Board Control Register (BCR)
+ */
+ case 1:
+ unmap_regs((addr_t)fup->fu_ctlreg,
+ sizeof(Fore_reg));
+ break;
+
+ /*
+ * i960 RAM
+ */
+ case 2:
+ unmap_regs((addr_t)fup->fu_ram,
+ fup->fu_ramsize);
+ break;
+ }
+ break;
+#endif /* FORE_SBUS */
+ }
+ }
+
+ /*
+ * Remove the interrupt vector(s)
+ */
+ dev_intr_p = devinfo_p->devi_intr;
+ for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p )
+ {
+ if ( dev_intr_p == NULL )
+ {
+ /*
+ * Can't happen...
+ */
+ break;
+ }
+ (void) remintr ( dev_intr_p->int_pri, fore_poll );
+ }
+#endif /* sun */
+
+#ifdef __FreeBSD__
+ /*
+ * Unmap the device interrupt
+ */
+ (void) pci_unmap_int(fup->fu_pcitag);
+
+ /*
+ * Unmap memory
+ */
+#ifdef notdef
+ (void) pci_unmap_mem(fup->fu_pcitag, PCA200E_PCI_MEMBASE);
+#endif
+#endif /* __FreeBSD__ */
+
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+}
+
+
+/*
+ * Device reset routine
+ *
+ * Reset the physical device
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+static void
+fore_reset(fup)
+ Fore_unit *fup;
+{
+ int s = splimp();
+
+ /*
+ * Reset the board and return it to cold_start state
+ */
+ if (fup->fu_mon)
+ fup->fu_mon->mon_bstat = CP_WRITE(BOOT_COLDSTART);
+
+ if (fup->fu_ctlreg) {
+
+ switch (fup->fu_config.ac_device) {
+
+#ifdef FORE_SBUS
+ case DEV_FORE_SBA200E:
+ /*
+ * Reset i960 by setting and clearing RESET
+ */
+ SBA200E_HCR_INIT(*fup->fu_ctlreg, SBA200E_RESET);
+ SBA200E_HCR_CLR(*fup->fu_ctlreg, SBA200E_RESET);
+ break;
+
+ case DEV_FORE_SBA200:
+ /*
+ * Reset i960 by setting and clearing RESET
+ *
+ * SBA200 will NOT reset if bit is OR'd in!
+ */
+ *fup->fu_ctlreg = SBA200_RESET;
+ *fup->fu_ctlreg = SBA200_RESET_CLR;
+ break;
+#endif /* FORE_SBUS */
+#ifdef FORE_PCI
+ case DEV_FORE_PCA200E:
+ /*
+ * Reset i960 by setting and clearing RESET
+ */
+ PCA200E_HCR_INIT(*fup->fu_ctlreg, PCA200E_RESET);
+ DELAY(10000);
+ PCA200E_HCR_CLR(*fup->fu_ctlreg, PCA200E_RESET);
+ break;
+
+#endif
+ }
+ }
+
+ (void) splx(s);
+ return;
+}
+
+
+#ifndef ATM_LINKED
+/*
+ *******************************************************************
+ *
+ * Loadable Module Support
+ *
+ *******************************************************************
+ */
+
+/*
+ * Generic module load processing
+ *
+ * This function is called by an OS-specific function when this
+ * module is being loaded.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 load was successful
+ * errno load failed - reason indicated
+ *
+ */
+static int
+fore_doload()
+{
+ int err = 0;
+
+ /*
+ * Start us up
+ */
+ err = fore_start();
+ if (err)
+ /* Problems, clean up */
+ (void)fore_stop();
+
+ return (err);
+}
+
+
+/*
+ * Generic module unload processing
+ *
+ * This function is called by an OS-specific function when this
+ * module is being unloaded.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 unload was successful
+ * errno unload failed - reason indicated
+ *
+ */
+static int
+fore_dounload()
+{
+ int err = 0;
+
+ /*
+ * OK, try to clean up our mess
+ */
+ err = fore_stop();
+
+ return (err);
+}
+
+
+#ifdef sun
+/*
+ * Loadable driver description
+ */
+static struct vdldrv fore_drv = {
+ VDMAGIC_DRV, /* Device Driver */
+ "fore_mod", /* name */
+ &fore_ops, /* dev_ops */
+ NULL, /* bdevsw */
+ NULL, /* cdevsw */
+ 0, /* blockmajor */
+ 0 /* charmajor */
+};
+
+
+/*
+ * Loadable module support entry point
+ *
+ * This is the routine called by the vd driver for all loadable module
+ * functions for this pseudo driver. This routine name must be specified
+ * on the modload(1) command. This routine will be called whenever the
+ * modload(1), modunload(1) or modstat(1) commands are issued for this
+ * module.
+ *
+ * Arguments:
+ * cmd vd command code
+ * vdp pointer to vd driver's structure
+ * vdi pointer to command-specific vdioctl_* structure
+ * vds pointer to status structure (VDSTAT only)
+ *
+ * Returns:
+ * 0 command was successful
+ * errno command failed - reason indicated
+ *
+ */
+int
+fore_mod(cmd, vdp, vdi, vds)
+ int cmd;
+ struct vddrv *vdp;
+ caddr_t vdi;
+ struct vdstat *vds;
+{
+ int err = 0;
+
+ switch (cmd) {
+
+ case VDLOAD:
+ /*
+ * Module Load
+ *
+ * We dont support any user configuration
+ */
+ err = fore_doload();
+ if (err == 0)
+ /* Let vd driver know about us */
+ vdp->vdd_vdtab = (struct vdlinkage *)&fore_drv;
+ break;
+
+ case VDUNLOAD:
+ /*
+ * Module Unload
+ */
+ err = fore_dounload();
+ break;
+
+ case VDSTAT:
+ /*
+ * Module Status
+ */
+
+ /* Not much to say at the moment */
+
+ break;
+
+ default:
+ log(LOG_ERR, "fore_mod: Unknown vd command 0x%x\n", cmd);
+ err = EINVAL;
+ }
+
+ return (err);
+}
+#endif /* sun */
+
+#ifdef __FreeBSD__
+#ifdef notdef
+
+/*
+ * Driver entry points
+ */
+static struct cdevsw fore_cdev = {
+ (d_open_t *)enodev, /* open */
+ (d_close_t *)enodev, /* close */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* ioctl */
+ NULL, /* stop */
+ NULL, /* reset */
+ NULL, /* devtotty */
+ NULL, /* select */
+ NULL, /* mmap */
+ NULL /* strategy */
+};
+
+
+/*
+ * Loadable device driver module description
+ */
+#if BSD < 199506
+MOD_DEV("fore_mod", LM_DT_CHAR, -1, (void *)&fore_cdev);
+#else
+MOD_DEV(fore, LM_DT_CHAR, -1, (void *)&fore_cdev);
+#endif
+
+
+/*
+ * Loadable module support "load" entry point
+ *
+ * This is the routine called by the lkm driver whenever the
+ * modload(1) command is issued for this module.
+ *
+ * Arguments:
+ * lkmtp pointer to lkm drivers's structure
+ * cmd lkm command code
+ *
+ * Returns:
+ * 0 command was successful
+ * errno command failed - reason indicated
+ *
+ */
+static int
+fore_load(lkmtp, cmd)
+ struct lkm_table *lkmtp;
+ int cmd;
+{
+ return(fore_doload());
+}
+
+
+/*
+ * Loadable module support "unload" entry point
+ *
+ * This is the routine called by the lkm driver whenever the
+ * modunload(1) command is issued for this module.
+ *
+ * Arguments:
+ * lkmtp pointer to lkm drivers's structure
+ * cmd lkm command code
+ *
+ * Returns:
+ * 0 command was successful
+ * errno command failed - reason indicated
+ *
+ */
+static int
+fore_unload(lkmtp, cmd)
+ struct lkm_table *lkmtp;
+ int cmd;
+{
+ return(fore_dounload());
+}
+
+
+/*
+ * Loadable module support entry point
+ *
+ * This is the routine called by the lkm driver for all loadable module
+ * functions for this driver. This routine name must be specified
+ * on the modload(1) command. This routine will be called whenever the
+ * modload(1), modunload(1) or modstat(1) commands are issued for this
+ * module.
+ *
+ * Arguments:
+ * lkmtp pointer to lkm drivers's structure
+ * cmd lkm command code
+ * ver lkm version
+ *
+ * Returns:
+ * 0 command was successful
+ * errno command failed - reason indicated
+ *
+ */
+int
+fore_mod(lkmtp, cmd, ver)
+ struct lkm_table *lkmtp;
+ int cmd;
+ int ver;
+{
+#if BSD < 199506
+ DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, nosys);
+#else
+ DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, lkm_nullcmd);
+#endif
+}
+#endif /* notdef */
+#endif /* __FreeBSD__ */
+
+#endif /* ATM_LINKED */
+
diff --git a/sys/dev/hfa/fore_output.c b/sys/dev/hfa/fore_output.c
new file mode 100644
index 0000000..59c82c9
--- /dev/null
+++ b/sys/dev/hfa/fore_output.c
@@ -0,0 +1,415 @@
+/*
+ *
+ * ===================================
+ * 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: fore_output.c,v 1.7 1998/02/19 20:10:34 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * PDU output processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_output.c,v 1.7 1998/02/19 20:10:34 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Local functions
+ */
+static KBuffer * fore_xmit_segment __P((Fore_unit *, KBuffer *,
+ H_xmit_queue *, u_int *, u_int *));
+
+
+/*
+ * 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 Fore-specific details.
+ * Then we just build a transmit descriptor request for the PDU and issue
+ * the command to the CP.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ * m pointer to output PDU buffer chain head
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+fore_output(cup, cvp, m)
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+ KBuffer *m;
+{
+ Fore_unit *fup = (Fore_unit *)cup;
+ Fore_vcc *fvp = (Fore_vcc *)cvp;
+ struct vccb *vcp;
+ H_xmit_queue *hxp;
+ Xmit_queue *cqp;
+ Xmit_descr *xdp;
+ u_int retry, nsegs, pdulen;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (atm_dev_print)
+ atm_dev_pdu_print(cup, cvp, m, "fore_output");
+#endif
+
+ vcp = fvp->fv_connvc->cvc_vcc;
+
+ /*
+ * If we're still waiting for activation to finish, delay for
+ * a little while before we toss the PDU
+ */
+ if (fvp->fv_state == CVS_INITED) {
+ retry = 3;
+ while (retry-- && (fvp->fv_state == CVS_INITED))
+ DELAY(1000);
+ if (fvp->fv_state != CVS_ACTIVE) {
+ /*
+ * Activation still hasn't finished, oh well....
+ */
+ fup->fu_stats->st_drv.drv_xm_notact++;
+ vcp->vc_oerrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_oerrors++;
+ KB_FREEALL(m);
+ return;
+ }
+ }
+
+ /*
+ * Queue PDU at end of transmit queue
+ *
+ * If queue is full we'll delay a bit before tossing the PDU
+ */
+ s = splnet();
+ hxp = fup->fu_xmit_tail;
+ if (!((*hxp->hxq_status) & QSTAT_FREE)) {
+
+ fup->fu_stats->st_drv.drv_xm_full++;
+ retry = 3;
+ do {
+ DELAY(1000);
+
+ DEVICE_LOCK((Cmn_unit *)fup);
+ fore_xmit_drain(fup);
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+
+ } while (--retry && (!((*hxp->hxq_status) & QSTAT_FREE)));
+
+ if (!((*hxp->hxq_status) & QSTAT_FREE)) {
+ /*
+ * Queue is still full, bye-bye PDU
+ */
+ fup->fu_pif.pif_oerrors++;
+ vcp->vc_oerrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_oerrors++;
+ KB_FREEALL(m);
+ (void) splx(s);
+ return;
+ }
+ }
+
+ /*
+ * We've got a free transmit queue entry
+ */
+
+ /*
+ * Now build the transmit segment descriptors for this PDU
+ */
+ m = fore_xmit_segment(fup, m, hxp, &nsegs, &pdulen);
+ if (m == NULL) {
+ /*
+ * The build failed, buffer chain has been freed
+ */
+ vcp->vc_oerrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_oerrors++;
+ (void) splx(s);
+ return;
+ }
+
+ /*
+ * Set up the descriptor header
+ */
+ xdp = hxp->hxq_descr;
+ xdp->xd_cell_hdr = ATM_HDR_SET(vcp->vc_vpi, vcp->vc_vci, 0, 0);
+ xdp->xd_spec = XDS_SET_SPEC(0, fvp->fv_aal, nsegs, pdulen);
+ xdp->xd_rate = FORE_DEF_RATE;
+
+ /*
+ * Everything is ready to go, so officially claim the host queue
+ * entry and setup the CP-resident queue entry. The CP will grab
+ * the PDU when the descriptor pointer is set.
+ */
+ fup->fu_xmit_tail = hxp->hxq_next;
+ hxp->hxq_buf = m;
+ hxp->hxq_vcc = fvp;
+ (*hxp->hxq_status) = QSTAT_PENDING;
+ cqp = hxp->hxq_cpelem;
+ cqp->cq_descr = (CP_dma)
+ CP_WRITE((u_long)hxp->hxq_descr_dma | XMIT_SEGS_TO_BLKS(nsegs));
+
+ (void) splx(s);
+
+ /*
+ * See if there are any completed queue entries
+ */
+ DEVICE_LOCK((Cmn_unit *)fup);
+ fore_xmit_drain(fup);
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+
+ return;
+}
+
+
+/*
+ * Build Transmit Segment Descriptors
+ *
+ * This function will take a supplied buffer chain of data to be transmitted
+ * and build the transmit segment descriptors for the data. This will include
+ * the dreaded operation of ensuring that the data for each transmit segment
+ * is full-word aligned and (except for the last segment) is an integral number
+ * of words in length. If the data isn't already aligned and sized as
+ * required, then the data must be shifted (copied) into place - a sure
+ * performance killer. Note that we rely on the fact that all buffer data
+ * areas are allocated with (at least) full-word alignments/lengths.
+ *
+ * If any errors are encountered, the buffer chain will be freed.
+ *
+ * Arguments:
+ * fup pointer to device unit
+ * m pointer to output PDU buffer chain head
+ * hxp pointer to host transmit queue entry
+ * segp pointer to return the number of transmit segments
+ * lenp pointer to return the pdu length
+ *
+ * Returns:
+ * m build successful, pointer to (possibly new) head of
+ * output PDU buffer chain
+ * NULL build failed, buffer chain freed
+ *
+ */
+static KBuffer *
+fore_xmit_segment(fup, m, hxp, segp, lenp)
+ Fore_unit *fup;
+ KBuffer *m;
+ H_xmit_queue *hxp;
+ u_int *segp;
+ u_int *lenp;
+{
+ Xmit_descr *xdp = hxp->hxq_descr;
+ Xmit_seg_descr *xsp;
+ H_dma *sdmap;
+ KBuffer *m0, *m1, *mprev;
+ caddr_t cp, bfr;
+ void *dma;
+ u_int pdulen, nsegs, len, align;
+ int compressed = 0;
+
+ m0 = m;
+
+retry:
+ xsp = xdp->xd_seg;
+ sdmap = hxp->hxq_dma;
+ mprev = NULL;
+ pdulen = 0;
+ nsegs = 0;
+
+ /*
+ * Loop thru each buffer in the chain, performing the necessary
+ * data positioning and then building a segment descriptor for
+ * that data.
+ */
+ while (m) {
+ /*
+ * 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;
+ }
+
+ /*
+ * Make sure we don't try to use too many segments
+ */
+ if (nsegs >= XMIT_MAX_SEGS) {
+ /*
+ * Try to compress buffer chain (but only once)
+ */
+ if (compressed) {
+ KB_FREEALL(m0);
+ return (NULL);
+ }
+
+ fup->fu_stats->st_drv.drv_xm_maxpdu++;
+
+ m = atm_dev_compress(m0);
+ if (m == NULL) {
+ return (NULL);
+ }
+
+ /*
+ * Build segment descriptors for compressed chain
+ */
+ m0 = m;
+ compressed = 1;
+ goto retry;
+ }
+
+ /*
+ * Get start of data onto full-word alignment
+ */
+ KB_DATASTART(m, cp, caddr_t);
+ if (align = ((u_int)cp) & (XMIT_SEG_ALIGN - 1)) {
+ /*
+ * Gotta slide the data up
+ */
+ fup->fu_stats->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 & (XMIT_SEG_ALIGN - 1))) &&
+ (m1 = KB_NEXT(m))) {
+
+ /*
+ * Have to move some data from following buffer(s)
+ * to word-fill this buffer
+ */
+ u_int ncopy = MIN(XMIT_SEG_ALIGN - align, KB_LEN(m1));
+
+ if (ncopy) {
+ /*
+ * Move data to current buffer
+ */
+ caddr_t dest;
+
+ fup->fu_stats->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);
+ }
+ }
+
+ /*
+ * Finally, build the segment descriptor
+ */
+
+ /*
+ * Round last segment to fullword length (if needed)
+ */
+ if (len & (XMIT_SEG_ALIGN - 1))
+ xsp->xsd_len = KB_LEN(m) =
+ (len + XMIT_SEG_ALIGN) & ~(XMIT_SEG_ALIGN - 1);
+ else
+ xsp->xsd_len = KB_LEN(m) = len;
+
+ /*
+ * Get a DMA address for the data
+ */
+ dma = DMA_GET_ADDR(bfr, xsp->xsd_len, XMIT_SEG_ALIGN, 0);
+ if (dma == NULL) {
+
+ fup->fu_stats->st_drv.drv_xm_segdma++;
+ KB_FREEALL(m0);
+ return (NULL);
+ }
+
+ /*
+ * Now we're really ready to call it a segment
+ */
+ *sdmap++ = xsp->xsd_buffer = (H_dma) dma;
+
+ /*
+ * Bump counters and get ready for next buffer
+ */
+ pdulen += len;
+ nsegs++;
+ xsp++;
+ mprev = m;
+ m = KB_NEXT(m);
+ }
+
+ /*
+ * Validate PDU length
+ */
+ if (pdulen > XMIT_MAX_PDULEN) {
+ fup->fu_stats->st_drv.drv_xm_maxpdu++;
+ KB_FREEALL(m0);
+ return (NULL);
+ }
+
+ /*
+ * Return the good news to the caller
+ */
+ *segp = nsegs;
+ *lenp = pdulen;
+
+ return (m0);
+}
+
diff --git a/sys/dev/hfa/fore_receive.c b/sys/dev/hfa/fore_receive.c
new file mode 100644
index 0000000..f9a9d19
--- /dev/null
+++ b/sys/dev/hfa/fore_receive.c
@@ -0,0 +1,582 @@
+/*
+ *
+ * ===================================
+ * 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: fore_receive.c,v 1.10 1998/07/17 20:19:35 root Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Receive queue management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_receive.c,v 1.10 1998/07/17 20:19:35 root Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Local functions
+ */
+static void fore_recv_stack __P((void *, KBuffer *));
+
+
+/*
+ * Allocate Receive Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * 0 allocations successful
+ * else allocation failed
+ */
+int
+fore_recv_allocate(fup)
+ Fore_unit *fup;
+{
+ caddr_t memp;
+
+ /*
+ * Allocate non-cacheable memory for receive status words
+ */
+ memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_recv_stat = (Q_status *) memp;
+
+ memp = DMA_GET_ADDR(fup->fu_recv_stat, sizeof(Q_status) * RECV_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_recv_statd = (Q_status *) memp;
+
+ /*
+ * Allocate memory for receive descriptors
+ */
+ memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,
+ RECV_DESCR_ALIGN, 0);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_recv_desc = (Recv_descr *) memp;
+
+ memp = DMA_GET_ADDR(fup->fu_recv_desc,
+ sizeof(Recv_descr) * RECV_QUELEN, RECV_DESCR_ALIGN, 0);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_recv_descd = (Recv_descr *) memp;
+
+ return (0);
+}
+
+
+/*
+ * Receive Queue Initialization
+ *
+ * Allocate and initialize the host-resident receive queue structures
+ * and then initialize the CP-resident queue structures.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_recv_initialize(fup)
+ Fore_unit *fup;
+{
+ Aali *aap = fup->fu_aali;
+ Recv_queue *cqp;
+ H_recv_queue *hrp;
+ Recv_descr *rdp;
+ Recv_descr *rdp_dma;
+ Q_status *qsp;
+ Q_status *qsp_dma;
+ int i;
+
+ /*
+ * Point to CP-resident receive queue
+ */
+ cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));
+
+ /*
+ * Point to host-resident receive queue structures
+ */
+ hrp = fup->fu_recv_q;
+ qsp = fup->fu_recv_stat;
+ qsp_dma = fup->fu_recv_statd;
+ rdp = fup->fu_recv_desc;
+ rdp_dma = fup->fu_recv_descd;
+
+ /*
+ * Loop thru all queue entries and do whatever needs doing
+ */
+ for (i = 0; i < RECV_QUELEN; i++) {
+
+ /*
+ * Set queue status word to free
+ */
+ *qsp = QSTAT_FREE;
+
+ /*
+ * Set up host queue entry and link into ring
+ */
+ hrp->hrq_cpelem = cqp;
+ hrp->hrq_status = qsp;
+ hrp->hrq_descr = rdp;
+ hrp->hrq_descr_dma = rdp_dma;
+ if (i == (RECV_QUELEN - 1))
+ hrp->hrq_next = fup->fu_recv_q;
+ else
+ hrp->hrq_next = hrp + 1;
+
+ /*
+ * Now let the CP into the game
+ */
+ cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);
+ cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
+
+ /*
+ * Bump all queue pointers
+ */
+ hrp++;
+ qsp++;
+ qsp_dma++;
+ rdp++;
+ rdp_dma++;
+ cqp++;
+ }
+
+ /*
+ * Initialize queue pointers
+ */
+ fup->fu_recv_head = fup->fu_recv_q;
+
+ return;
+}
+
+
+/*
+ * Drain Receive Queue
+ *
+ * This function will process all completed entries at the head of the
+ * receive queue. The received segments will be linked into a received
+ * PDU buffer chain and it will then be passed up the PDU's VCC stack for
+ * processing by the next higher protocol layer.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_recv_drain(fup)
+ Fore_unit *fup;
+{
+ H_recv_queue *hrp = NULL;
+ Recv_descr *rdp;
+ Recv_seg_descr *rsp;
+ Buf_handle *bhp;
+ Fore_vcc *fvp;
+ struct vccb *vcp;
+ KBuffer *m, *mhead, *mtail;
+ caddr_t cp;
+ u_long hdr, nsegs;
+ u_int seglen, type0;
+ int i, pdulen, retries = 0, error;
+
+ /* Silence the compiler */
+ mtail = NULL;
+ type0 = 0;
+
+ /*
+ * Process each completed entry
+ */
+retry:
+ while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {
+
+ /*
+ * Get completed entry's receive descriptor
+ */
+ hrp = fup->fu_recv_head;
+ rdp = hrp->hrq_descr;
+
+#ifdef VAC
+ /*
+ * Cache flush receive descriptor
+ */
+ if (vac) {
+ vac_flush((addr_t)rdp, sizeof(Recv_descr));
+ }
+#endif
+
+ hdr = rdp->rd_cell_hdr;
+ nsegs = rdp->rd_nsegs;
+
+ pdulen = 0;
+ error = 0;
+ mhead = NULL;
+
+ /*
+ * Locate incoming VCC for this PDU
+ */
+ fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,
+ ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);
+
+ /*
+ * Check for a receive error
+ *
+ * Apparently the receive descriptor itself contains valid
+ * information, but the received pdu data is probably bogus.
+ * We'll arrange for the receive buffer segments to be tossed.
+ */
+ if (*hrp->hrq_status & QSTAT_ERROR) {
+
+ fup->fu_pif.pif_ierrors++;
+ if (fvp) {
+ vcp = fvp->fv_connvc->cvc_vcc;
+ vcp->vc_ierrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_ierrors++;
+ }
+ ATM_DEBUG1("fore receive error: hdr=0x%x\n", hdr);
+ error = 1;
+ }
+
+ /*
+ * Build PDU buffer chain from receive segments
+ */
+ for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {
+
+ bhp = rsp->rsd_handle;
+ seglen = rsp->rsd_len;
+
+ /*
+ * Remove buffer from our supplied queue and get
+ * to the underlying buffer
+ */
+ switch (bhp->bh_type) {
+
+ case BHT_S1_SMALL:
+ DEQUEUE(bhp, Buf_handle, bh_qelem,
+ fup->fu_buf1s_bq);
+ fup->fu_buf1s_cnt--;
+ m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);
+ break;
+
+ case BHT_S1_LARGE:
+ DEQUEUE(bhp, Buf_handle, bh_qelem,
+ fup->fu_buf1l_bq);
+ fup->fu_buf1l_cnt--;
+ m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);
+ break;
+
+ default:
+ log(LOG_ERR,
+ "fore_recv_drain: bhp=0x%x type=0x%x\n",
+ (int)bhp, bhp->bh_type);
+ panic("fore_recv_drain: bad buffer type");
+ }
+
+ /*
+ * Toss any zero-length or receive error buffers
+ */
+ if ((seglen == 0) || error) {
+ KB_FREEALL(m);
+ continue;
+ }
+
+ /*
+ * Link buffer into chain
+ */
+ if (mhead == NULL) {
+ type0 = bhp->bh_type;
+ KB_LINKHEAD(m, mhead);
+ mhead = m;
+ } else {
+ KB_LINK(m, mtail);
+ }
+ KB_LEN(m) = seglen;
+ pdulen += seglen;
+ mtail = m;
+
+ /*
+ * Flush received buffer data
+ */
+#ifdef VAC
+ if (vac) {
+ addr_t dp;
+
+ KB_DATASTART(m, dp, addr_t);
+ vac_pageflush(dp);
+ }
+#endif
+ }
+
+ /*
+ * Make sure we've got a non-null PDU
+ */
+ if (mhead == NULL) {
+ goto free_ent;
+ }
+
+ /*
+ * We only support user data PDUs (for now)
+ */
+ if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
+ KB_FREEALL(mhead);
+ goto free_ent;
+ }
+
+ /*
+ * Toss the data if there's no VCC
+ */
+ if (fvp == NULL) {
+ fup->fu_stats->st_drv.drv_rv_novcc++;
+ KB_FREEALL(mhead);
+ goto free_ent;
+ }
+
+#ifdef DIAGNOSTIC
+ if (atm_dev_print)
+ atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp,
+ mhead, "fore_recv");
+#endif
+
+ /*
+ * Make sure we have our queueing headroom at the front
+ * of the buffer chain
+ */
+ if (type0 != BHT_S1_SMALL) {
+
+ /*
+ * Small buffers already have headroom built-in, but
+ * if CP had to use a large buffer for the first
+ * buffer, then we have to allocate a buffer here to
+ * contain the headroom.
+ */
+ fup->fu_stats->st_drv.drv_rv_nosbf++;
+
+ KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
+ if (m == NULL) {
+ fup->fu_stats->st_drv.drv_rv_nomb++;
+ KB_FREEALL(mhead);
+ goto free_ent;
+ }
+
+ /*
+ * Put new buffer at head of PDU chain
+ */
+ KB_LINKHEAD(m, mhead);
+ KB_LEN(m) = 0;
+ KB_HEADSET(m, BUF1_SM_DOFF);
+ mhead = m;
+ }
+
+ /*
+ * It looks like we've got a valid PDU - count it quick!!
+ */
+ KB_PLENSET(mhead, pdulen);
+ fup->fu_pif.pif_ipdus++;
+ fup->fu_pif.pif_ibytes += pdulen;
+ vcp = fvp->fv_connvc->cvc_vcc;
+ 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
+ }
+
+ /*
+ * The STACK_CALL needs to happen at splnet() in order
+ * for the stack sequence processing to work. Schedule an
+ * interrupt queue callback at splnet() since we are
+ * currently at device level.
+ */
+
+ /*
+ * Prepend callback function pointer and token value to buffer.
+ * We have already guaranteed that the space is available
+ * in the first buffer.
+ */
+ KB_HEADADJ(mhead, sizeof(atm_intr_func_t) + sizeof(int));
+ KB_DATASTART(mhead, cp, caddr_t);
+ *((atm_intr_func_t *)cp) = fore_recv_stack;
+ cp += sizeof(atm_intr_func_t);
+ *((void **)cp) = (void *)fvp;
+
+ /*
+ * Schedule callback
+ */
+ if (!IF_QFULL(&atm_intrq)) {
+ IF_ENQUEUE(&atm_intrq, mhead);
+ SCHED_ATM;
+ } else {
+ fup->fu_stats->st_drv.drv_rv_ifull++;
+ KB_FREEALL(mhead);
+ goto free_ent;
+ }
+
+free_ent:
+ /*
+ * Mark this entry free for use and bump head pointer
+ * to the next entry in the queue
+ */
+ *hrp->hrq_status = QSTAT_FREE;
+ hrp->hrq_cpelem->cq_descr =
+ (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);
+ fup->fu_recv_head = hrp->hrq_next;
+ }
+
+ /*
+ * Nearly all of the interrupts generated by the CP will be due
+ * to PDU reception. However, we may receive an interrupt before
+ * the CP has completed the status word DMA to host memory. Thus,
+ * if we haven't processed any PDUs during this interrupt, we will
+ * wait a bit for completed work on the receive queue, rather than
+ * having to field an extra interrupt very soon.
+ */
+ if (hrp == NULL) {
+ if (++retries <= FORE_RECV_RETRY) {
+ DELAY(FORE_RECV_DELAY);
+ goto retry;
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * Pass Incoming PDU up Stack
+ *
+ * This function is called via the core ATM interrupt queue callback
+ * set in fore_recv_drain(). It will pass the supplied incoming
+ * PDU up the incoming VCC's stack.
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * tok token to identify stack instantiation
+ * m pointer to incoming PDU buffer chain
+ *
+ * Returns:
+ * none
+ */
+static void
+fore_recv_stack(tok, m)
+ void *tok;
+ KBuffer *m;
+{
+ Fore_vcc *fvp = (Fore_vcc *)tok;
+ int err;
+
+ /*
+ * Send the data up the stack
+ */
+ STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,
+ fvp->fv_toku, fvp->fv_connvc, (int)m, 0, err);
+ if (err)
+ KB_FREEALL(m);
+
+ return;
+}
+
+
+/*
+ * Free Receive Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_recv_free(fup)
+ Fore_unit *fup;
+{
+ /*
+ * We'll just let fore_buf_free() take care of freeing any
+ * buffers sitting on the receive queue (which are also still
+ * on the fu_*_bq queue).
+ */
+ if (fup->fu_flags & CUF_INITED) {
+ }
+
+ /*
+ * Free the status words
+ */
+ if (fup->fu_recv_stat) {
+ if (fup->fu_recv_statd) {
+ DMA_FREE_ADDR(fup->fu_recv_stat, fup->fu_recv_statd,
+ sizeof(Q_status) * RECV_QUELEN,
+ ATM_DEV_NONCACHE);
+ }
+ atm_dev_free((void *)fup->fu_recv_stat);
+ fup->fu_recv_stat = NULL;
+ fup->fu_recv_statd = NULL;
+ }
+
+ /*
+ * Free the receive descriptors
+ */
+ if (fup->fu_recv_desc) {
+ if (fup->fu_recv_descd) {
+ DMA_FREE_ADDR(fup->fu_recv_desc, fup->fu_recv_descd,
+ sizeof(Recv_descr) * RECV_QUELEN, 0);
+ }
+ atm_dev_free(fup->fu_recv_desc);
+ fup->fu_recv_desc = NULL;
+ fup->fu_recv_descd = NULL;
+ }
+
+ return;
+}
+
diff --git a/sys/dev/hfa/fore_slave.h b/sys/dev/hfa/fore_slave.h
new file mode 100644
index 0000000..05e7b5b
--- /dev/null
+++ b/sys/dev/hfa/fore_slave.h
@@ -0,0 +1,191 @@
+/*
+ *
+ * ===================================
+ * 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: fore_slave.h,v 1.5 1997/08/22 19:45:50 jpt Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Slave Interface definitions
+ *
+ */
+
+#ifndef _FORE_SLAVE_H
+#define _FORE_SLAVE_H
+
+/*
+ * This file contains the (mostly hardware) definitions for each of the
+ * supported 200-series slave interfaces.
+ */
+
+
+/*
+ * Structure defining the supported FORE 200-series interfaces
+ */
+struct fore_device {
+ char *fd_name; /* Device name (from PROM) */
+ Atm_device fd_devtyp; /* Device type */
+};
+typedef struct fore_device Fore_device;
+
+
+
+/*
+ * Common definitions
+ * ------------------
+ */
+#define MON960_BASE 0x400 /* Address offset of Mon960 */
+#define AALI_BASE 0x4d40 /* Address offset of Aali */
+
+typedef volatile unsigned int Fore_reg; /* Slave control register */
+typedef volatile unsigned char Fore_mem; /* Slave memory */
+
+
+/*
+ * SBA-200E SBus Slave Interface
+ * -----------------------------
+ */
+
+#define SBA200E_PROM_NAME "FORE,sba-200e"
+
+/*
+ * SBA-200E Host Control Register (HCR)
+ */
+#define SBA200E_READ_BITS 0x1ff /* Valid read data bits */
+#define SBA200E_WRITE_BITS 0x01f /* Valid write data bits */
+#define SBA200E_STICKY_BITS 0x013 /* Sticky data bits */
+
+/* Read access */
+#define SBA200E_SBUS_INTR_RD 0x100 /* State of SBus interrupt */
+#define SBA200E_TEST_MODE 0x080 /* Device is in test-mode */
+#define SBA200E_IFIFO_FULL 0x040 /* Input FIFO almost full (when 0) */
+#define SBA200E_ESP_HOLD_RD 0x020 /* State of ESP bus hold */
+#define SBA200E_SBUS_ENA_RD 0x010 /* State of SBus interrupt enable */
+#define SBA200E_OFIFO_FULL 0x008 /* Output FIFO almost full */
+#define SBA200E_SELFTEST_FAIL 0x004 /* i960 self-test failed (when 0) */
+#define SBA200E_HOLD_LOCK_RD 0x002 /* State of i960 hold lock signal */
+#define SBA200E_RESET_RD 0x001 /* State of board reset signal */
+
+/* Write access - bit set (clear) */
+#define SBA200E_SBUS_ENA 0x010 /* Enable (disable) SBus interrupts */
+#define SBA200E_CLR_SBUS_INTR 0x008 /* Clear SBus interrupt */
+#define SBA200E_I960_INTR 0x004 /* Issue interrupt to i960 */
+#define SBA200E_HOLD_LOCK 0x002 /* Set (clear) i960 hold lock signal */
+#define SBA200E_RESET 0x001 /* Set (clear) board reset signal */
+
+#define SBA200E_HCR_INIT(hcr,bits) \
+ ((hcr) = (SBA200E_WRITE_BITS & (bits)))
+#define SBA200E_HCR_SET(hcr,bits) \
+ ((hcr) = (((hcr) & SBA200E_STICKY_BITS) | (bits)))
+#define SBA200E_HCR_CLR(hcr,bits) \
+ ((hcr) = ((hcr) & (SBA200E_STICKY_BITS ^ (bits))))
+
+
+
+/*
+ * SBA-200 SBus Slave Interface
+ * ----------------------------
+ */
+
+#define SBA200_PROM_NAME "FORE,sba-200"
+
+/*
+ * SBA-200 Board Control Register (BCR)
+ */
+/* Write access - bit set */
+#define SBA200_CLR_SBUS_INTR 0x04 /* Clear SBus interrupt */
+#define SBA200_RESET 0x01 /* Assert board reset signal */
+
+/* Write access - bit clear */
+#define SBA200_RESET_CLR 0x00 /* Clear board reset signal */
+
+
+
+/*
+ * PCA-200E PCI Bus Slave Interface
+ * --------------------------------
+ */
+
+/*
+ * PCI Identifiers
+ */
+#define FORE_VENDOR_ID 0x1127
+#define FORE_PCA200E_ID 0x0300
+
+/*
+ * PCA-200E PCI Configuration Space
+ */
+#define PCA200E_PCI_MEMBASE 0x10 /* Memory base address */
+#define PCA200E_PCI_MCTL 0x40 /* Master control */
+
+/*
+ * PCA-200E Address Space
+ */
+#define PCA200E_RAM_SIZE 0x100000
+#define PCA200E_HCR_OFFSET 0x100000
+#define PCA200E_IMASK_OFFSET 0x100004
+#define PCA200E_PSR_OFFSET 0x100008
+#define PCA200E_MMAP_SIZE 0x10000c
+
+/*
+ * PCA-200E Master Control
+ */
+#define PCA200E_MCTL_SWAP 0x4000 /* Convert Slave endianess */
+
+/*
+ * PCA-200E Host Control Register (HCR)
+ */
+#define PCA200E_READ_BITS 0x0ff /* Valid read data bits */
+#define PCA200E_WRITE_BITS 0x01f /* Valid write data bits */
+#define PCA200E_STICKY_BITS 0x000 /* Sticky data bits */
+
+/* Read access */
+#define PCA200E_TEST_MODE 0x080 /* Device is in test-mode */
+#define PCA200E_IFIFO_FULL 0x040 /* Input FIFO almost full */
+#define PCA200E_ESP_HOLD_RD 0x020 /* State of ESP hold bus */
+#define PCA200E_OFIFO_FULL 0x010 /* Output FIFO almost full */
+#define PCA200E_HOLD_ACK 0x008 /* State of Hold Ack */
+#define PCA200E_SELFTEST_FAIL 0x004 /* i960 self-test failed */
+#define PCA200E_HOLD_LOCK_RD 0x002 /* State of i960 hold lock signal */
+#define PCA200E_RESET_BD 0x001 /* State of board reset signal */
+
+/* Write access */
+#define PCA200E_CLR_HBUS_INT 0x010 /* Clear host bus interrupt */
+#define PCA200E_I960_INTRA 0x008 /* Set slave interrupt A */
+#define PCA200E_I960_INTRB 0x004 /* Set slave interrupt B */
+#define PCA200E_HOLD_LOCK 0x002 /* Set (clear) i960 hold lock signal */
+#define PCA200E_RESET 0x001 /* Set (clear) board reset signal */
+
+#define PCA200E_HCR_INIT(hcr,bits) \
+ ((hcr) = (PCA200E_WRITE_BITS & (bits)))
+#define PCA200E_HCR_SET(hcr,bits) \
+ ((hcr) = (bits))
+#define PCA200E_HCR_CLR(hcr,bits) \
+ ((hcr) = 0)
+
+#endif /* _FORE_SLAVE_H */
diff --git a/sys/dev/hfa/fore_stats.c b/sys/dev/hfa/fore_stats.c
new file mode 100644
index 0000000..a8271a2
--- /dev/null
+++ b/sys/dev/hfa/fore_stats.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * ===================================
+ * 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: fore_stats.c,v 1.5 1997/08/22 18:41:21 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Device statistics routines
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_stats.c,v 1.5 1997/08/22 18:41:21 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Get device statistics from CP
+ *
+ * This function will issue a GET_STATS command to the CP in order to
+ * initiate the DMA transfer of the CP's statistics structure to the host.
+ * We will then sleep pending command completion. This must only be called
+ * from the ioctl system call handler.
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * 0 stats retrieval successful
+ * errno stats retrieval failed - reason indicated
+ *
+ */
+int
+fore_get_stats(fup)
+ Fore_unit *fup;
+{
+ H_cmd_queue *hcp;
+ Cmd_queue *cqp;
+ int s, sst;
+
+ ATM_DEBUG1("fore_get_stats: fup=0x%x\n", (int)fup);
+
+ /*
+ * Make sure device has been initialized
+ */
+ if ((fup->fu_flags & CUF_INITED) == 0) {
+ return (EIO);
+ }
+
+ /*
+ * If someone has already initiated a stats request, we'll
+ * just wait for that one to complete
+ */
+ s = splimp();
+ if (fup->fu_flags & FUF_STATCMD) {
+
+#if (defined(BSD) && (BSD >= 199103))
+ sst = tsleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH, "fore", 0);
+#else
+ sst = sleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH);
+ if (sst != 0)
+ sst = EINTR;
+#endif
+ (void) splx(s);
+ return (sst ? sst : fup->fu_stats_ret);
+ }
+
+ /*
+ * Limit stats gathering to once a second or so
+ */
+ if (time_second == fup->fu_stats_time) {
+ (void) splx(s);
+ return (0);
+ } else
+ fup->fu_stats_time = time_second;
+
+ /*
+ * Queue command at end of command queue
+ */
+ hcp = fup->fu_cmd_tail;
+ if ((*hcp->hcq_status) & QSTAT_FREE) {
+ void *dma;
+
+ /*
+ * Queue entry available, so set our view of things up
+ */
+ hcp->hcq_code = CMD_GET_STATS;
+ hcp->hcq_arg = NULL;
+ fup->fu_cmd_tail = hcp->hcq_next;
+
+ /*
+ * Now set the CP-resident queue entry - the CP will grab
+ * the command when the op-code is set.
+ */
+ cqp = hcp->hcq_cpelem;
+ (*hcp->hcq_status) = QSTAT_PENDING;
+
+ dma = DMA_GET_ADDR(fup->fu_stats, sizeof(Fore_cp_stats),
+ FORE_STATS_ALIGN, 0);
+ if (dma == NULL) {
+ fup->fu_stats->st_drv.drv_cm_nodma++;
+ (void) splx(s);
+ return (EIO);
+ }
+ fup->fu_statsd = dma;
+ cqp->cmdq_stats.stats_buffer = (CP_dma) CP_WRITE(dma);
+
+ fup->fu_flags |= FUF_STATCMD;
+ cqp->cmdq_stats.stats_cmd =
+ CP_WRITE(CMD_GET_STATS | CMD_INTR_REQ);
+
+ /*
+ * Now wait for command to finish
+ */
+#if (defined(BSD) && (BSD >= 199103))
+ sst = tsleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH, "fore", 0);
+#else
+ sst = sleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH);
+ if (sst != 0)
+ sst = EINTR;
+#endif
+ (void) splx(s);
+ return (sst ? sst : fup->fu_stats_ret);
+
+ } else {
+ /*
+ * Command queue full
+ */
+ fup->fu_stats->st_drv.drv_cm_full++;
+ (void) splx(s);
+ return (EIO);
+ }
+}
+
diff --git a/sys/dev/hfa/fore_stats.h b/sys/dev/hfa/fore_stats.h
new file mode 100644
index 0000000..3803ddd
--- /dev/null
+++ b/sys/dev/hfa/fore_stats.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * ===================================
+ * 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: fore_stats.h,v 1.3 1997/05/06 22:10:21 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Driver statistics definitions
+ *
+ */
+
+#ifndef _FORE_STATS_H
+#define _FORE_STATS_H
+
+
+/*
+ * Fore Driver Statistics
+ */
+struct Stats_driver {
+ u_long drv_xm_notact; /* PDU drops out - VCC not active */
+ u_long drv_xm_full; /* Xmit queue full */
+ u_long drv_xm_maxpdu; /* PDU drops out - max segment/size */
+ u_long drv_xm_segnoal; /* Non-aligned segments */
+ u_long drv_xm_seglen; /* Padded length segments */
+ u_long drv_xm_segdma; /* PDU drops out - no dma address */
+ u_long drv_rv_novcc; /* PDU drops in - no VCC */
+ u_long drv_rv_nosbf; /* No small buffers */
+ u_long drv_rv_nomb; /* PDU drops in - no buffer */
+ u_long drv_rv_ifull; /* PDU drops in - intr queue full */
+ u_long drv_bf_segdma; /* Buffer supply - no dma address */
+ u_long drv_cm_full; /* Command queue full */
+ u_long drv_cm_nodma; /* Command failed - no dma address */
+};
+typedef struct Stats_driver Stats_driver;
+
+
+/*
+ * Fore Device Statistics
+ *
+ * This structure is used by pass all statistics (including CP maintained
+ * and driver maintained) data to user space (atm command).
+ */
+struct fore_stats {
+ Fore_cp_stats st_cpstat; /* CP stats */
+ Stats_driver st_drv; /* Driver maintained stats */
+};
+typedef struct fore_stats Fore_stats;
+
+#define st_taxi st_cpstat.st_cp_taxi
+#define st_oc3 st_cpstat.st_cp_oc3
+#define st_atm st_cpstat.st_cp_atm
+#define st_aal0 st_cpstat.st_cp_aal0
+#define st_aal4 st_cpstat.st_cp_aal4
+#define st_aal5 st_cpstat.st_cp_aal5
+#define st_misc st_cpstat.st_cp_misc
+
+#endif /* _FORE_STATS_H */
diff --git a/sys/dev/hfa/fore_timer.c b/sys/dev/hfa/fore_timer.c
new file mode 100644
index 0000000..e0d0c0e
--- /dev/null
+++ b/sys/dev/hfa/fore_timer.c
@@ -0,0 +1,97 @@
+/*
+ *
+ * ===================================
+ * 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: fore_timer.c,v 1.5 1997/05/06 22:10:24 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Timer processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_timer.c,v 1.5 1997/05/06 22:10:24 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Process a Fore timer tick
+ *
+ * This function is called every FORE_TIME_TICK seconds in order to update
+ * all of the unit watchdog timers.
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * tip pointer to fore timer control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+fore_timeout(tip)
+ struct atm_time *tip;
+{
+ Fore_unit *fup;
+ int i;
+
+
+ /*
+ * Schedule next timeout
+ */
+ atm_timeout(&fore_timer, ATM_HZ * FORE_TIME_TICK, fore_timeout);
+
+ /*
+ * Run through all units, updating each active timer.
+ * If an expired timer is found, notify that unit.
+ */
+ for (i = 0; i < fore_nunits; i++) {
+
+ if ((fup = fore_units[i]) == NULL)
+ continue;
+
+ /*
+ * Decrement timer, if it's active
+ */
+ if (fup->fu_timer && (--fup->fu_timer == 0)) {
+
+ /*
+ * Timeout occurred - go check out the queues
+ */
+ ATM_DEBUG0("fore_timeout\n");
+ DEVICE_LOCK((Cmn_unit *)fup);
+ fore_watchdog(fup);
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+ }
+ }
+}
+
diff --git a/sys/dev/hfa/fore_transmit.c b/sys/dev/hfa/fore_transmit.c
new file mode 100644
index 0000000..744e775
--- /dev/null
+++ b/sys/dev/hfa/fore_transmit.c
@@ -0,0 +1,371 @@
+/*
+ *
+ * ===================================
+ * 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: fore_transmit.c,v 1.8 1998/07/17 20:19:37 root Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Transmit queue management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_transmit.c,v 1.8 1998/07/17 20:19:37 root Exp $";
+#endif
+
+#include <dev/hfa/fore_include.h>
+
+
+/*
+ * Allocate Transmit Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * 0 allocations successful
+ * else allocation failed
+ */
+int
+fore_xmit_allocate(fup)
+ Fore_unit *fup;
+{
+ void *memp;
+ H_xmit_queue *hxp;
+ int i;
+
+ /*
+ * Allocate non-cacheable memory for transmit status words
+ */
+ memp = atm_dev_alloc(sizeof(Q_status) * XMIT_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_xmit_stat = (Q_status *) memp;
+
+ memp = DMA_GET_ADDR(fup->fu_xmit_stat, sizeof(Q_status) * XMIT_QUELEN,
+ QSTAT_ALIGN, ATM_DEV_NONCACHE);
+ if (memp == NULL) {
+ return (1);
+ }
+ fup->fu_xmit_statd = (Q_status *) memp;
+
+ /*
+ * Allocate memory for transmit descriptors
+ *
+ * We will allocate the transmit descriptors individually rather than
+ * as a single memory block, which will often be larger than a memory
+ * page. On some systems (eg. FreeBSD) the physical addresses of
+ * adjacent virtual memory pages are not contiguous.
+ */
+ hxp = fup->fu_xmit_q;
+ for (i = 0; i < XMIT_QUELEN; i++, hxp++) {
+
+ /*
+ * Allocate a transmit descriptor for this queue entry
+ */
+ hxp->hxq_descr = atm_dev_alloc(sizeof(Xmit_descr),
+ XMIT_DESCR_ALIGN, 0);
+ if (hxp->hxq_descr == NULL) {
+ return (1);
+ }
+
+ hxp->hxq_descr_dma = DMA_GET_ADDR(hxp->hxq_descr,
+ sizeof(Xmit_descr), XMIT_DESCR_ALIGN, 0);
+ if (hxp->hxq_descr_dma == NULL) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Transmit Queue Initialization
+ *
+ * Allocate and initialize the host-resident transmit queue structures
+ * and then initialize the CP-resident queue structures.
+ *
+ * Called at interrupt level.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_xmit_initialize(fup)
+ Fore_unit *fup;
+{
+ Aali *aap = fup->fu_aali;
+ Xmit_queue *cqp;
+ H_xmit_queue *hxp;
+ Q_status *qsp;
+ Q_status *qsp_dma;
+ int i;
+
+ /*
+ * Point to CP-resident transmit queue
+ */
+ cqp = (Xmit_queue *)(fup->fu_ram + CP_READ(aap->aali_xmit_q));
+
+ /*
+ * Point to host-resident transmit queue structures
+ */
+ hxp = fup->fu_xmit_q;
+ qsp = fup->fu_xmit_stat;
+ qsp_dma = fup->fu_xmit_statd;
+
+ /*
+ * Loop thru all queue entries and do whatever needs doing
+ */
+ for (i = 0; i < XMIT_QUELEN; i++) {
+
+ /*
+ * Set queue status word to free
+ */
+ *qsp = QSTAT_FREE;
+
+ /*
+ * Set up host queue entry and link into ring
+ */
+ hxp->hxq_cpelem = cqp;
+ hxp->hxq_status = qsp;
+ if (i == (XMIT_QUELEN - 1))
+ hxp->hxq_next = fup->fu_xmit_q;
+ else
+ hxp->hxq_next = hxp + 1;
+
+ /*
+ * Now let the CP into the game
+ */
+ cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
+
+ /*
+ * Bump all queue pointers
+ */
+ hxp++;
+ qsp++;
+ qsp_dma++;
+ cqp++;
+ }
+
+ /*
+ * Initialize queue pointers
+ */
+ fup->fu_xmit_head = fup->fu_xmit_tail = fup->fu_xmit_q;
+
+ return;
+}
+
+
+/*
+ * Drain Transmit Queue
+ *
+ * This function will free all completed entries at the head of the
+ * transmit queue. Freeing the entry includes releasing the transmit
+ * buffers (buffer chain) back to the kernel.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_xmit_drain(fup)
+ Fore_unit *fup;
+{
+ H_xmit_queue *hxp;
+ H_dma *sdmap;
+ Fore_vcc *fvp;
+ struct vccb *vcp;
+ KBuffer *m;
+
+ /*
+ * Process each completed entry
+ */
+ while (*fup->fu_xmit_head->hxq_status & QSTAT_COMPLETED) {
+
+ hxp = fup->fu_xmit_head;
+
+ /*
+ * Release the entry's DMA addresses and buffer chain
+ */
+ for (m = hxp->hxq_buf, sdmap = hxp->hxq_dma; m;
+ m = KB_NEXT(m), sdmap++) {
+ caddr_t cp;
+
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0);
+ }
+ KB_FREEALL(hxp->hxq_buf);
+
+ /*
+ * Get VCC over which data was sent (may be null if
+ * VCC has been closed in the meantime)
+ */
+ fvp = hxp->hxq_vcc;
+
+ /*
+ * Now collect some statistics
+ */
+ if (*hxp->hxq_status & QSTAT_ERROR) {
+ /*
+ * CP ran into problems, not much we can do
+ * other than record the event
+ */
+ fup->fu_pif.pif_oerrors++;
+ if (fvp) {
+ vcp = fvp->fv_connvc->cvc_vcc;
+ vcp->vc_oerrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_oerrors++;
+ }
+ } else {
+ /*
+ * Good transmission
+ */
+ int len = XDS_GET_LEN(hxp->hxq_descr->xd_spec);
+
+ fup->fu_pif.pif_opdus++;
+ fup->fu_pif.pif_obytes += len;
+ if (fvp) {
+ vcp = fvp->fv_connvc->cvc_vcc;
+ vcp->vc_opdus++;
+ vcp->vc_obytes += len;
+ if (vcp->vc_nif) {
+ vcp->vc_nif->nif_obytes += len;
+ vcp->vc_nif->nif_if.if_opackets++;
+#if (defined(BSD) && (BSD >= 199103))
+ vcp->vc_nif->nif_if.if_obytes += len;
+#endif
+ }
+ }
+ }
+
+ /*
+ * Mark this entry free for use and bump head pointer
+ * to the next entry in the queue
+ */
+ *hxp->hxq_status = QSTAT_FREE;
+ fup->fu_xmit_head = hxp->hxq_next;
+ }
+
+ return;
+}
+
+
+/*
+ * Free Transmit Queue Data Structures
+ *
+ * Arguments:
+ * fup pointer to device unit structure
+ *
+ * Returns:
+ * none
+ */
+void
+fore_xmit_free(fup)
+ Fore_unit *fup;
+{
+ H_xmit_queue *hxp;
+ H_dma *sdmap;
+ KBuffer *m;
+ int i;
+
+ /*
+ * Free any transmit buffers left on the queue
+ */
+ if (fup->fu_flags & CUF_INITED) {
+ while (*fup->fu_xmit_head->hxq_status != QSTAT_FREE) {
+
+ hxp = fup->fu_xmit_head;
+
+ /*
+ * Release the entry's DMA addresses and buffer chain
+ */
+ for (m = hxp->hxq_buf, sdmap = hxp->hxq_dma; m;
+ m = KB_NEXT(m), sdmap++) {
+ caddr_t cp;
+
+ KB_DATASTART(m, cp, caddr_t);
+ DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0);
+ }
+ KB_FREEALL(hxp->hxq_buf);
+
+ *hxp->hxq_status = QSTAT_FREE;
+ fup->fu_xmit_head = hxp->hxq_next;
+ }
+ }
+
+ /*
+ * Free the status words
+ */
+ if (fup->fu_xmit_stat) {
+ if (fup->fu_xmit_statd) {
+ DMA_FREE_ADDR(fup->fu_xmit_stat, fup->fu_xmit_statd,
+ sizeof(Q_status) * XMIT_QUELEN,
+ ATM_DEV_NONCACHE);
+ }
+ atm_dev_free((void *)fup->fu_xmit_stat);
+ fup->fu_xmit_stat = NULL;
+ fup->fu_xmit_statd = NULL;
+ }
+
+ /*
+ * Free the transmit descriptors
+ */
+ hxp = fup->fu_xmit_q;
+ for (i = 0; i < XMIT_QUELEN; i++, hxp++) {
+
+ /*
+ * Free the transmit descriptor for this queue entry
+ */
+ if (hxp->hxq_descr_dma) {
+ DMA_FREE_ADDR(hxp->hxq_descr, hxp->hxq_descr_dma,
+ sizeof(Xmit_descr), 0);
+ hxp->hxq_descr_dma = NULL;
+ }
+
+ if (hxp->hxq_descr) {
+ atm_dev_free(hxp->hxq_descr);
+ hxp->hxq_descr = NULL;
+ }
+ }
+
+ return;
+}
+
diff --git a/sys/dev/hfa/fore_var.h b/sys/dev/hfa/fore_var.h
new file mode 100644
index 0000000..25d2131
--- /dev/null
+++ b/sys/dev/hfa/fore_var.h
@@ -0,0 +1,269 @@
+/*
+ *
+ * ===================================
+ * 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: fore_var.h,v 1.8 1998/02/19 20:10:53 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Host protocol control blocks
+ *
+ */
+
+#ifndef _FORE_VAR_H
+#define _FORE_VAR_H
+
+/*
+ * Device VCC Entry
+ *
+ * Contains the common and Fore-specific information for each VCC
+ * which is opened through a Fore device.
+ */
+struct fore_vcc {
+ struct cmn_vcc fv_cmn; /* Common VCC stuff */
+ Fore_aal fv_aal; /* CP version of AAL */
+};
+typedef struct fore_vcc Fore_vcc;
+
+#define fv_next fv_cmn.cv_next
+#define fv_toku fv_cmn.cv_toku
+#define fv_upper fv_cmn.cv_upper
+#define fv_connvc fv_cmn.cv_connvc
+#define fv_state fv_cmn.cv_state
+#define fv_flags fv_cmn.cv_flags
+
+/*
+ * VCC Flags
+ */
+#define FVF_ACTCMD 0x01 /* Activate command issued */
+
+
+/*
+ * Host Transmit Queue Element
+ *
+ * Defines the host's view of the CP PDU Transmit Queue
+ */
+struct h_xmit_queue {
+ struct h_xmit_queue *hxq_next; /* Next element in queue */
+ Xmit_queue *hxq_cpelem; /* CP queue element */
+ Q_status *hxq_status; /* Element status word */
+ Xmit_descr *hxq_descr; /* Element's transmit descriptor */
+ Xmit_descr *hxq_descr_dma; /* Element's transmit descriptor */
+ Fore_vcc *hxq_vcc; /* Data's VCC */
+ KBuffer *hxq_buf; /* Data's buffer chain head */
+ H_dma hxq_dma[XMIT_MAX_SEGS]; /* DMA addresses for segments */
+};
+typedef struct h_xmit_queue H_xmit_queue;
+
+
+
+/*
+ * Host Receive Queue Element
+ *
+ * Defines the host's view of the CP PDU Receive Queue
+ */
+struct h_recv_queue {
+ struct h_recv_queue *hrq_next; /* Next element in queue */
+ Recv_queue *hrq_cpelem; /* CP queue element */
+ Q_status *hrq_status; /* Element status word */
+ Recv_descr *hrq_descr; /* Element's receive descriptor */
+ Recv_descr *hrq_descr_dma; /* Element's receive descriptor */
+};
+typedef struct h_recv_queue H_recv_queue;
+
+
+
+/*
+ * Host Buffer Supply Queue Element
+ *
+ * Defines the host's view of the CP Buffer Supply Queue
+ */
+struct h_buf_queue {
+ struct h_buf_queue *hbq_next; /* Next element in queue */
+ Buf_queue *hbq_cpelem; /* CP queue element */
+ Q_status *hbq_status; /* Element status word */
+ Buf_descr *hbq_descr; /* Element's buffer descriptor array */
+ Buf_descr *hbq_descr_dma; /* Element's buffer descriptor array */
+};
+typedef struct h_buf_queue H_buf_queue;
+
+
+
+/*
+ * Host Command Queue Element
+ *
+ * Defines the host's view of the CP Command Queue
+ */
+struct h_cmd_queue {
+ struct h_cmd_queue *hcq_next; /* Next element in queue */
+ Cmd_queue *hcq_cpelem; /* CP queue element */
+ Q_status *hcq_status; /* Element status word */
+ Cmd_code hcq_code; /* Command code */
+ void *hcq_arg; /* Command-specific argument */
+};
+typedef struct h_cmd_queue H_cmd_queue;
+
+
+
+/*
+ * Host Buffer Handle
+ *
+ * For each buffer supplied to the CP, there will be one of these structures
+ * embedded into the non-data portion of the buffer. This will allow us to
+ * track which buffers are currently "controlled" by the CP. The address of
+ * this structure will supplied to/returned from the CP as the buffer handle.
+ */
+struct buf_handle {
+ Qelem_t bh_qelem; /* Queuing element */
+ u_int bh_type; /* Buffer type (see below) */
+ H_dma bh_dma; /* Buffer DMA address */
+};
+typedef struct buf_handle Buf_handle;
+#define SIZEOF_Buf_handle 16
+
+/*
+ * Buffer Types
+ */
+#define BHT_S1_SMALL 1 /* Buffer strategy 1, small */
+#define BHT_S1_LARGE 2 /* Buffer strategy 1, large */
+#define BHT_S2_SMALL 3 /* Buffer strategy 2, small */
+#define BHT_S2_LARGE 4 /* Buffer strategy 2, large */
+
+
+
+/*
+ * Device Unit Structure
+ *
+ * Contains all the information for a single device (adapter).
+ */
+struct fore_unit {
+ Cmn_unit fu_cmn; /* Common unit stuff */
+#ifdef sun
+ struct dev_info *fu_devinfo; /* Device node for this unit */
+#endif
+ Fore_reg *fu_ctlreg; /* Device control register */
+#ifdef FORE_SBUS
+ Fore_reg *fu_intlvl; /* Interrupt level register */
+#endif
+#ifdef FORE_PCI
+ Fore_reg *fu_imask; /* Interrupt mask register */
+ Fore_reg *fu_psr; /* PCI specific register */
+ pcici_t fu_pcitag; /* PCI tag */
+#endif
+ Fore_mem *fu_ram; /* Device RAM */
+ u_int fu_ramsize; /* Size of device RAM */
+ Mon960 *fu_mon; /* Monitor program interface */
+ Aali *fu_aali; /* Microcode program interface */
+ u_int fu_timer; /* Watchdog timer value */
+
+ /* Transmit Queue */
+ H_xmit_queue fu_xmit_q[XMIT_QUELEN]; /* Host queue */
+ H_xmit_queue *fu_xmit_head; /* Queue head */
+ H_xmit_queue *fu_xmit_tail; /* Queue tail */
+ Q_status *fu_xmit_stat; /* Status array (host) */
+ Q_status *fu_xmit_statd; /* Status array (DMA) */
+
+ /* Receive Queue */
+ H_recv_queue fu_recv_q[RECV_QUELEN]; /* Host queue */
+ H_recv_queue *fu_recv_head; /* Queue head */
+ Q_status *fu_recv_stat; /* Status array (host) */
+ Q_status *fu_recv_statd; /* Status array (DMA) */
+ Recv_descr *fu_recv_desc; /* Descriptor array (host) */
+ Recv_descr *fu_recv_descd; /* Descriptor array (DMA) */
+
+ /* Buffer Supply Queue - Strategy 1 Small */
+ H_buf_queue fu_buf1s_q[BUF1_SM_QUELEN]; /* Host queue */
+ H_buf_queue *fu_buf1s_head; /* Queue head */
+ H_buf_queue *fu_buf1s_tail; /* Queue tail */
+ Q_status *fu_buf1s_stat; /* Status array (host) */
+ Q_status *fu_buf1s_statd;/* Status array (DMA) */
+ Buf_descr *fu_buf1s_desc; /* Descriptor array (host) */
+ Buf_descr *fu_buf1s_descd;/* Descriptor array (DMA) */
+ Queue_t fu_buf1s_bq; /* Queue of supplied buffers */
+ u_int fu_buf1s_cnt; /* Count of supplied buffers */
+
+ /* Buffer Supply Queue - Strategy 1 Large */
+ H_buf_queue fu_buf1l_q[BUF1_LG_QUELEN]; /* Host queue */
+ H_buf_queue *fu_buf1l_head; /* Queue head */
+ H_buf_queue *fu_buf1l_tail; /* Queue tail */
+ Q_status *fu_buf1l_stat; /* Status array (host) */
+ Q_status *fu_buf1l_statd;/* Status array (DMA) */
+ Buf_descr *fu_buf1l_desc; /* Descriptor array (host) */
+ Buf_descr *fu_buf1l_descd;/* Descriptor array (DMA) */
+ Queue_t fu_buf1l_bq; /* Queue of supplied buffers */
+ u_int fu_buf1l_cnt; /* Count of supplied buffers */
+
+ /* Command Queue */
+ H_cmd_queue fu_cmd_q[CMD_QUELEN]; /* Host queue */
+ H_cmd_queue *fu_cmd_head; /* Queue head */
+ H_cmd_queue *fu_cmd_tail; /* Queue tail */
+ Q_status *fu_cmd_stat; /* Status array (host) */
+ Q_status *fu_cmd_statd; /* Status array (DMA) */
+
+ Fore_stats *fu_stats; /* Device statistics buffer */
+ Fore_stats *fu_statsd; /* Device statistics buffer (DMA) */
+ time_t fu_stats_time; /* Last stats request timestamp */
+ int fu_stats_ret; /* Stats request return code */
+#ifdef FORE_PCI
+ Fore_prom *fu_prom; /* Device PROM buffer */
+ Fore_prom *fu_promd; /* Device PROM buffer (DMA) */
+#endif
+ struct callout_handle fu_thandle; /* Timer handle */
+};
+typedef struct fore_unit Fore_unit;
+
+#define fu_pif fu_cmn.cu_pif
+#define fu_unit fu_cmn.cu_unit
+#define fu_flags fu_cmn.cu_flags
+#define fu_mtu fu_cmn.cu_mtu
+#define fu_open_vcc fu_cmn.cu_open_vcc
+#define fu_vcc fu_cmn.cu_vcc
+#define fu_intrpri fu_cmn.cu_intrpri
+#define fu_savepri fu_cmn.cu_savepri
+#define fu_vcc_pool fu_cmn.cu_vcc_pool
+#define fu_nif_pool fu_cmn.cu_nif_pool
+#define fu_ioctl fu_cmn.cu_ioctl
+#define fu_instvcc fu_cmn.cu_instvcc
+#define fu_openvcc fu_cmn.cu_openvcc
+#define fu_closevcc fu_cmn.cu_closevcc
+#define fu_output fu_cmn.cu_output
+#define fu_config fu_cmn.cu_config
+
+/*
+ * Device flags (in addition to CUF_* flags)
+ */
+#define FUF_STATCMD 0x80 /* Statistics request in progress */
+
+
+/*
+ * Macros to access CP memory
+ */
+#define CP_READ(x) ntohl((u_long)(x))
+#define CP_WRITE(x) htonl((u_long)(x))
+
+#endif /* _FORE_VAR_H */
diff --git a/sys/dev/hfa/fore_vcm.c b/sys/dev/hfa/fore_vcm.c
new file mode 100644
index 0000000..3efea6a
--- /dev/null
+++ b/sys/dev/hfa/fore_vcm.c
@@ -0,0 +1,321 @@
+/*
+ *
+ * ===================================
+ * 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: fore_vcm.c,v 1.7 1998/06/29 21:42:20 mks Exp $
+ *
+ */
+
+/*
+ * FORE Systems 200-Series Adapter Support
+ * ---------------------------------------
+ *
+ * Virtual Channel Management
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: fore_vcm.c,v 1.7 1998/06/29 21:42:20 mks Exp $";
+#endif
+
+#include <dev/hfa/fore_include.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 Fore-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
+fore_instvcc(cup, cvp)
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+{
+ Fore_vcc *fvp = (Fore_vcc *)cvp;
+ Atm_attributes *ap = &fvp->fv_connvc->cvc_attr;
+
+ /*
+ * Validate requested AAL
+ */
+ switch (ap->aal.type) {
+
+ case ATM_AAL0:
+ fvp->fv_aal = FORE_AAL_0;
+ break;
+
+ case ATM_AAL3_4:
+ fvp->fv_aal = FORE_AAL_4;
+ if ((ap->aal.v.aal4.forward_max_SDU_size > FORE_IFF_MTU) ||
+ (ap->aal.v.aal4.backward_max_SDU_size > FORE_IFF_MTU))
+ return (EINVAL);
+ break;
+
+ case ATM_AAL5:
+ fvp->fv_aal = FORE_AAL_5;
+ if ((ap->aal.v.aal5.forward_max_SDU_size > FORE_IFF_MTU) ||
+ (ap->aal.v.aal5.backward_max_SDU_size > FORE_IFF_MTU))
+ 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 Fore-specific details.
+ * Then we just issue the command to the CP. Note that we can't wait around
+ * for the CP to process the command, so we return success for now and abort
+ * the connection if the command later fails.
+ *
+ * Called at splimp.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 open successful
+ * else open failed
+ *
+ */
+int
+fore_openvcc(cup, cvp)
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+{
+ Fore_unit *fup = (Fore_unit *)cup;
+ Fore_vcc *fvp = (Fore_vcc *)cvp;
+ H_cmd_queue *hcp;
+ Cmd_queue *cqp;
+ struct vccb *vcp;
+
+ vcp = fvp->fv_connvc->cvc_vcc;
+
+ ATM_DEBUG4("fore_openvcc: fup=0x%x, fvp=0x%x, vcc=(%d,%d)\n",
+ (int)fup, (int)fvp, vcp->vc_vpi, vcp->vc_vci);
+
+ /*
+ * Validate the VPI and VCI values
+ */
+ if ((vcp->vc_vpi > fup->fu_pif.pif_maxvpi) ||
+ (vcp->vc_vci > fup->fu_pif.pif_maxvci)) {
+ return (1);
+ }
+
+ /*
+ * Only need to tell the CP about incoming VCCs
+ */
+ if ((vcp->vc_type & VCC_IN) == 0) {
+ DEVICE_LOCK((Cmn_unit *)fup);
+ fup->fu_open_vcc++;
+ fvp->fv_state = CVS_ACTIVE;
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+ return (0);
+ }
+
+ /*
+ * Queue command at end of command queue
+ */
+ hcp = fup->fu_cmd_tail;
+ if ((*hcp->hcq_status) & QSTAT_FREE) {
+
+ /*
+ * Queue entry available, so set our view of things up
+ */
+ hcp->hcq_code = CMD_ACT_VCCIN;
+ hcp->hcq_arg = fvp;
+ fup->fu_cmd_tail = hcp->hcq_next;
+ fvp->fv_flags |= FVF_ACTCMD;
+
+ /*
+ * Now set the CP-resident queue entry - the CP will grab
+ * the command when the op-code is set.
+ */
+ cqp = hcp->hcq_cpelem;
+ (*hcp->hcq_status) = QSTAT_PENDING;
+ cqp->cmdq_act.act_vccid = CP_WRITE(vcp->vc_vci);
+ if (fvp->fv_aal == FORE_AAL_0)
+ cqp->cmdq_act.act_batch = CP_WRITE(1);
+ cqp->cmdq_act.act_spec = CP_WRITE(
+ ACT_SET_SPEC(BUF_STRAT_1, fvp->fv_aal,
+ CMD_ACT_VCCIN | CMD_INTR_REQ));
+ } else {
+ /*
+ * Command queue full
+ */
+ fup->fu_stats->st_drv.drv_cm_full++;
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * 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 Fore-specific details.
+ * Then we just issue the command to the CP. Note that we can't wait around
+ * for the CP to process the command, so we return success for now and whine
+ * if the command later fails.
+ *
+ * Called at splimp.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 close successful
+ * else close failed
+ *
+ */
+int
+fore_closevcc(cup, cvp)
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+{
+ Fore_unit *fup = (Fore_unit *)cup;
+ Fore_vcc *fvp = (Fore_vcc *)cvp;
+ H_xmit_queue *hxp;
+ H_cmd_queue *hcp;
+ Cmd_queue *cqp;
+ struct vccb *vcp;
+ int i, err = 0;
+
+ vcp = fvp->fv_connvc->cvc_vcc;
+
+ ATM_DEBUG4("fore_closevcc: fup=0x%x, fvp=0x%x, vcc=(%d,%d)\n",
+ (int)fup, (int)fvp, vcp->vc_vpi, vcp->vc_vci);
+
+ DEVICE_LOCK((Cmn_unit *)fup);
+
+ /*
+ * Clear any references to this VCC in our transmit queue
+ */
+ for (hxp = fup->fu_xmit_head, i = 0;
+ (*hxp->hxq_status != QSTAT_FREE) && (i < XMIT_QUELEN);
+ hxp = hxp->hxq_next, i++) {
+ if (hxp->hxq_vcc == fvp) {
+ hxp->hxq_vcc = NULL;
+ }
+ }
+
+ /*
+ * Clear any references to this VCC in our command queue
+ */
+ for (hcp = fup->fu_cmd_head, i = 0;
+ (*hcp->hcq_status != QSTAT_FREE) && (i < CMD_QUELEN);
+ hcp = hcp->hcq_next, i++) {
+ switch (hcp->hcq_code) {
+
+ case CMD_ACT_VCCIN:
+ case CMD_ACT_VCCOUT:
+ if (hcp->hcq_arg == fvp) {
+ hcp->hcq_arg = NULL;
+ }
+ break;
+ }
+ }
+
+ /*
+ * If this VCC has been previously activated, then we need to tell
+ * the CP to deactivate it.
+ */
+ if (fvp->fv_flags & FVF_ACTCMD) {
+
+ /*
+ * Queue command at end of command queue
+ */
+ hcp = fup->fu_cmd_tail;
+ if ((*hcp->hcq_status) & QSTAT_FREE) {
+
+ /*
+ * Queue entry available, so set our view of things up
+ */
+ hcp->hcq_code = CMD_DACT_VCCIN;
+ hcp->hcq_arg = fvp;
+ fup->fu_cmd_tail = hcp->hcq_next;
+
+ /*
+ * Now set the CP-resident queue entry - the CP will
+ * grab the command when the op-code is set.
+ */
+ cqp = hcp->hcq_cpelem;
+ (*hcp->hcq_status) = QSTAT_PENDING;
+ cqp->cmdq_dact.dact_vccid = CP_WRITE(vcp->vc_vci);
+ cqp->cmdq_dact.dact_cmd =
+ CP_WRITE(CMD_DACT_VCCIN|CMD_INTR_REQ);
+ } else {
+ /*
+ * Command queue full
+ *
+ * If we get here, we'll be getting out-of-sync with
+ * the CP because we can't (for now at least) do
+ * anything about close errors in the common code.
+ * This won't be too bad, since we'll just toss any
+ * PDUs received from the VCC and the sigmgr's will
+ * always get open failures when trying to use this
+ * (vpi,vci)...oh, well...always gotta have that one
+ * last bug to fix! XXX
+ */
+ fup->fu_stats->st_drv.drv_cm_full++;
+ err = 1;
+ }
+ }
+
+ /*
+ * Finish up...
+ */
+ if (fvp->fv_state == CVS_ACTIVE)
+ fup->fu_open_vcc--;
+
+ DEVICE_UNLOCK((Cmn_unit *)fup);
+
+ return (err);
+}
+
OpenPOWER on IntegriCloud