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