diff options
author | phk <phk@FreeBSD.org> | 1998-09-15 08:23:17 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 1998-09-15 08:23:17 +0000 |
commit | c3dd1fa899d435ea4bf79897f646a93cb80c94ac (patch) | |
tree | 98dfbc96e3c6aa7ff1f322855f6484c4e609819d /sys/dev | |
parent | 9ed6892f4808d56de443849229e151f8f7ad43b0 (diff) | |
download | FreeBSD-src-c3dd1fa899d435ea4bf79897f646a93cb80c94ac.zip FreeBSD-src-c3dd1fa899d435ea4bf79897f646a93cb80c94ac.tar.gz |
Add new files for HARP3
Host ATM Research Platform (HARP), Network Computing Services, Inc.
This software was developed with the support of the Defense Advanced
Research Projects Agency (DARPA).
Diffstat (limited to 'sys/dev')
32 files changed, 11767 insertions, 0 deletions
diff --git a/sys/dev/hea/eni.c b/sys/dev/hea/eni.c new file mode 100644 index 0000000..088503a --- /dev/null +++ b/sys/dev/hea/eni.c @@ -0,0 +1,664 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni.c,v 1.4 1998/06/29 19:39:10 jpt Exp $ + * + */ + +/* + * Efficient ENI adapter support + * ----------------------------- + * + * Module supports PCI interface to ENI adapter + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni.c,v 1.4 1998/06/29 19:39:10 jpt Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +/* + * Typedef local functions + */ +static char *eni_pci_probe __P((pcici_t, pcidi_t)); +static void eni_pci_attach __P((pcici_t, int)); +static int eni_get_ack __P((Eni_unit *)); +static int eni_get_sebyte __P((Eni_unit *)); +static void eni_read_seeprom __P((Eni_unit *)); +#ifdef __FreeBSD__ +#if BSD < 199506 +static int eni_pci_shutdown __P((struct kern_devconf *, int)); +#else +static void eni_pci_shutdown __P((int, void *)); +#endif +static void eni_pci_reset __P((Eni_unit *)); +#endif /* __FreeBSD__ */ + +/* + * Used by kernel to return number of claimed devices + */ +#ifdef __FreeBSD__ +static u_long eni_nunits; + +static struct pci_device eni_pci_device = { + ENI_DEV_NAME, + eni_pci_probe, + eni_pci_attach, + &eni_nunits, +#if BSD < 199506 + eni_pci_shutdown +#else + NULL +#endif +}; + +DATA_SET ( pcidevice_set, eni_pci_device ); +#endif /* __FreeBSD__ */ + +/* + * Called by kernel with PCI device_id which was read from the PCI + * register set. If the identified vendor is Efficient, see if we + * recognize the particular device. If so, return an identifying string, + * if not, return null. + * + * Arguments: + * config_id PCI config token + * device_id contents of PCI device ID register + * + * Returns: + * string Identifying string if we will handle this device + * NULL unrecognized vendor/device + * + */ +static char * +eni_pci_probe ( pcici_t config_id, pcidi_t device_id ) +{ + + if ( (device_id & 0xFFFF) == EFF_VENDOR_ID ) { + switch ( (device_id >> 16) ) { + case EFF_DEV_ID: + return ( "Efficient ENI ATM Adapter" ); +/* NOTREACHED */ + break; + } + } + + return ( NULL ); +} + +/* + * The ENI-155p adapter uses an ATMEL AT24C01 serial EEPROM to store + * configuration information. The SEEPROM is accessed via two wires, + * CLOCK and DATA, which are accessible via the PCI configuration + * registers. The following macros manipulate the lines to access the + * SEEPROM. See http://www.atmel.com/atmel/products/prod162.htm for + * a description of the AT24C01 part. Value to be read/written is + * part of the per unit structure. + */ +/* + * Write bits to SEEPROM + */ +#define WRITE_SEEPROM() ( \ + { \ + (void) pci_conf_write ( eup->eu_pcitag, SEEPROM, \ + eup->eu_sevar ); \ + DELAY(SEPROM_DELAY); \ + } \ +) +/* + * Stobe first the DATA, then the CLK lines high + */ +#define STROBE_HIGH() ( \ + { \ + eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM(); \ + eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \ + } \ +) +/* + * Strobe first the CLK, then the DATA lines high + */ +#define INV_STROBE_HIGH() ( \ + { \ + eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \ + eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM(); \ + } \ +) +/* + * Strobe first the CLK, then the DATA lines low - companion to + * STROBE_HIGH() + */ +#define STROBE_LOW() ( \ + { \ + eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \ + eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM(); \ + } \ +) +/* + * Strobe first the DATA, then the CLK lines low - companion to + * INV_STROBE_HIGH() + */ +#define INV_STROBE_LOW() ( \ + { \ + eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM(); \ + eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \ + } \ +) +/* + * Strobe the CLK line high, then low + */ +#define STROBE_CLK() ( \ + { \ + eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \ + eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \ + } \ +) + +/* + * Look for a positive ACK from the SEEPROM. Cycle begins by asserting + * the DATA line, then the CLK line. The DATA line is then read to + * retrieve the ACK status, and then the cycle is finished by deasserting + * the CLK line, and asserting the DATA line. + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * 0/1 value of ACK + * + */ +static int +eni_get_ack ( eup ) + Eni_unit *eup; +{ + int ack; + + STROBE_HIGH(); + /* + * Read DATA line from SEPROM + */ + eup->eu_sevar = pci_conf_read ( eup->eu_pcitag, SEEPROM ); + DELAY ( SEPROM_DELAY ); + ack = eup->eu_sevar & SEPROM_DATA; + + eup->eu_sevar &= ~SEPROM_CLK; + WRITE_SEEPROM (); + eup->eu_sevar |= SEPROM_DATA; + WRITE_SEEPROM (); + + return ( ack ); +} + +/* + * Read a byte from the SEEPROM. Data is read as 8 bits. There are two types + * of read operations. The first is a single byte read, the second is + * multiple sequential bytes read. Both cycles begin with a 'START' operation, + * followed by a memory address word. Following the memory address, the + * SEEPROM will send a data byte, followed by an ACK. If the host responds + * with a 'STOP' operation, then a single byte cycle is performed. If the + * host responds with an 'ACK', then the memory address is incremented, and + * the next sequential memory byte is serialized. + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * val value of byte read from SEEPROM + * + */ +static int +eni_get_sebyte( eup ) + Eni_unit *eup; +{ + int i; + int data; + int rval; + + /* Initial value */ + rval = 0; + /* Read 8 bits */ + for ( i = 0; i < 8; i++ ) { + /* Shift bits to left so the next bit goes to position 0 */ + rval <<= 1; + /* Indicate we're ready to read bit */ + STROBE_HIGH(); + /* + * Read DATA line from SEPROM + */ + data = pci_conf_read ( eup->eu_pcitag, SEEPROM ); + DELAY ( SEPROM_DELAY ); + /* (Possibly) mask bit into accumulating value */ + if ( data & SEPROM_DATA ) + rval |= 1; /* If DATA bit '1' */ + /* Indicate we're done reading this bit */ + STROBE_LOW(); + } + + /* Return acquired byte */ + return ( rval ); +} + +/* + * The AT24C01 is a 1024 bit part organized as 128 words by 8 bits. + * We will read the entire contents into the per unit structure. Later, + * we'll retrieve the MAC address and serial number from the data read. + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * none + * + */ +static void +eni_read_seeprom ( eup ) + Eni_unit *eup; +{ + int addr; + int i, j; + + /* + * Set initial state + */ + eup->eu_sevar = SEPROM_DATA | SEPROM_CLK; + WRITE_SEEPROM (); + + /* Loop for all bytes */ + for ( i = 0; i < SEPROM_SIZE ; i++ ) { + /* Send START operation */ + STROBE_HIGH(); + INV_STROBE_LOW(); + + /* + * Send address. Addresses are sent as 7 bits plus + * last bit high. + */ + addr = ((i) << 1) + 1; + /* + * Start with high order bit first working toward low + * order bit. + */ + for ( j = 7; j >= 0; j-- ) { + /* Set current bit value */ + eup->eu_sevar = ( addr >> j ) & 1 ? + eup->eu_sevar | SEPROM_DATA : + eup->eu_sevar & ~SEPROM_DATA; + WRITE_SEEPROM (); + /* Indicate we've sent it */ + STROBE_CLK(); + } + /* + * We expect a zero ACK after sending the address + */ + if ( !eni_get_ack ( eup ) ) { + /* Address okay - read data byte */ + eup->eu_seeprom[i] = eni_get_sebyte ( eup ); + /* Grab but ignore the ACK op */ + (void) eni_get_ack ( eup ); + } else { + /* Address ACK was bad - can't retrieve data byte */ + eup->eu_seeprom[i] = 0xff; + } + } + + return; +} + +/* + * The kernel has found a device which we are willing to support. + * We are now being called to do any necessary work to make the + * device initially usable. In our case, this means allocating + * structure memory, configuring registers, mapping device + * memory, setting pointers, registering with the core services, + * and doing the initial PDU processing configuration. + * + * Arguments: + * config_id PCI device token + * unit instance of the unit + * + * Returns: + * none + * + */ +static void +eni_pci_attach ( pcici_t config_id, int unit ) +{ + vm_offset_t va; + vm_offset_t pa; + Eni_unit *eup; + long val; + + /* + * Just checking... + */ + if ( unit >= ENI_MAX_UNITS ) { + log ( LOG_ERR, "%s%d: too many devices\n", + ENI_DEV_NAME, unit ); + return; + } + + /* + * Make sure this isn't a duplicate unit + */ + if ( eni_units[unit] != NULL ) + return; + + /* + * Allocate a new unit structure + */ + eup = (Eni_unit *) atm_dev_alloc ( sizeof(Eni_unit), sizeof(int), 0 ); + if ( eup == NULL ) + return; + + /* + * Start initializing it + */ + eup->eu_unit = unit; + eup->eu_mtu = ENI_IFF_MTU; + eup->eu_pcitag = config_id; + eup->eu_ioctl = eni_atm_ioctl; + eup->eu_instvcc = eni_instvcc; + eup->eu_openvcc = eni_openvcc; + eup->eu_closevcc = eni_closevcc; + eup->eu_output = eni_output; + eup->eu_vcc_pool = &eni_vcc_pool; + eup->eu_nif_pool = &eni_nif_pool; + + /* + * Map in adapter RAM + */ + if ( ( pci_map_mem ( config_id, PCI_MAP_REG_START, &va, &pa ) ) == 0 ) + { + /* + * Nothing's happened yet that we need to undo. + */ + return; + } + /* + * Map okay - retain address assigned + */ + eup->eu_base = (Eni_mem)va; + eup->eu_ram = (Eni_mem)(eup->eu_base + RAM_OFFSET); + + /* + * Map memory structures into adapter space + */ + eup->eu_suni = (Eni_mem)(eup->eu_base + SUNI_OFFSET); + eup->eu_midway = (Eni_mem)(eup->eu_base + MIDWAY_OFFSET); + eup->eu_vcitbl = (VCI_Table *)(eup->eu_base + VCITBL_OFFSET); + eup->eu_rxdma = (Eni_mem)(eup->eu_base + RXQUEUE_OFFSET); + eup->eu_txdma = (Eni_mem)(eup->eu_base + TXQUEUE_OFFSET); + eup->eu_svclist = (Eni_mem)(eup->eu_base + SVCLIST_OFFSET); + eup->eu_servread = 0; + + /* + * Reset the midway chip + */ + eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET; + + /* + * Size and test adapter memory. Initialize our adapter memory + * allocater. + */ + if ( eni_init_memory ( eup ) < 0 ) { + /* + * Adapter memory test failed. Clean up and + * return. + */ + /* + * FreeBSD doesn't support unmapping PCI memory (yet?). + */ + return; + } + + /* + * Read the contents of the SEEPROM + */ + eni_read_seeprom ( eup ); + /* + * Copy MAC address to PIF and config structures + */ + KM_COPY ( (caddr_t)&eup->eu_seeprom[SEPROM_MAC_OFF], + (caddr_t)&eup->eu_pif.pif_macaddr, sizeof(struct mac_addr) ); + eup->eu_config.ac_macaddr = eup->eu_pif.pif_macaddr; + + /* + * Copy serial number into config space + */ + eup->eu_config.ac_serial = + ntohl(*(u_long *)&eup->eu_seeprom[SEPROM_SN_OFF]); + + /* + * Convert Endianess on DMA + */ + val = pci_conf_read ( config_id, PCI_CONTROL_REG ); + val |= ENDIAN_SWAP_DMA; + pci_conf_write ( config_id, PCI_CONTROL_REG, val ); + + /* + * Map interrupt in + */ + if ( !pci_map_int ( config_id, eni_intr, (void *)eup, &net_imask ) ) + { + /* + * Finish by unmapping memory, etc. + */ + log ( LOG_ERR, "eni_pci_attach: Unable to map interrupt\n" ); + /* + * Can't unmap PCI memory (yet). + */ + return; + } + + /* + * Setup some of the adapter configuration + */ + /* + * Get MIDWAY ID + */ + val = eup->eu_midway[MIDWAY_ID]; + eup->eu_config.ac_vendor = VENDOR_ENI; + eup->eu_config.ac_vendapi = VENDAPI_ENI_1; + eup->eu_config.ac_device = DEV_ENI_155P; + eup->eu_config.ac_media = val & MEDIA_MASK ? MEDIA_UTP155 : MEDIA_OC3C; + eup->eu_pif.pif_pcr = ATM_PCR_OC3C; + eup->eu_config.ac_bustype = BUS_PCI; + eup->eu_config.ac_busslot = config_id->bus << 8 | config_id->slot; + + /* + * Make a hw version number from the ID register values. + * Format: {Midway ID}.{Mother board ID}.{Daughter board ID} + */ + sprintf ( eup->eu_config.ac_hard_vers, "%d/%d/%d", + (val >> ID_SHIFT) & ID_MASK, + (val >> MID_SHIFT) & MID_MASK, + (val >> DID_SHIFT) & DID_MASK ); + + /* + * There is no software version number + */ + sprintf ( eup->eu_config.ac_firm_vers, "\0" ); + + /* + * Save device ram info for user-level programs + * NOTE: This really points to start of EEPROM + * and includes all the device registers in the + * lower 2 Megabytes. + */ + eup->eu_config.ac_ram = (long)eup->eu_base; + eup->eu_config.ac_ramsize = eup->eu_ramsize + ENI_REG_SIZE; + + /* + * Setup max VPI/VCI values + */ + eup->eu_pif.pif_maxvpi = ENI_MAX_VPI; + eup->eu_pif.pif_maxvci = ENI_MAX_VCI; + + /* + * Register this interface with ATM core services + */ + if ( atm_physif_register + ( (Cmn_unit *)eup, ENI_DEV_NAME, eni_services ) != 0 ) + { + /* + * Registration failed - back everything out + */ + /* + * Can't unmap PCI memory (yet). + */ + /* + * Unmap PCI interrupt + */ + (void) pci_unmap_int ( config_id ); + log ( LOG_ERR, + "eni_pci_attach: atm_physif_register failed\n" ); + return; + } + + eni_units[unit] = eup; + +#if BSD >= 199506 + /* + * Add hook to out shutdown function + */ + at_shutdown ( (bootlist_fn)eni_pci_shutdown, eup, SHUTDOWN_POST_SYNC ); +#endif + + /* + * Initialize driver processing + */ + if ( eni_init ( eup ) ) { + log ( LOG_ERR, "eni_pci_attach: Failed eni_init()\n" ); + /* + * Can't unmap PCI memory (yet). + */ + /* + * Unmap PCI interrupt + */ + (void) pci_unmap_int ( config_id ); + /* + * Fall through to return + */ + } + + return; + +} + +/* + * Device reset routine + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * none + * + */ +static void +eni_pci_reset ( eup ) + Eni_unit *eup; +{ + + /* + * We should really close down any open VCI's and + * release all memory (TX and RX) buffers. For now, + * we assume we're shutting the card down for good. + */ + + /* + * Issue RESET command to Midway chip + */ + eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET; + + /* + * Delay to allow everything to terminate + */ + DELAY ( MIDWAY_DELAY ); + + return; +} + +#ifdef __FreeBSD__ +#if BSD < 199506 +/* + * Device shutdown routine + * + * Arguments: + * kdc pointer to device's configuration table + * force forced shutdown flag + * + * Returns: + * none + * + */ +static int +eni_pci_shutdown ( kdc, force ) + struct kern_devconf *kdc; + int force; +{ + Eni_unit *eup = NULL; + + if ( kdc->kdc_unit < eni_nunits ) { + + eup = eni_units[kdc->kdc_unit]; + if ( eup != NULL ) { + /* Do device reset */ + eni_pci_reset ( eup ); + } + } + + (void) dev_detach ( kdc ); + return ( 0 ); +} +#else +/* + * Device shutdown routine + * + * Arguments: + * howto type of shutdown + * eup pointer to device unit structure + * + * Returns: + * none + * + */ +static void +eni_pci_shutdown ( howto, eup ) + int howto; + void *eup; +{ + + /* Do device reset */ + eni_pci_reset ( eup ); + +} +#endif /* BSD < 199506 */ +#endif /* __FreeBSD__ */ diff --git a/sys/dev/hea/eni.h b/sys/dev/hea/eni.h new file mode 100644 index 0000000..ff4a81b --- /dev/null +++ b/sys/dev/hea/eni.h @@ -0,0 +1,499 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni.h,v 1.7 1998/06/29 19:45:14 jpt Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * + * Protocol and implementation definitions + * + */ + +#ifndef _ENI_ENI_H +#define _ENI_ENI_H + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +/* + * Physical device name - used to configure HARP devices + */ +#ifndef ENI_DEV_NAME +#define ENI_DEV_NAME "hea" /* HARP Efficient ATM */ +#endif + +#define ENI_MAX_UNITS 4 + +#define ENI_IFF_MTU 9188 +#define ENI_MAX_VCI 1023 /* 0 - 1023 */ +#define ENI_MAX_VPI 0 + +#define ENI_IFQ_MAXLEN 1000 /* rx/tx queue lengths */ + +#ifdef BSD +/* + * Size of small and large receive buffers + */ +#define ENI_SMALL_BSIZE 64 +#define ENI_LARGE_BSIZE MCLBYTES +#endif /* BSD */ + +/* + * ENI memory map offsets IN WORDS, not bytes + * + * The Efficient Adapter implements a 4 MB address space. The lower + * 2 MB are used by bootprom (E)EPROM and by chipset registers such + * as the MIDWAY and SUNI chips. The (upto) upper 2 MB is used for + * RAM. Of the RAM, the lower 28 KB is used for fixed tables - the + * VCI table, the RX and TX DMA queues, and the Service List queue. + * Memory above the 28 KB range is available for RX and TX buffers. + * + * NOTE: Access to anything other then the (E)EPROM MUST be as a 32 bit + * access. Also note that Efficient uses both byte addresses and word + * addresses when describing offsets. BE CAREFUL or you'll get confused! + */ +/* + * Size of memory space reserved for registers and expansion (e)eprom. + */ +#define ENI_REG_SIZE 0x200000 /* Two megabytes */ + +#define SUNI_OFFSET 0x008000 /* SUNI chip registers */ +#define MIDWAY_OFFSET 0x010000 /* MIDWAY chip registers */ +#define RAM_OFFSET 0x080000 /* Adapter RAM */ +#define VCITBL_OFFSET 0x080000 /* VCI Table offset */ +#define RXQUEUE_OFFSET 0x081000 /* RX DMA Queue offset */ +#define TXQUEUE_OFFSET 0x081400 /* TX DMA Queue offset */ +#define SVCLIST_OFFSET 0x081800 /* SVC List Queue offset */ + +#define SEGBUF_BASE 0x007000 /* Base from start of RAM */ + +#define DMA_LIST_SIZE 512 /* 1024 words / 2 words per entry */ +#define SVC_LIST_SIZE 1024 /* 1024 words / 1 word per entry */ + +/* + * Values for testing size of RAM on adapter + * + * Efficient has (at least) two different memory sizes available. One + * is a client card which has either 128 KB or 512 KB RAM, the other + * is a server card which has 2 MB RAM. The driver will size and test + * the memory to correctly determine what's available. + */ +#define MAX_ENI_MEM 0x200000 /* 2 MB - max. mem supported */ +#define TEST_STEP 0x000400 /* Look at 1 KB steps */ +#define TEST_PAT 0xA5A5A5A5 /* Test pattern */ + +/* + * Values for memory allocator + */ +#define ENI_BUF_PGSZ 1024 /* Allocation unit of buffers */ +#define ENI_BUF_NBIT 8 /* Number of bits to get from */ + /* min buffer (1KB) to max (128KB) */ + +/* + * Values for allocating TX buffers + */ +#define MAX_CLIENT_RAM 512 /* Most RAM a client card will have */ +#define TX_SMALL_BSIZE 32 /* Small buffer - 32KB */ +#define TX_LARGE_BSIZE 128 /* Large buffer - 128KB */ + +/* + * Values for allocating RX buffers + */ +#define RX_SIG_BSIZE 4 /* Signalling buffer - 4KB */ +#define RX_CLIENT_BSIZE 16 /* Client buffer - 16KB */ +#define RX_SERVER_BSIZE 32 /* Server buffer - 32KB */ + +/* + * Adapter bases all addresses off of some power from 1KB. Thus, it + * only needs to store the most sigificant bits and can drop the lower + * 10 bits. + */ +#define ENI_LOC_PREDIV 10 /* Bits location is shifted */ + /* Location is prescaled by 1KB */ + /* before use in various places */ + +#define MIDWAY_DELAY 10 /* Time to wait for Midway finish */ + +/* + * Define the MIDWAY register offsets and any interesting bits within + * the register + */ +#define MIDWAY_ID 0x00 /* ID/Reset register */ + #define MIDWAY_RESET 0 /* iWrite of any value */ + #define ID_SHIFT 27 /* Midway ID version */ + #define ID_MASK 0x1F /* ID mask */ + #define MID_SHIFT 7 /* Mother board ID */ + #define MID_MASK 0x7 /* MID mask */ + #define DID_SHIFT 0 /* Daughter board ID */ + #define DID_MASK 0x1F /* DID mask */ + /* + * Efficient defines the following IDs for their adapters: + * 0x420/0x620 - SONET MMF, client memory size + * 0x430/0x630 - SONET MMF, server memory size + * 0x424/0x624 - UTP-5, client memory size + * 0x434/0x634 - UTP-5, server memory size + */ + #define MEDIA_MASK 0x04 /* Mask off UTP-5/MMF media */ + +#define MIDWAY_ISA 0x01 /* Interrupt Status Ack. */ + /* Reading this register */ + /* also acknowledges the */ + /* posted interrupt(s) */ + +#define MIDWAY_IS 0x02 /* Interrupt Status */ + /* Reading this register */ + /* does NOT acknowledge the */ + /* posted interrupt(s) */ + /* Interrupt names */ + #define ENI_INT_STAT 0x00000001 + #define ENI_INT_SUNI 0x00000002 + #define ENI_INT_SERVICE 0x00000004 + #define ENI_INT_TX_DMA 0x00000008 + #define ENI_INT_RX_DMA 0x00000010 + #define ENI_INT_DMA_ERR 0x00000020 + #define ENI_INT_DMA_LERR 0x00000040 + #define ENI_INT_IDEN 0x00000080 + #define ENI_INT_DMA_OVFL 0x00000100 + #define ENI_INT_TX_MASK 0x0001FE00 + +#define MIDWAY_IE 0x03 /* Interrupt Enable register */ + /* Interrupt enable bits are the same as the Interrupt names */ + +#define MIDWAY_MASTER 0x04 /* Master Control */ + /* Master control bits */ + #define ENI_M_WAIT500 0x00000001 /* Disable interrupts .5 ms */ + #define ENI_M_WAIT1 0x00000002 /* Disable interrupts 1 ms */ + #define ENI_M_RXENABLE 0x00000004 /* Enable RX engine */ + #define ENI_M_TXENABLE 0x00000008 /* Enable TX engine */ + #define ENI_M_DMAENABLE 0x00000010 /* Enable DMA */ + #define ENI_M_TXLOCK 0x00000020 /* 0: Streaming, 1: Lock */ + #define ENI_M_INTSEL 0x000001C0 /* Int Select mask */ + #define ENI_ISEL_SHIFT 6 /* Bits to shift ISEL value */ + +#define MIDWAY_STAT 0x05 /* Statistics register */ + +#define MIDWAY_SVCWR 0x06 /* Svc List write pointer */ + #define SVC_SIZE_MASK 0x3FF /* Valid bits in svc pointer */ + +#define MIDWAY_DMAADDR 0x07 /* Current virtual DMA addr */ + +#define MIDWAY_RX_WR 0x08 /* Write ptr to RX DMA queue */ + +#define MIDWAY_RX_RD 0x09 /* Read ptr to RX DMA queue */ + +#define MIDWAY_TX_WR 0x0A /* Write ptr to TX DMA queue */ + +#define MIDWAY_TX_RD 0x0B /* Read ptr to TX DMA queue */ + +/* + * Registers 0x0C - 0x0F are unused + */ + +/* + * MIDWAY supports 8 transmit channels. Each channel has 3 registers + * to control operation. Each new channel starts on N * 4 set. Thus, + * channel 0 uses register 0x10 - 0x13, channel 1 uses 0x14 - 0x17, etc. + * Register 0x13 + N * 4 is unused. + */ + +#define MIDWAY_TXPLACE 0x10 /* Channel N TX location */ + #define TXSIZE_SHIFT 11 /* Bits to shift size by */ + #define TX_PLACE_MASK 0x7FF /* Valid bits in TXPLACE */ + +#define MIDWAY_RDPTR 0x11 /* Channel N Read ptr */ + +#define MIDWAY_DESCR 0x12 /* Channel N Descr ptr */ + +/* + * Register 0x30 on up are unused + */ + +/* + * Part of PCI configuration registers but not defined in <pci/pcireg.h> + */ +#define PCI_CONTROL_REG 0x60 +#define ENDIAN_SWAP_DMA 0x80 /* Enable endian swaps on DMA */ + +/* + * The Efficient adapter references adapter RAM through the use of + * location and size values. Eight sizes are defined. When allocating + * buffers, there size must be rounded up to the next size which will + * hold the requested size. Buffers are allocated on 'SIZE' boundaries. + * See eni_buffer.c for more info. + */ + +/* + * Buffer SIZE definitions - in words, so from 1 KB to 128 KB + */ +#define SIZE_256 0x00 +#define SIZE_512 0x01 +#define SIZE_1K 0x02 +#define SIZE_2K 0x03 +#define SIZE_4K 0x04 +#define SIZE_8K 0x05 +#define SIZE_16K 0x06 +#define SIZE_32K 0x07 + +/* + * Define values for DMA type - DMA descriptors include a type field and a + * count field except in the special case of JK (just-kidding). With type JK, + * the count field should be set to the address which will be loaded + * into the pointer, ie. where the pointer should next point to, since + * JK doesn't have a "size" associated with it. JK DMA is used to skip + * over descriptor words, and to strip off padding of AAL5 PDUs. The + * DMA_nWORDM types will do a n word DMA burst, but the count field + * does not have to equal n. Any difference results in garbage filling + * the remaining words of the DMA. These types could be used where a + * particular burst size yields better DMA performance. + */ +#define DMA_WORD 0x00 +#define DMA_BYTE 0x01 +#define DMA_HWORD 0x02 +#define DMA_JK 0x03 +#define DMA_4WORD 0x04 +#define DMA_8WORD 0x05 +#define DMA_16WORD 0x06 +#define DMA_2WORD 0x07 +#define DMA_4WORDM 0x0C +#define DMA_8WORDM 0x0D +#define DMA_16WORDM 0x0E +#define DMA_2WORDM 0x0F + +/* + * Define the size of the local DMA list we'll build before + * giving up on the PDU. + */ +#define TEMP_DMA_SIZE 120 /* Enough for 58/59 buffers */ + +#define DMA_COUNT_SHIFT 16 /* Number of bits to shift count */ + /* in DMA descriptor word */ +#define DMA_VCC_SHIFT 6 /* Number of bits to shift RX VCC or */ + /* TX channel in DMA descriptor word */ +#define DMA_END_BIT 0x20 /* Signal end of DMA list */ + +/* + * Defines for VCI table + * + * The VCI table is a 1K by 4 word table allowing up to 1024 (0-1023) + * VCIs. Entries into the table use the VCI number as the index. + */ +struct vci_table { + u_long vci_control; /* Control word */ + u_long vci_descr; /* Descr/ReadPtr */ + u_long vci_write; /* WritePtr/State/Cell count */ + u_long vci_crc; /* ongoing CRC calculation */ +}; +typedef volatile struct vci_table VCI_Table; + +#define VCI_MODE_SHIFT 30 /* Shift to get MODE field */ +#define VCI_MODE_MASK 0x3FFFFFFF /* Bits to strip MODE off */ +#define VCI_PTI_SHIFT 29 /* Shift to get PTI mode field */ +#define VCI_LOC_SHIFT 18 /* Shift to get location field */ +#define VCI_LOC_MASK 0x7FF /* Valid bits in location field */ +#define VCI_SIZE_SHIFT 15 /* Shift to get size field */ +#define VCI_SIZE_MASK 7 /* Valid bits in size field */ +#define VCI_IN_SERVICE 1 /* Mask for IN_SERVICE field */ + +/* + * Defines for VC mode + */ +#define VCI_MODE_TRASH 0x00 /* Trash all cells for this VC */ +#define VCI_MODE_AAL0 0x01 /* Reassemble as AAL_0 PDU */ +#define VCI_MODE_AAL5 0x02 /* Reassemble as AAL_5 PDU */ +/* + * Defines for handling cells with PTI(2) set to 1. + */ +#define PTI_MODE_TRASH 0x00 /* Trash cell */ +#define PTI_MODE_PRESV 0x01 /* Send cell to OAM channel */ +/* + * Current state of VC + */ +#define VCI_STATE_IDLE 0x00 /* VC is idle */ +#define VCI_STATE_REASM 0x01 /* VC is reassembling PDU */ +#define VCI_STATE_TRASH 0x03 /* VC is trashing cells */ + +/* + * RX Descriptor word values + */ +#define DESCR_TRASH_BIT 0x1000 /* VCI was trashing cells */ +#define DESCR_CRC_ERR 0x0800 /* PDU has CRC error */ +#define DESCR_CELL_COUNT 0x07FF /* Mask to get cell count */ +/* + * TX Descriptor word values + */ +#define TX_IDEN_SHIFT 28 /* Unique identifier location */ +#define TX_MODE_SHIFT 27 /* AAL5 or AAL0 */ +#define TX_VCI_SHIFT 4 /* Bits to shift VCI value */ + +/* + * When setting up descriptor words (at head of segmentation queues), there + * is a unique identifier used to help detect sync problems. + */ +#define MIDWAY_UNQ_ID 0x0B + +/* + * Defines for cell sizes + */ +#define BYTES_PER_CELL 48 /* Number of data bytes per cell */ +#define WORDS_PER_CELL 12 /* Number of data words per cell */ + +/* + * Access to Serial EEPROM [as opposed to expansion (E)PROM]. + * + * This is a ATMEL AT24C01 serial EEPROM part. + * See http://www.atmel.com/atmel/products/prod162.htm for timimg diagrams + * for START/STOP/ACK/READ cycles. + */ +#define SEEPROM PCI_CONTROL_REG /* Serial EEPROM is accessed thru */ + /* PCI control register */ +#define SEPROM_DATA 0x02 /* SEEPROM DATA line */ +#define SEPROM_CLK 0x01 /* SEEPROM CLK line */ +#define SEPROM_SIZE 128 /* Size of Serial EEPROM */ +#define SEPROM_MAC_OFF 64 /* Offset to MAC address */ +#define SEPROM_SN_OFF 112 /* Offset to serial number */ +#define SEPROM_DELAY 10 /* Delay when strobing CLK/DATA lines */ + +/* + * Host protocol control blocks + * + */ + +/* + * Device VCC Entry + * + * Contains the common and ENI-specific information for each VCC + * which is opened through a ENI device. + */ +struct eni_vcc { + struct cmn_vcc ev_cmn; /* Common VCC stuff */ + caddr_t ev_rxbuf; /* Receive buffer */ + u_long ev_rxpos; /* Adapter buffer read pointer */ +}; +typedef struct eni_vcc Eni_vcc; + +#define ev_next ev_cmn.cv_next +#define ev_toku ev_cmn.cv_toku +#define ev_upper ev_cmn.cv_upper +#define ev_connvc ev_cmn.cv_connvc +#define ev_state ev_cmn.cv_state + +typedef volatile unsigned long * Eni_mem; + +/* + * Define the ID's we'll look for in the PCI config + * register when deciding if we'll support this device. + * The DEV_ID will need to be turned into an array of + * ID's in order to support multiple adapters with + * the same driver. + */ +#define EFF_VENDOR_ID 0x111A +#define EFF_DEV_ID 0x0002 + +/* + * Memory allocator defines and buffer descriptors + */ +#define MEM_FREE 0 +#define MEM_INUSE 1 + +typedef struct mbd Mbd; +struct mbd { + Mbd *prev; + Mbd *next; + caddr_t base; /* Adapter base address */ + int size; /* Size of buffer */ + int state; /* INUSE or FREE */ +}; + +/* + * We use a hack to allocate a smaller RX buffer for signalling + * channels as they tend to have small MTU lengths. + */ +#define UNI_SIG_VCI 5 + +/* + * Device Unit Structure + * + * Contains all the information for a single device (adapter). + */ +struct eni_unit { + Cmn_unit eu_cmn; /* Common unit stuff */ + pcici_t eu_pcitag; /* PCI tag */ + Eni_mem eu_base; /* Adapter memory base */ + Eni_mem eu_ram; /* Adapter RAM */ + u_long eu_ramsize; + + Eni_mem eu_suni; /* SUNI registers */ + + Eni_mem eu_midway; /* MIDWAY registers */ + + VCI_Table *eu_vcitbl; /* VCI Table */ + Eni_mem eu_rxdma; /* Receive DMA queue */ + Eni_mem eu_txdma; /* Transmit DMA queue */ + Eni_mem eu_svclist; /* Service list */ + u_long eu_servread; /* Read pointer into Service list */ + + caddr_t eu_txbuf; /* One large TX buff for everything */ + u_long eu_txsize; /* Size of TX buffer */ + u_long eu_txpos; /* Current word being stored in RAM */ + u_long eu_txfirst; /* First word of unack'ed data */ + + u_long eu_trash; + u_long eu_ovfl; + + struct ifqueue eu_txqueue; + u_long eu_txdmawr; + struct ifqueue eu_rxqueue; + u_long eu_rxdmawr; /* DMA list write pointer */ + + u_char eu_seeprom[SEPROM_SIZE]; /* Serial EEPROM contents */ + u_int eu_sevar; /* Unique (per unit) seeprom var. */ + + Mbd *eu_memmap; /* Adapter RAM memory allocator map */ + int eu_memclicks[ENI_BUF_NBIT];/* Count of INUSE buffers */ + + Eni_stats eu_stats; /* Statistics */ + +}; +typedef struct eni_unit Eni_unit; + +#define eu_pif eu_cmn.cu_pif +#define eu_unit eu_cmn.cu_unit +#define eu_flags eu_cmn.cu_flags +#define eu_mtu eu_cmn.cu_mtu +#define eu_open_vcc eu_cmn.cu_open_vcc +#define eu_vcc eu_cmn.cu_vcc +#define eu_vcc_pool eu_cmn.cu_vcc_pool +#define eu_nif_pool eu_cmn.cu_nif_pool +#define eu_ioctl eu_cmn.cu_ioctl +#define eu_instvcc eu_cmn.cu_instvcc +#define eu_openvcc eu_cmn.cu_openvcc +#define eu_closevcc eu_cmn.cu_closevcc +#define eu_output eu_cmn.cu_output +#define eu_config eu_cmn.cu_config + +#endif /* _ENI_ENI_H */ diff --git a/sys/dev/hea/eni_buffer.c b/sys/dev/hea/eni_buffer.c new file mode 100644 index 0000000..35d1b13 --- /dev/null +++ b/sys/dev/hea/eni_buffer.c @@ -0,0 +1,465 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_buffer.c,v 1.8 1998/08/26 23:28:53 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Handle adapter memory buffers for ENI adapters + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_buffer.c,v 1.8 1998/08/26 23:28:53 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +static int eni_test_memory __P((Eni_unit *)); + +/* + * The host is going to manage (that is, allocate and free) buffers + * in the adapters RAM space. We are going to implement this as a + * linked list describing FREE and INUSE memory segments. Initially, + * the list contains one element with all memory marked free. As requests + * are made, we search the list until we find the first free element + * which can satisfy the request. If necessary, we will break the free + * element into an INUSE element, and a new FREE element. When freeing + * memory, we look at adjacent elements and if one or more are free, + * we will combine into a single larger FREE element. + */ + +/* + * This is for testing purposes. Since there are two versions of + * the Efficient adapter with different memory sizes, this allows + * us to fool an adapter with more memory into thinking it has less. + */ +int eni_mem_max = MAX_ENI_MEM; /* Default to all available memory */ + +/* + * Size and test adapter RAM + * + * Walk through adapter RAM writing known patterns and reading back + * for comparison. We write more than one pattern on the off chance + * that we "get lucky" and read what we expected. + * + * Arguments: + * eup pointer to device unit structure + * + * Returns + * size memory size in bytes + */ +static int +eni_test_memory ( eup ) + Eni_unit *eup; +{ + int ram_size = 0; + int i; + Eni_mem mp; + + /* + * Walk through to maximum looking for RAM + */ + for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) { + mp = (Eni_mem)((int)eup->eu_ram + i); + /* write pattern */ + *mp = (u_long)TEST_PAT; + /* read pattern, match? */ + if ( *mp == (u_long)TEST_PAT ) { + /* yes - write inverse pattern */ + *mp = (u_long)~TEST_PAT; + /* read pattern, match? */ + if ( *mp == (u_long)~TEST_PAT ) { + /* yes - assume another 1K available */ + ram_size = i + TEST_STEP; + } else + break; + } else + break; + } + /* + * Clear all RAM to initial value of zero. + * This makes sure we don't leave anything funny in the + * queues. + */ + KM_ZERO ( eup->eu_ram, ram_size ); + + /* + * If we'd like to claim to have less memory, here's where + * we do so. We take the minimum of what we'd like and what + * we really found on the adapter. + */ + ram_size = MIN ( ram_size, eni_mem_max ); + + return ( ram_size ); + +} + +/* + * Initialize our memory allocator. + * + * Arguments: + * eup Pointer to per unit structure + * + * Returns: + * size Physical RAM size + * -1 failed to initialize memory + * + */ +int +eni_init_memory ( eup ) + Eni_unit *eup; +{ + + /* + * Have we (somehow) been called before? + */ + if ( eup->eu_memmap != NULL ) + { + /* Oops - it's already been initialized */ + return -1; + } + + /* + * Allocate initial element which will hold all of memory + */ + if ( ( eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, + M_NOWAIT ) ) == NULL ) + { + /* Memory allocation error */ + return -1; + } + + /* + * Test and size memory + */ + eup->eu_ramsize = eni_test_memory ( eup ); + + /* + * Initialize a one element list which contains + * all buffer memory + */ + eup->eu_memmap->prev = eup->eu_memmap->next = NULL; + eup->eu_memmap->base = (caddr_t)SEGBUF_BASE; + eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE; + eup->eu_memmap->state = MEM_FREE; + + return ( eup->eu_ramsize ); +} + +/* + * Allocate a buffer from adapter RAM. Due to constraints on the card, + * we may roundup the size request to the next largest chunksize. Note + * also that we must pay attention to address alignment within adapter + * memory as well. + * + * Arguments: + * eup pointer to per unit structure + * size pointer to requested size - in bytes + * + * Returns: + * addr address relative to adapter of allocated memory + * size modified to reflect actual size of buffer + * + */ +caddr_t +eni_allocate_buffer ( eup, size ) + Eni_unit *eup; + u_long *size; +{ + int nsize; + int nclicks; + Mbd *eptr = eup->eu_memmap; + + /* + * Initial size requested + */ + nsize = *size; + + /* + * Find the buffer size which will hold this request. There + * are 8 possible sizes, each a power of two up, starting at + * 256 words or 1024 bytes. + */ + for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ ) + if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize ) + break; + + /* + * Request was for larger then the card supports + */ + if ( nclicks >= ENI_BUF_NBIT ) { + eup->eu_stats.eni_st_drv.drv_mm_toobig++; + /* Indicate 0 bytes allocated */ + *size = 0; + /* Return NULL buffer */ + return ( (caddr_t)NULL ); + } + + /* + * New size will be buffer size + */ + nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ; + + /* + * Look through memory for a segment large enough to + * hold request + */ + while ( eptr ) { + /* + * State must be FREE and size must hold request + */ + if ( eptr->state == MEM_FREE && eptr->size >= nsize ) + { + /* + * Request will fit - now check if the + * alignment needs fixing + */ + if ( ((u_int)eptr->base & (nsize-1)) != 0 ) + { + caddr_t nbase; + + /* + * Calculate where the buffer would have to + * fall to be aligned. + */ + nbase = (caddr_t)((u_int)( eptr->base + nsize ) & + ~(nsize-1)); + /* + * If we use this alignment, will it still fit? + */ + if ( (eptr->size - (nbase - eptr->base)) >= 0 ) + { + Mbd *etmp; + + /* Yep - create a new segment */ + etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, + M_NOWAIT ); + if ( etmp == (Mbd *)NULL ) { + /* + * Couldn't allocate a new descriptor. Indicate + * failure and exit now or we'll start losing + * memory. + */ + eup->eu_stats.eni_st_drv.drv_mm_nodesc++; + *size = 0; + return ( (caddr_t)NULL ); + } + /* Place it in the list */ + etmp->next = eptr->next; + if ( etmp->next ) + etmp->next->prev = etmp; + etmp->prev = eptr; + eptr->next = etmp; + /* Fill in new base and size */ + etmp->base = nbase; + etmp->size = eptr->size - ( nbase - eptr->base ); + /* Adjust old size */ + eptr->size -= etmp->size; + /* Mark its state */ + etmp->state = MEM_FREE; + eptr = etmp; + /* Done - outa here */ + break; + } + } else + break; /* Alignment is okay - we're done */ + } + /* Haven't found anything yet - keep looking */ + eptr = eptr->next; + } + + if ( eptr != NULL ) + { + /* Found a usable segment - grab what we need */ + /* Exact fit? */ + if ( eptr->size == nsize ) + /* Mark it as INUSE */ + eptr->state = MEM_INUSE; + else + { + Mbd *etmp; + /* larger then we need - split it */ + + etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_NOWAIT ); + if ( etmp == (Mbd *)NULL ) { + /* + * Couldn't allocate new descriptor. Indicate + * failure and exit now or we'll start losing + * memory. + */ + eup->eu_stats.eni_st_drv.drv_mm_nodesc++; + *size = 0; + return ( (caddr_t)NULL ); + } + /* Place new element in list */ + etmp->next = eptr->next; + if ( etmp->next ) + etmp->next->prev = etmp; + etmp->prev = eptr; + eptr->next = etmp; + /* Set new base, size and state */ + etmp->base = eptr->base + nsize; + etmp->size = eptr->size - nsize; + etmp->state = MEM_FREE; + /* Adjust size and state of element we intend to use */ + eptr->size = nsize; + eptr->state = MEM_INUSE; + } + } + + /* After all that, did we find a usable buffer? */ + if ( eptr ) + { + /* Record another inuse buffer of this size */ + if ( eptr->base ) + eup->eu_memclicks[nclicks]++; + + /* + * Return true size of allocated buffer + */ + *size = eptr->size; + /* + * Make address relative to start of RAM since + * its (the address) for use by the adapter, not + * the host. + */ + return ((caddr_t)eptr->base); + } else { + eup->eu_stats.eni_st_drv.drv_mm_nobuf++; + /* No buffer to return - indicate zero length */ + *size = 0; + /* Return NULL buffer */ + return ( (caddr_t)NULL ); + } +} + +/* + * Procedure to release a buffer previously allocated from adapter + * RAM. When possible, we'll compact memory. + * + * Arguments: + * eup pointer to per unit structure + * base base adapter address of buffer to be freed + * + * Returns: + * none + * + */ +void +eni_free_buffer ( eup, base ) + Eni_unit *eup; + caddr_t base; +{ + Mbd *eptr = eup->eu_memmap; + int nclicks; + + /* Look through entire list */ + while ( eptr ) + { + /* Is this the buffer to be freed? */ + if ( eptr->base == base ) + { + /* + * We're probably asking for trouble but, + * assume this is it. + */ + if ( eptr->state != MEM_INUSE ) + { + eup->eu_stats.eni_st_drv.drv_mm_notuse++; + /* Huh? Something's wrong */ + return; + } + /* Reset state to FREE */ + eptr->state = MEM_FREE; + + /* Determine size for stats info */ + for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ ) + if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size ) + break; + + /* Valid size? Yes - decrement inuse count */ + if ( nclicks < ENI_BUF_NBIT ) + eup->eu_memclicks[nclicks]--; + + /* Try to compact neighbors */ + /* with previous */ + if ( eptr->prev ) + if ( eptr->prev->state == MEM_FREE ) + { + Mbd *etmp = eptr; + /* Add to previous block */ + eptr->prev->size += eptr->size; + /* Set prev block to skip this one */ + eptr->prev->next = eptr->next; + /* Set next block to skip this one */ + if ( eptr->next ) + eptr->next->prev = eptr->prev; + /* Reset to where we want to be */ + eptr = eptr->prev; + /* and free this element */ + (void)KM_FREE(etmp, etmp->size, M_DEVBUF); + } + /* with next */ + if ( eptr->next ) + if ( eptr->next->state == MEM_FREE ) + { + Mbd *etmp = eptr->next; + + /* add following block in */ + eptr->size += etmp->size; + /* set next next block to skip next block */ + if ( etmp->next ) + etmp->next->prev = eptr; + /* skip next block */ + eptr->next = etmp->next; + /* and free next element */ + (void)KM_FREE(etmp, etmp->size, M_DEVBUF); + } + /* + * We've freed the buffer and done any compaction, + * we needn't look any further... + */ + return; + } + eptr = eptr->next; + } + + if ( eptr == NULL ) + { + /* Oops - failed to find the buffer. This is BAD */ + eup->eu_stats.eni_st_drv.drv_mm_notfnd++; + } + +} + diff --git a/sys/dev/hea/eni_globals.c b/sys/dev/hea/eni_globals.c new file mode 100644 index 0000000..a2a51fa --- /dev/null +++ b/sys/dev/hea/eni_globals.c @@ -0,0 +1,102 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_globals.c,v 1.2 1997/05/06 22:07:52 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Global variable definitions + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_globals.c,v 1.2 1997/05/06 22:07:52 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +/* + * Device unit table + */ +Eni_unit *eni_units[ENI_MAX_UNITS] = {NULL}; + +/* + * ATM Interface services + */ +/* + * AAL5 service stack + */ +static struct stack_defn eni_svaal5 = { + NULL, + SAP_CPCS_AAL5, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +/* + * Efficient hardware doesn't support AAL3/4. Don't define + * an AAL3/4 stack. + */ +/* + * AAL0 service stack + */ +static struct stack_defn eni_svaal0 = { + &eni_svaal5, + SAP_ATM, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +struct stack_defn *eni_services = &eni_svaal0; + +/* + * Storage pools + */ +struct sp_info eni_nif_pool = { + "eni nif pool", /* si_name */ + sizeof(struct atm_nif), /* si_blksiz */ + 5, /* si_blkcnt */ + 20 /* si_maxallow */ +}; + +struct sp_info eni_vcc_pool = { + "eni vcc pool", /* si_name */ + sizeof(Eni_vcc), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + diff --git a/sys/dev/hea/eni_if.c b/sys/dev/hea/eni_if.c new file mode 100644 index 0000000..41ffd48 --- /dev/null +++ b/sys/dev/hea/eni_if.c @@ -0,0 +1,270 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_if.c,v 1.6 1998/08/26 23:28:53 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Network interface layer support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_if.c,v 1.6 1998/08/26 23:28:53 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_suni.h> +#include <dev/hea/eni_var.h> + +static void eni_get_stats __P((Eni_unit *)); + +/* + * SUNI statistics counters take one of three forms: + * single byte value (0x0 - 0xff) + * two byte value (0x0 - 0xffff) + * two + 1/2 (three) byte value + * (0x0 - 0x0fffff) + */ +#define READ_ONE(x) ( (eup->eu_suni[(x)] & 0xff) ) + +#define READ_TWO(x) ( (eup->eu_suni[(x)+1] & 0xff) << 8 | \ + (eup->eu_suni[(x)] & 0xff) ) + +#define READ_THREE(x) ( (eup->eu_suni[(x)+2] & 0xf) << 16 | \ + (eup->eu_suni[(x)+1] & 0xff) << 8 | \ + (eup->eu_suni[(x)] & 0xff) ) + +/* + * Do an initial read of the error counters without saving them. + * In effect, this will "zero" our idea of the number of errors + * which have occurred since the driver was loaded. + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * none + * + */ +void +eni_zero_stats ( eup ) + Eni_unit *eup; +{ + int val; + + /* + * Write the SUNI master control register which + * will cause all the statistics counters to be + * loaded. + */ + eup->eu_suni[SUNI_MASTER_REG] = eup->eu_suni[SUNI_MASTER_REG]; + + /* + * Delay to allow for counter load time... + */ + DELAY ( SUNI_DELAY ); + + /* + * Statistics counters contain the number of events + * since the last time the counter was read. + */ + val = READ_TWO ( SUNI_SECT_BIP_REG ); /* oc3_sect_bip8 */ + val = READ_TWO ( SUNI_PATH_BIP_REG ); /* oc3_path_bip8 */ + val = READ_THREE ( SUNI_LINE_BIP_REG ); /* oc3_line_bip24 */ + val = READ_THREE ( SUNI_LINE_FEBE_REG ); /* oc3_line_febe */ + val = READ_TWO ( SUNI_PATH_FEBE_REG ); /* oc3_path_febe */ + val = READ_ONE ( SUNI_HECS_REG ); /* oc3_hec_corr */ + val = READ_ONE ( SUNI_UHECS_REG ); /* oc3_hec_uncorr */ +} + +/* + * Retrieve SUNI stats + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * none + * + */ +static void +eni_get_stats ( eup ) + Eni_unit *eup; +{ + /* + * Write the SUNI master control register which + * will cause all the statistics counters to be + * loaded. + */ + eup->eu_suni[SUNI_MASTER_REG] = eup->eu_suni[SUNI_MASTER_REG]; + + /* + * Delay to allow for counter load time... + */ + DELAY ( 10 ); + + /* + * Statistics counters contain the number of events + * since the last time the counter was read. + */ + eup->eu_stats.eni_st_oc3.oc3_sect_bip8 += + READ_TWO ( SUNI_SECT_BIP_REG ); + eup->eu_stats.eni_st_oc3.oc3_path_bip8 += + READ_TWO ( SUNI_PATH_BIP_REG ); + eup->eu_stats.eni_st_oc3.oc3_line_bip24 += + READ_THREE ( SUNI_LINE_BIP_REG ); + eup->eu_stats.eni_st_oc3.oc3_line_febe += + READ_THREE ( SUNI_LINE_FEBE_REG ); + eup->eu_stats.eni_st_oc3.oc3_path_febe += + READ_TWO ( SUNI_PATH_FEBE_REG ); + eup->eu_stats.eni_st_oc3.oc3_hec_corr += + READ_ONE ( SUNI_HECS_REG ); + eup->eu_stats.eni_st_oc3.oc3_hec_uncorr += + READ_ONE ( SUNI_UHECS_REG ); +} + +/* + * Handle netatm core service interface ioctl requests + * + * Called at splnet. + * + * Arguments: + * code ioctl function (sub)code + * data data to/from ioctl + * arg optional code-specific argument + * + * Returns: + * 0 request processed successfully + * error request failed - reason code + * + */ +int +eni_atm_ioctl ( code, data, arg ) + int code; + caddr_t data; + caddr_t arg; +{ + struct atminfreq *aip = (struct atminfreq *)data; + struct atm_pif *pip = (struct atm_pif *)arg; + Eni_unit *eup = (Eni_unit *)pip; + caddr_t buf = aip->air_buf_addr; + struct air_vinfo_rsp *avr; + int count, len, buf_len = aip->air_buf_len; + int err = 0; + char ifname[2*IFNAMSIZ]; + + ATM_DEBUG2("eni_atm_ioctl: code=%d, opcode=%d\n", + code, aip->air_opcode ); + + switch ( aip->air_opcode ) { + + case AIOCS_INF_VST: + /* + * Get vendor statistics + */ + if ( eup == NULL ) + return ( ENXIO ); + sprintf ( ifname, "%s%d", pip->pif_name, pip->pif_unit ); + + /* + * Cast response structure onto user's buffer + */ + avr = (struct air_vinfo_rsp *)buf; + + /* + * How large is the response structure + */ + len = sizeof(struct air_vinfo_rsp); + + /* + * Sanity check - enough room for response structure? + */ + if ( buf_len < len ) + return ( ENOSPC ); + + /* + * Copy interface name into response structure + */ + if ( err = copyout ( ifname, avr->avsp_intf, IFNAMSIZ ) ) + break; + + /* + * Advance the buffer address and decrement the size + */ + buf += len; + buf_len -= len; + + /* + * Get the vendor stats (SUNI) from the hardware + */ + eni_get_stats ( eup ); + /* + * Stick as much of it as we have room for + * into the response + */ + count = MIN ( sizeof(Eni_stats), buf_len ); + + /* + * Copy stats into user's buffer. Return value is + * amount of data copied. + */ + if ( err = copyout ((void *)&eup->eu_stats, buf, + count)) + break; + buf += count; + buf_len -= count; + if ( count < sizeof(Eni_stats) ) + err = ENOSPC; + + /* + * Record amount we're returning as vendor info... + */ + if (err = copyout(&count, &avr->avsp_len, sizeof(int))) + break; + + /* + * Update the reply pointers and length + */ + aip->air_buf_addr = buf; + aip->air_buf_len = buf_len; + break; + + default: + err = ENOSYS; /* Operation not supported */ + break; + } + + return ( err ); + +} + diff --git a/sys/dev/hea/eni_init.c b/sys/dev/hea/eni_init.c new file mode 100644 index 0000000..d8106ab --- /dev/null +++ b/sys/dev/hea/eni_init.c @@ -0,0 +1,142 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_init.c,v 1.6 1998/08/26 23:28:53 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Driver initialization support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_init.c,v 1.6 1998/08/26 23:28:53 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +/* + * Initialize adapter for PDU processing + * + * Enable interrupts, set master control, initialize TX buffer, + * set initial pointers, etc. + * + * Arguments: + * eup pointer to device unit structure + * + * Returns: + * 0 successful + * error error condition + */ +int +eni_init ( eup ) + Eni_unit *eup; +{ + u_long words, order; + + /* + * Allocate one large TX buffer. Currently we use only one + * channel with full cell rate which all VCs will use. + * This will (probably) have to change (alot) when we + * implement QoS. + */ + /* + * Server cards, which have more then 512KB of RAM, will + * allocate a 128KB TX buffer, while client cards, with + * 512KB or less will allocate a 32KB TX buffer. + */ + words = ( eup->eu_ramsize > MAX_CLIENT_RAM * ENI_BUF_PGSZ ? + TX_LARGE_BSIZE : TX_SMALL_BSIZE ) * ENI_BUF_PGSZ; + if ( ( eup->eu_txbuf = eni_allocate_buffer ( eup, &words ) ) == + (caddr_t)NULL ) { + return ENOMEM; + } + eup->eu_txsize = words >> 2; /* Bytes to words */ + words >>= ENI_LOC_PREDIV; /* Predivide by 256 words */ + for ( order = -1; words; order++ ) + words >>= 1; + eup->eu_midway[MIDWAY_TXPLACE] = + (order << TXSIZE_SHIFT) | ((int)eup->eu_txbuf >> ENI_LOC_PREDIV); + eup->eu_txpos = eup->eu_midway[MIDWAY_DESCR] & 0x7FFF; + /* + * Set first word of unack'ed data to start + */ + eup->eu_txfirst = eup->eu_txpos; + + /* + * Set initial values of local DMA pointer used to prevent wraps + */ + eup->eu_txdmawr = 0; + eup->eu_rxdmawr = 0; + + /* + * Initialize queue's for receive/transmit pdus + */ + eup->eu_txqueue.ifq_maxlen = ENI_IFQ_MAXLEN; + eup->eu_rxqueue.ifq_maxlen = ENI_IFQ_MAXLEN; + + /* + * Acknowledge any interrupts + */ + (void) eup->eu_midway[MIDWAY_ISA]; + + /* + * "Zero" Sonet error counters + */ + eni_zero_stats ( eup ); + + /* + * Set master control register + * + * IntSel1 | LOCK_MODE | DMA_ENABLE | TX_ENABLE | RX_ENABLE + * + */ + eup->eu_midway[MIDWAY_MASTER] = 1 << ENI_ISEL_SHIFT | + ENI_M_DMAENABLE | ENI_M_TXENABLE | ENI_M_RXENABLE; + + /* + * Enable interrupts + */ + eup->eu_midway[MIDWAY_IE] = ENI_INT_SERVICE | ENI_INT_RX_DMA | + ENI_INT_TX_DMA | ENI_INT_DMA_ERR | ENI_INT_DMA_LERR | + ENI_INT_IDEN | ENI_INT_DMA_OVFL; + + /* + * Last thing to do is to indicate that we've finished initializing + * this unit. + */ + eup->eu_flags |= CUF_INITED; + + return 0; +} + diff --git a/sys/dev/hea/eni_intr.c b/sys/dev/hea/eni_intr.c new file mode 100644 index 0000000..e0a5642 --- /dev/null +++ b/sys/dev/hea/eni_intr.c @@ -0,0 +1,230 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_intr.c,v 1.4 1998/08/26 23:28:54 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Interrupt processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_intr.c,v 1.4 1998/08/26 23:28:54 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_suni.h> +#include <dev/hea/eni_var.h> + +static void eni_suni_intr __P((Eni_unit *)); + +/* + * SUNI Interrupt processing + * + * Currently, we don't do anything more then clear the interrupt + * for the SUNI chip. + * + * Arguments: + * eup pointer to device unit structure + * + * Returns: + * none + * + */ +static void +eni_suni_intr ( eup ) + Eni_unit *eup; +{ + int SuniInt; + int val; + + SuniInt = eup->eu_suni[SUNI_IS_REG]; + + /* RSOPI */ + if ( SuniInt & SUNI_RSOPI ) + val = eup->eu_suni[SUNI_RSOP_REG]; + + /* RLOPI */ + if ( SuniInt & SUNI_RLOPI ) + val = eup->eu_suni[SUNI_RLOP_REG]; + + /* RPOPI */ + if ( SuniInt & SUNI_RPOPI ) + val = eup->eu_suni[SUNI_RPOP_IS_REG]; + + /* RACPI */ + if ( SuniInt & SUNI_RACPI ) + val = eup->eu_suni[SUNI_RACP_REG]; + + /* TACPI */ + if ( SuniInt & SUNI_TACPI ) + val = eup->eu_suni[SUNI_TACP_REG]; + + /* TROOLI */ + if ( SuniInt & SUNI_TROOLI ) + val = eup->eu_suni[SUNI_CLOCK_REG]; + + /* LCDI */ + /* Cleared when reading Master Interrupt Status Reg */ + + /* RDOOLI */ + if ( SuniInt & SUNI_RDOOLI ) + val = eup->eu_suni[SUNI_CLOCK_REG]; + + return; +} + +/* + * Device interrupt routine + * + * Service an interrupt from this device + * + * Arguments: + * eup pointer to device unit structure + * + * Returns: + * none + * + */ +#if defined(BSD) && BSD < 199506 +int +#else +void +#endif +eni_intr ( arg ) + void *arg; +{ + Eni_unit *eup = (Eni_unit *)arg; +#if defined(BSD) && BSD < 199506 + int serviced = 1; +#endif /* BSD < 199506 */ + + /* + * Read and acknowledge any interrupts + */ + u_long mask = eup->eu_midway[MIDWAY_ISA]; + /* + * Read the error statistics counter + */ + u_long sval = eup->eu_midway[MIDWAY_STAT]; + + /* + * Update statistics from adapter + */ + eup->eu_trash += ( sval >> 16 ); + eup->eu_ovfl += ( sval & 0xffff ); + + /* + * We handle any DMA completes first so + * that we can free resources for use + * during transmit and especially receive + */ + /* + * Handle RX DMA Complete + */ + if ( mask & ENI_INT_RX_DMA ) { + eni_recv_drain ( eup ); + } + + /* + * Handle TX DMA Complete + */ + if ( mask & ENI_INT_TX_DMA ) { + eni_xmit_drain ( eup ); + } + + /* + * Look for any PDUs in service list + */ + if ( mask & ENI_INT_SERVICE ) { + eni_do_service ( eup ); + } + + /* + * Handle miscelaneous interrupts + */ + if ( mask & ENI_INT_STAT ) { /* STAT_OVFL */ + log ( LOG_INFO, "eni_intr: stat_ovfl: 0x%x\n", sval ); + } + if ( mask & ENI_INT_SUNI ) { /* SUNI_INTR */ + eni_suni_intr ( eup ); + } + if ( mask & ENI_INT_DMA_ERR ) { /* DMA Error */ + log ( LOG_ERR, + "eni_intr: DMA Error\n" ); + /* + * We don't know how to recover from DMA errors + * yet. The adapter has disabled any further + * processing and we're going to leave it like + * that. + */ +#if defined(BSD) && BSD < 199506 + return serviced; /* Leave now */ +#else + return; /* Leave now */ +#endif + } + if ( mask & ENI_INT_IDEN ) { + log ( LOG_ERR, + "eni_intr: TX DMA Ident mismatch\n" ); + /* + * Something in the TX buffer has really gotten messed + * up. Since this is most likely a driver bug, and + * the adapter has shut everything down, leave it + * like that. + */ +#if BSD < 199506 + return 0; /* Leave now */ +#else + return; /* Leave now */ +#endif + } + if ( mask & ENI_INT_DMA_OVFL ) + eup->eu_stats.eni_st_drv.drv_xm_dmaovfl++; + if ( mask & ENI_INT_DMA_LERR ) { + log ( LOG_ERR, + "eni_intr: DMA LERR\n" ); +#if BSD < 199506 + return 0; +#else + return; +#endif + } + +#if BSD < 199506 + return 0; +#else + return; +#endif +} + diff --git a/sys/dev/hea/eni_receive.c b/sys/dev/hea/eni_receive.c new file mode 100644 index 0000000..097f297 --- /dev/null +++ b/sys/dev/hea/eni_receive.c @@ -0,0 +1,871 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_receive.c,v 1.13 1998/08/07 22:14:13 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Receive management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_receive.c,v 1.13 1998/08/07 22:14:13 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +static void eni_recv_stack __P((void *, KBuffer *)); + +#ifdef DIAGNOSTIC +extern int eni_pdu_print; +#endif + +/* + * Procedure to remove VCs from the Service List and generate DMA + * requests to move the associated PDUs into host memory. As PDUs + * are completed in adapter memory, the adapter examines the IN_SERVICE + * bit for the VC in the VC table. If this bit is not set, the adapter + * will place the VC number at the end of the service list queue, set + * the IN_SERVICE bit in the VC table, and interrupt the host. The host + * will remove VCs from the service list, clear the IN_SERVICE bit in + * the VC table, and create a DMA list to move the PDU into host buffers. + * + * Arguments: + * eup pointer to per unit structure + * + * Returns: + * none + * + */ +void +eni_do_service ( eup ) + Eni_unit *eup; +{ + int vcc; + Eni_vcc *evp; + u_long servwrite; + VCI_Table *vct; + u_long rdptr; + u_long *rxp; + KBuffer *m; + u_long dma[TEMP_DMA_SIZE]; + u_long i, j; + u_long dma_rd, dma_wr; + u_long dma_avail; + int pdulen; + int mask; + u_long *upp; + + /* + * Where is the adapter currently inserting entries? + */ + servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK; + /* + * As long as we're not caught up with the adapter, keep + * removing VCs from the service list. + */ + while ( servwrite != eup->eu_servread ) { + int vci_hdr; + u_long descr; + + /* + * Get VC number and find VC table entry. + */ + vcc = eup->eu_svclist[eup->eu_servread]; + vct = &eup->eu_vcitbl[vcc]; + vci_hdr = vct->vci_control; /* Current status */ + + /* + * Check that this VCC still needs servicing. We + * might have closed this VCC down in between + * the adapter setting the flag and our checking + * the flag. Also check that we haven't placed the + * VCC into TRASH mode. + */ + if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 || + ( vci_hdr & ~VCI_MODE_MASK == + VCI_MODE_TRASH << VCI_MODE_SHIFT ) ) + goto next_vcc; + + /* + * Find the size of this VCs buffer + */ + mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK; + mask = 1 << (ENI_LOC_PREDIV + mask); + /* Turn byte count into word count */ + mask >>= 2; + /* + * Find the start of the adapter buffer for this VC. + */ + rxp = (u_long *) + ((int)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK) + << ENI_LOC_PREDIV) + (int)eup->eu_ram); + /* + * Locate incoming VCC for this PDU and find where we + * should next read from. + */ + evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup, + 0, vcc, VCC_IN ); + if ( evp == (Eni_vcc *)NULL ) + goto next_vcc; /* VCI no longer active */ + rdptr = evp->ev_rxpos; + /* + * Find out where the adapter is currently reassembling. + * The PDU which starts at descr is not yet complete so we + * must stop there. + */ + descr = ( vct->vci_descr >> 16 ) & 0x7FFF; + /* + * As long as we haven't processed all the completed PDUs on + * this VC, keep going... + */ + while ( rdptr != descr ) + { + int n_cells; + int pdu_descr; + int aal5; + + /* + * Ensure that the following are reset for every new + * PDU. + */ + upp = NULL; + m = NULL; + + /* + * Fisrt build a DMA with JK to skip the descriptor word. + * We must always skip the descriptor even if it turns out + * that there isn't any PDU here. + */ + j = 0; + dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) | + ( vcc << DMA_VCC_SHIFT ) | DMA_JK; + dma[j++] = 0; + + /* + * We'll use some of the values below for skipping + * bad PDUs or counting statistics so compute them + * now. + */ + + /* + * Grab a copy of the descriptor word + */ + pdu_descr = rxp[rdptr]; + + /* + * Strip out cell count from descriptor word. + * At this point, we still don't know if there + * is any real data until after we check for + * TRASH mode. + */ + n_cells = pdu_descr & DESCR_CELL_COUNT; + + /* + * Is this an AAL5 PDU? Check MODE in vci_hdr. + */ + aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) == + VCI_MODE_AAL5 << VCI_MODE_SHIFT ); + + /* + * Now check to see if we're trashing on this vcc. + * If so, there is no data with this VC and the + * next word after the current descriptor is the + * descriptor for the next PDU. + */ + if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) { + if ( aal5 ) + /* + * Count as number of AAL5 cells dropped + */ + eup->eu_stats.eni_st_aal5.aal5_drops += n_cells; + else + /* + * Count as number of AAL0 cells dropped + */ + eup->eu_stats.eni_st_aal0.aal0_drops += n_cells; + eup->eu_pif.pif_ierrors++; + /* + * When cells have been trashed, all we have in the + * buffer is a descriptor word. There are no data + * words. Set the number of cells to zero so that + * we correctly skip to the next word which will + * be the descriptor for the next PDU. + */ + n_cells = 0; + /* + * Go issue the DMA to skip this descriptor word. + */ + goto send_dma; + } + + /* + * Data length: number of cells * cell size + */ + pdulen = n_cells * BYTES_PER_CELL; + + /* + * If this is an AAL5 PDU, then we need to check + * for the presence of any CRC errors. If there + * is one or more CRC errors, then we are going to + * drop this PDU. + */ + if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) { + /* + * Count the stat + */ + eup->eu_pif.pif_ierrors++; + eup->eu_stats.eni_st_aal5.aal5_pdu_crc++; + if ( evp->ev_connvc->cvc_vcc ) + evp->ev_connvc->cvc_vcc->vc_ierrors++; + /* + * Build a DMA entry to skip the rest of this + * PDU. + */ + dma[j++] = + (((rdptr + n_cells*WORDS_PER_CELL + 1) + & (mask-1)) << DMA_COUNT_SHIFT ) | + (vcc << DMA_VCC_SHIFT ) | DMA_JK; + dma[j++] = 0; + /* + * All done with this PDU. Get a buffer to save some + * data for reclamation services. + */ + KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, + KB_T_DATA ); + if ( m ) { + u_long *up; + + KB_DATASTART ( m, up, u_long * ); + /* + * Indicate no PDU + */ + KB_PLENSET ( m, 0 ); + /* + * Set buffer length - only driver overhead + */ + KB_LEN ( m ) = 3 * sizeof ( u_long ); + /* + * Insert vcc, space for DMA pointers, + * and pdulen + */ + *up++ = vcc; + upp = up; /* Remember location */ + up++; /* And skip it */ + /* - to be filled later */ + *up = pdulen; /* Actual PDU length if it */ + /* were valid */ + } else { + /* + * We've a real problem here as now we can't + * reclaim/advance resources/safety pointers. + */ + eup->eu_stats.eni_st_drv.drv_rv_norsc++; +#ifdef DO_LOG + log ( LOG_ERR, + "eni_do_service: No drain buffers available. Receiver about to lock.\n" ); +#endif + } + goto send_dma; + } + + /* + * Do we need to strip the AAL layer? Yes if this + * is an AAL5 PDU. + */ + if ( aal5 ) { + /* + * Grab the CS-PDU length. Find the address of the + * last word, back up one word to skip CRC, and + * then mask the whole thing to handle circular wraps. + */ + pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1) + & (mask-1)] + & 0xFFFF; + } + + /* + * We now have a valid PDU of some length. Build + * the necessary DMA list to move it into host + * memory. + */ + + /* + * Get an initial buffer. + */ + KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); + /* + * Do we have a valid buffer? + */ + if ( m != (KBuffer *)NULL ) + { + int len; + u_long *up; + KBuffer *m0; + + KB_DATASTART ( m, up, u_long * ); + /* + * Fill in pdulen in PKTHDR structure (for IP). + */ + KB_PLENSET ( m, pdulen ); + /* + * We're going to save the VCI nuber, the start + * and stop DMA pointers, and the PDU length at + * the head of the buffer. We'll pull this out + * later after the DMA has completed. + * + * Insert VCI number as first word in first buffer, + * remeber where we want to store the start/stop + * pointers, and store the PDU length. + */ + *up++ = vcc; /* PDU's VCC */ + upp = up; /* Remember where we are */ + up++; /* To stuff start/stop pointers in */ + *up++ = pdulen; /* PDU's length */ + /* + * Leave some extra room in case a higher protocol + * (IP) wants to do a pullup. Maybe we can keep + * someone from having to allocate another buffer + * a do a larger memory copy. + */ + len = MIN ( ENI_SMALL_BSIZE, pdulen ); + (void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j, + vcc, (u_long)up, len ); + /* + * Adjust length of remaining data in PDU + */ + pdulen -= len; + /* + * Set buffer length, including our overhead + */ + KB_LEN ( m ) = len + 3 * sizeof ( u_long ); + /* + * Finish by moving anything which won't fit in + * first buffer + */ + m0 = m; + while ( pdulen ) { + KBuffer *m1; + u_long data_addr; + + /* + * Get another buffer + */ + KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT, + KB_T_DATA ); + + /* + * If we succeeded... + */ + if ( m1 ) { + /* + * Figure out how much we can move into + * this buffer. + */ + len = MIN ( ENI_LARGE_BSIZE, pdulen ); + /* + * Setup DMA list for this buffer + */ + KB_DATASTART ( m1, data_addr, u_long ); + (void) eni_set_dma + ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc, + data_addr, len ); + /* + * Adjust remaining length + */ + pdulen -= len; + /* + * Set buffer length + */ + KB_LEN ( m1 ) = len; + /* + * Link new buffer onto end and advance + * pointer + */ + KB_NEXT ( m0 ) = m1; + m0 = m1; + } else { + /* + * Either we were unable to grab another + * buffer or there are no large buffers + * available. We know that the first + * buffer is valid, so drop everything + * else, build a JK DMA to skip/drop this + * PDU, set the pointers to reclaim + * resources/advance pointers, and + * finish this PDU now. + */ + if ( KB_NEXT ( m ) ) + KB_FREEALL ( KB_NEXT ( m ) ); + eup->eu_pif.pif_ierrors++; + j = 2; + dma[j++] = + (((rdptr + n_cells*WORDS_PER_CELL + 1) + & (mask-1)) << DMA_COUNT_SHIFT ) | + (vcc << DMA_VCC_SHIFT ) | + DMA_JK; + dma[j++] = 0; + /* + * Reset PDU length to zero + */ + KB_PLENSET ( m, 0 ); + /* + * Count some statistics + */ + /* + * Count this as dropped cells + */ + if ( aal5 ) { + eup->eu_stats.eni_st_aal5.aal5_drops += + n_cells; + eup->eu_stats.eni_st_aal5.aal5_pdu_drops++; + } else + eup->eu_stats.eni_st_aal0.aal0_drops += + n_cells; + /* + * Drop it + */ + goto send_dma; + } + } + /* + * If necessary, skip AAL layer + */ + if ( aal5 ) { + dma[j++] = + (((rdptr + n_cells*WORDS_PER_CELL + 1) + & (mask-1)) << DMA_COUNT_SHIFT) + | (vcc << DMA_VCC_SHIFT) | DMA_JK; + dma[j++] = 0; + } + } else { + /* + * We failed to get an initial buffer. Since we + * haven't changed anything for this PDU yet and the + * PDU is still valid, exit now and try to service it + * next time around. We're not very likely to get + * another buffer right now anyways. + */ + eup->eu_stats.eni_st_drv.drv_rv_nobufs++; +#ifdef DO_LOG + log ( LOG_ERR, +"eni_do_service: No buffers available. Exiting without servicing service list.\n" ); +#endif + /* + * Clear the IN_SERVICE indicator for this VCC + */ + vct->vci_control &= ~VCI_IN_SERVICE; + return; + } + +send_dma: + /* + * Set the end bit on the last DMA for this PDU + */ + dma[j-2] |= DMA_END_BIT; + + /* + * Where are the current DMA pointers + */ + dma_rd = eup->eu_midway[MIDWAY_RX_RD]; + dma_wr = eup->eu_midway[MIDWAY_RX_WR]; + + /* + * Check how much space is available + */ + if ( dma_rd == dma_wr ) + dma_avail = DMA_LIST_SIZE; + else + dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr ) + & (DMA_LIST_SIZE-1); + + /* + * Check for queue full or wrap past write okay pointer + */ + if ( dma_avail < j || + ( dma_wr + j > eup->eu_rxdmawr + DMA_LIST_SIZE ) ) { + /* + * There's no room in the DMA list to insert + * this request. Since we haven't changed anything + * yet and the PDU is good, exit now and service + * it next time around. What we really need to do + * is wait for the RX list to drain and that won't + * happen if we keep trying to process PDUs here. + */ + eup->eu_stats.eni_st_drv.drv_rv_nodma++; +#ifdef DO_LOG + log ( LOG_ERR, +"eni_do_service: No room in receive DMA list. Postponing service request.\n" ); +#endif + /* + * Free the local buffer chain + */ + KB_FREEALL ( m ); + /* + * Clear the IN_SERVICE indicator for this VCC. + */ + vct->vci_control &= ~VCI_IN_SERVICE; + return; + } + + /* + * If we have a buffer chain, save the starting + * dma_list location. + */ + if ( upp ) { + *upp = dma_wr << 16; + } + + /* + * Stuff the DMA list + */ + j >>= 1; + for ( i = 0; i < j; i++ ) { + eup->eu_rxdma[dma_wr*2] = dma[i*2]; + eup->eu_rxdma[dma_wr*2+1] = dma[i*2+1]; + dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1); + } + /* + * If we have a buffer chain, save the location of + * the ending dma_list location and queue the chain + * so that we can recover the resources later. + */ + if ( upp ) { + *upp |= dma_wr; + /* + * Place buffer on receive queue waiting for RX_DMA + */ + if ( IF_QFULL ( &eup->eu_rxqueue ) ) { + /* + * We haven't done anything we can't back out + * of. Drop request and service it next time. + * We've inserted the DMA list but it's not + * valid until we advance the RX_WR pointer, + * thus it's okay to bail here... + */ + eup->eu_stats.eni_st_drv.drv_rv_rxq++; +#ifdef DO_LOG + log ( LOG_ERR, + "eni_do_service: RX drain queue full. Postponing servicing.\n" ); +#endif + KB_FREEALL ( m ); + /* + * Clear the IN_SERVICE indicator for this VCC. + */ + vct->vci_control &= ~VCI_IN_SERVICE; + return; + } else { + IF_ENQUEUE ( &eup->eu_rxqueue, m ); + /* + * Advance the RX_WR pointer to cause + * the adapter to work on this DMA list. + */ + eup->eu_midway[MIDWAY_RX_WR] = dma_wr; + } + } + /* + * Advance our notion of where the next PDU + * should start. + */ + rdptr = (rdptr + n_cells*WORDS_PER_CELL + 1) + & (mask-1); + evp->ev_rxpos = rdptr; + + /* + * Increment cells/pdu received stats. + */ + eup->eu_stats.eni_st_atm.atm_rcvd += n_cells; + if ( aal5 ) { + eup->eu_stats.eni_st_aal5.aal5_rcvd += n_cells; + eup->eu_stats.eni_st_aal5.aal5_pdu_rcvd++; + } else { + eup->eu_stats.eni_st_aal0.aal0_rcvd += n_cells; + } + + /* + * Continue processing PDUs on this same VCI + */ + } + +next_vcc: + /* + * Advance to next entry in the service_list. + */ + eup->eu_servread = (eup->eu_servread + 1) & SVC_SIZE_MASK; + + /* + * And clear the IN_SERVICE indicator for this VCC. + */ + vct->vci_control &= ~VCI_IN_SERVICE; + } + return; +} + +/* + * Drain Receive queue + * + * As we build DMA lists to move PDUs from adapter buffers into host + * buffers, we place the request on a private ifqueue so that we can + * free any resources AFTER we know they've been successfully DMAed. + * As part of the service processing, we record the PDUs start and stop + * entries in the DMA list, and prevent wrapping. When we pull the top + * entry off, we simply check that the current DMA location is outside + * this PDU and if so, it's okay to free things. + * + * Arguments: + * eup pointer to device unit structure + * + * Returns: + * none + * + */ +void +eni_recv_drain ( eup ) + Eni_unit *eup; +{ + KBuffer *m; + Eni_vcc *evp; + struct vccb *vcp; + u_long vcc; + u_long DMA_Rdptr; + u_long dma_wrp; + u_long start, stop; + int que = 0; + int s; + + s = splimp(); + /* Pop first buffer */ + IF_DEQUEUE ( &eup->eu_rxqueue, m ); + while ( m ) { + u_long *up; + u_long pdulen; + + KB_DATASTART ( m, up, u_long * ); + + /* + * Grab the VCI number + */ + vcc = *up++; + + /* + * Check to see if we can process this buffer yet. + */ + /* Get current DMA_Rdptr */ + DMA_Rdptr = eup->eu_midway[MIDWAY_RX_RD]; + /* Boundaries for first buffer */ + dma_wrp = *up++; + start = dma_wrp >> 16; + stop = dma_wrp & 0xffff; + /* + * Start should not equal stop because that would + * mean we tried inserting a NULL DMA list. + */ + if ( start > stop ) { /* We wrapped */ + if ( !(DMA_Rdptr >= stop && DMA_Rdptr < start) ) { + IF_PREPEND ( &eup->eu_rxqueue, m ); + goto finish; + } + } else { + if ( DMA_Rdptr < stop && DMA_Rdptr >= start ) { + IF_PREPEND ( &eup->eu_rxqueue, m ); + goto finish; + } + } + /* + * Adapter is finished with this buffer, we can + * continue processing it now. + */ + + /* + * Locate incoming VCC for this PDU + */ + evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup, + 0, vcc, VCC_IN ); + + if ( evp == NULL ) { + eup->eu_stats.eni_st_drv.drv_rv_novcc++; + KB_FREEALL ( m ); + goto next_buffer; + } + +#ifdef DIAGNOSTIC + if ( eni_pdu_print ) + atm_dev_pdu_print ( (Cmn_unit *)eup, (Cmn_vcc *)evp, m, + "eni_stack_drain" ); +#endif + + /* + * Grab theoretical PDU length + */ + pdulen = *up++; + + /* + * Quick, count the PDU + */ + eup->eu_pif.pif_ipdus++; + eup->eu_pif.pif_ibytes += pdulen; + if ( evp ) { + vcp = evp->ev_connvc->cvc_vcc; + if ( vcp ) { + vcp->vc_ipdus++; + vcp->vc_ibytes += pdulen; + if ( vcp->vc_nif ) { + vcp->vc_nif->nif_ibytes += pdulen; + vcp->vc_nif->nif_if.if_ipackets++; +#if (defined(BSD) && (BSD >= 199103)) + vcp->vc_nif->nif_if.if_ibytes += pdulen; +#endif + } + } + } + + /* + * Advance DMA write allowable pointer + */ + eup->eu_rxdmawr = stop; + + /* + * Get packet PDU length + */ + KB_PLENGET ( m, pdulen ); + + /* + * Only try queueing this if there is data + * to be handed up to the next layer. Errors + * such as CRC and VC trashing will get us this + * far to advance pointers, etc., but the PDU + * length will be zero. + */ + if ( pdulen ) { + /* + * We saved three words back in eni_do_service() + * to use for callback. Since the core only + * expects two words, skip over the first one. + * Then, reset up pointer to start of buffer data + * area and write the callback info. + */ + KB_HEADADJ ( m, -sizeof(u_long) ); + KB_DATASTART ( m, up, u_long * ); + *((int *)up) = (int)eni_recv_stack; + up++; + *((int *)up) = (int)evp; + /* + * Schedule callback + */ + if ( !IF_QFULL ( &atm_intrq ) ) { + que++; + IF_ENQUEUE ( &atm_intrq, m ); + } else { + eup->eu_stats.eni_st_drv.drv_rv_intrq++; + eup->eu_pif.pif_ierrors++; +#ifdef DO_LOG + log ( LOG_ERR, +"eni_receive_drain: ATM_INTRQ is full. Unable to pass up stack.\n" ); +#endif + KB_FREEALL ( m ); + } + } else { + /* + * Free zero-length buffer + */ + KB_FREEALL(m); + } + +next_buffer: + /* + * Look for next buffer + */ + IF_DEQUEUE ( &eup->eu_rxqueue, m ); + } +finish: + (void) splx(s); + + /* + * If we found any completed buffers, schedule a call into + * the kernel to process the atm_intrq. + */ + if ( que ) + SCHED_ATM; + + return; + +} + +/* + * Pass incoming PDU up Stack + * + * This function is called via the core ATM interrupt queue callback + * set in eni_recv_drain(). It will pass the supplied incoming + * PDU up the incoming VCC's stack. + * + * Arguments: + * tok token to identify stack instantiation + * m pointer to incoming PDU buffer chain + * + * Returns: + * none + */ +static void +eni_recv_stack ( tok, m ) + void *tok; + KBuffer *m; +{ + Eni_vcc *evp = (Eni_vcc *)tok; + int err; + + /* + * This should never happen now but if it does and we don't stop it, + * we end up panic'ing in netatm when trying to pull a function + * pointer and token value out of a buffer with address zero. + */ + if ( !m ) { +#ifdef DO_LOG + log ( LOG_ERR, + "eni_recv_stack: NULL buffer, tok = 0x%x\n", tok ); +#endif + return; + } + + /* + * Send the data up the stack + */ + STACK_CALL ( CPCS_UNITDATA_SIG, evp->ev_upper, + (void *)evp->ev_toku, evp->ev_connvc, (int)m, 0, err ); + if ( err ) { + KB_FREEALL ( m ); + } + + return; +} + diff --git a/sys/dev/hea/eni_stats.h b/sys/dev/hea/eni_stats.h new file mode 100644 index 0000000..1f2a413 --- /dev/null +++ b/sys/dev/hea/eni_stats.h @@ -0,0 +1,134 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_stats.h,v 1.6 1998/08/26 23:28:54 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Defines for statistics + * + */ + +#ifndef _ENI_ENI_STATS_H +#define _ENI_ENI_STATS_H + +struct eni_stats_oc3 { + u_long oc3_sect_bip8; /* Section 8-bit intrlv parity errors */ + u_long oc3_path_bip8; /* Path 8-bit intrlv parity errors */ + u_long oc3_line_bip24; /* Line 24-bit intrlv parity errors */ + u_long oc3_line_febe; /* Line far-end block errors */ + u_long oc3_path_febe; /* Path far-end block errors */ + u_long oc3_hec_corr; /* Correctable HEC errors */ + u_long oc3_hec_uncorr; /* Uncorrectable HEC errors */ + u_long oc3_pad; /* Pad to quad-word boundary */ +}; +typedef struct eni_stats_oc3 Eni_Stats_oc3; + +struct eni_stats_atm { + u_long atm_xmit; /* Cells transmitted */ + u_long atm_rcvd; /* Cells received */ + u_long atm_pad[2]; /* Pad to quad-word boundary */ +}; +typedef struct eni_stats_atm Eni_Stats_atm; + +struct eni_stats_aal0 { + u_long aal0_xmit; /* Cells transmitted */ + u_long aal0_rcvd; /* Cells received */ + u_long aal0_drops; /* Cells dropped */ + u_long aal0_pad; /* Pad to quad-word boundary */ +}; +typedef struct eni_stats_aal0 Eni_Stats_aal0; + +struct eni_stats_aal5 { + u_long aal5_xmit; /* Cells transmitted */ + u_long aal5_rcvd; /* Cells received */ + u_long aal5_crc_len; /* Cells with CRC/length errors */ + u_long aal5_drops; /* Cell drops */ + u_long aal5_pdu_xmit; /* CS PDUs transmitted */ + u_long aal5_pdu_rcvd; /* CS PDUs received */ + u_long aal5_pdu_crc; /* CS PDUs with CRC errors */ + u_long aal5_pdu_errs; /* CS layer protocol errors */ + u_long aal5_pdu_drops; /* CS PDUs dropped */ + u_long aal5_pad[3]; /* Pad to quad-word boundary */ +}; +typedef struct eni_stats_aal5 Eni_Stats_aal5; + +struct eni_stats_driver { + /* + * Adapter memory allocator stats + */ + u_long drv_mm_toobig; /* Size larger then adapter supports */ + u_long drv_mm_nodesc; /* No memory area descriptor avail */ + u_long drv_mm_nobuf; /* No memory buffer available */ + u_long drv_mm_notuse; /* Calling free() on free buffer */ + u_long drv_mm_notfnd; /* Couldn't find descr for free() */ + + /* + * VCM sats + */ + u_long drv_vc_maxpdu; /* Requested PDU size too large */ + u_long drv_vc_badrng; /* VPI and/or VCI too large */ + + /* + * Receive stats + */ + u_long drv_rv_norsc; /* No buffer for resource pointers */ + u_long drv_rv_nobufs; /* No buffers for PDU */ + u_long drv_rv_nodma; /* No room in RXDMA list */ + u_long drv_rv_rxq; /* No room in local rxqueue */ + u_long drv_rv_novcc; /* Draining PDU on closed VCC */ + u_long drv_rv_intrq; /* No room in atm_intrq */ + u_long drv_rv_null; /* Trying to pass null PDU up stack */ + u_long drv_rv_segdma; /* No DMA address */ + + /* + * Transmit stats + */ + u_long drv_xm_segdma; /* No DMA address */ + u_long drv_xm_segnoal; /* Non-aligned segment */ + u_long drv_xm_seglen; /* Padded length segment */ + u_long drv_xm_maxpdu; /* Too many segments - dropped */ + u_long drv_xm_nobuf; /* No space in TX buffer - dropped */ + u_long drv_xm_norsc; /* No buffers for resource pointers */ + u_long drv_xm_nodma; /* No space in TXDMA list */ + u_long drv_xm_dmaovfl; /* DMA overflow */ + +}; +typedef struct eni_stats_driver Eni_Stats_drv; + +struct eni_stats { + Eni_Stats_oc3 eni_st_oc3; /* OC3 layer stats */ + Eni_Stats_atm eni_st_atm; /* ATM layer stats */ + Eni_Stats_aal0 eni_st_aal0; /* AAL0 layer stats */ + Eni_Stats_aal5 eni_st_aal5; /* AAL5 layer stats */ + Eni_Stats_drv eni_st_drv; /* Driver stats */ +}; +typedef struct eni_stats Eni_stats; + +#endif /* _ENI_ENI_STATS_H */ diff --git a/sys/dev/hea/eni_suni.h b/sys/dev/hea/eni_suni.h new file mode 100644 index 0000000..3a0b0f7 --- /dev/null +++ b/sys/dev/hea/eni_suni.h @@ -0,0 +1,78 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_suni.h,v 1.2 1997/05/06 22:08:17 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Defines for SUNI chip + * + */ + +#ifndef _ENI_ENI_SUNI_H +#define _ENI_ENI_SUNI_H + +/* + * Interrupt bits in SUNI Master Interrupt Status Reg + */ +#define SUNI_RSOPI 0x01 +#define SUNI_RLOPI 0x02 +#define SUNI_RPOPI 0x04 +#define SUNI_RACPI 0x08 +#define SUNI_TACPI 0x10 +#define SUNI_RDOOLI 0x20 +#define SUNI_LCDI 0x40 +#define SUNI_TROOLI 0x80 + +/* + * SUNI Register numbers + */ +#define SUNI_MASTER_REG 0x00 /* Master reset and ID */ +#define SUNI_IS_REG 0x02 /* Master Interrupt Status */ +#define SUNI_CLOCK_REG 0x06 /* Clock synth/control/status */ +#define SUNI_RSOP_REG 0x10 /* RSOP control/Interrupt Status */ +#define SUNI_SECT_BIP_REG 0x12 +#define SUNI_RLOP_REG 0x18 /* RLOP control/Interrupt Status */ +#define SUNI_LINE_BIP_REG 0x1A +#define SUNI_LINE_FEBE_REG 0x1D +#define SUNI_RPOP_IS_REG 0x31 /* RPOP Interrupt Status */ +#define SUNI_PATH_BIP_REG 0x38 +#define SUNI_PATH_FEBE_REG 0x3A +#define SUNI_RACP_REG 0x50 /* RACP control/status */ +#define SUNI_HECS_REG 0x54 +#define SUNI_UHECS_REG 0x55 +#define SUNI_TACP_REG 0x60 /* TACP control/status */ + +/* + * Delay timer to allow SUNI statistic registers to load + */ +#define SUNI_DELAY 10 + +#endif /* _ENI_ENI_SUNI_H */ + diff --git a/sys/dev/hea/eni_transmit.c b/sys/dev/hea/eni_transmit.c new file mode 100644 index 0000000..25066df --- /dev/null +++ b/sys/dev/hea/eni_transmit.c @@ -0,0 +1,823 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_transmit.c,v 1.20 1998/07/17 20:20:16 root Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Transmit queue management and PDU output processing + * + */ + + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_transmit.c,v 1.20 1998/07/17 20:20:16 root Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +/* + * Make a variable which controls printing of PDUs + * as they travel through the driver. + */ +#ifdef DIAGNOSTIC +int eni_pdu_print = 0; +#endif + +/* + * Some PCI chipsets do not handle one or more of the 8WORD or + * 4WORD DMA transfer sizes. Default to using only 1WORD transfer + * sizes unless the user wishes to experiment. + * + * Make sure that these have to be changed here in this module. + */ +#define DMA_USE_8WORD +#define DMA_USE_4WORD + +/* + * Create a DMA list entry + * + * DMA entries consist of a control word and a physical address. + * Control words are comprised of a DMA type, a count of type transfers + * to occur, and a variable which for TX requests is the TX channel + * number and for RX requests is the VCC number. + * + * Arguments: + * eup pointer to unit structure + * rx set if receiving + * dma_list pointer to DMA list structure + * list_size length of DMA list structure + * idx pointer to current list entry + * val TX channel or RX vcc + * addr virtual DMA address of data buffer + * size size in bytes of DMA request to be built + * + * Returns: + * dma_list updated with new entries + * idx points to next list entry + * -1 no room in DMA list structure or DMA_GET_ADDR failed + */ +int +eni_set_dma ( eup, rx, dma_list, list_size, idx, val, addr, size ) +Eni_unit *eup; +u_long *dma_list; +int list_size; +long *idx; +int val; +u_long addr; +int size; +{ + int dsize; /* Size of current DMA request */ + + /* + * Round up to multiple of word and convert to number + * of words rather then number of bytes. + */ + size = ( size + 3 ) >> 2; + +#ifdef DMA_USE_8WORD + /* + * Check for room in DMA list - we need two entires + */ + if ( *idx + 2 >= list_size ) + return ( -1 ); + + /* + * Here is the big win. Move as much data possible with + * n 8WORD DMAs. + */ + /* + * Check if we can do one or more 8WORD DMAs + */ + dsize = size & ~7; + if ( dsize ) { + dma_list[(*idx)++] = ( dsize >> 3 ) << DMA_COUNT_SHIFT | + val << DMA_VCC_SHIFT | DMA_8WORD; + dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, dsize, 0, 0 ); + if ( dma_list[*idx] == NULL ) { + if ( rx ) + eup->eu_stats.eni_st_drv.drv_rv_segdma++; + else + eup->eu_stats.eni_st_drv.drv_xm_segdma++; + return ( -1 ); /* DMA_GET_ADDR failed */ + } else + (*idx)++; /* increment index */ + /* + * Adjust addr and size + */ + addr += dsize << 2; + size &= 7; + } +#endif /* DMA_USE_8WORD */ + +#ifdef DMA_USE_4WORD + /* + * Check for room in DMA list - we need two entries + */ + if ( *idx + 2 >= list_size ) + return ( -1 ); + + /* + * Kindof a tossup from this point on. Since we hacked as many + * 8WORD DMAs off as possible, we are left with 0-7 words + * of remaining data. We could do upto one 4WORD with 0-3 + * words left, or upto three 2WORDS with 0-1 words left, + * or upto seven WORDS with nothing left. Someday we should + * experiment with performance and see if any particular + * combination is a better win then some other... + */ + /* + * Check if we can do one or more 4WORD DMAs + */ + dsize = size & ~3; + if ( dsize ) { + dma_list[(*idx)++] = ( dsize >> 2 ) << DMA_COUNT_SHIFT | + val << DMA_VCC_SHIFT | DMA_4WORD; + dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, dsize, 0, 0 ); + if ( dma_list[*idx] == NULL ) { + if ( rx ) + eup->eu_stats.eni_st_drv.drv_rv_segdma++; + else + eup->eu_stats.eni_st_drv.drv_xm_segdma++; + return ( -1 ); /* DMA_GET_ADDR failed */ + } else + (*idx)++; /* increment index */ + /* + * Adjust addr and size + */ + addr += dsize << 2; + size &= 3; + } +#endif /* DMA_USE_4WORD */ + + /* + * Check for room in DMA list - we need two entries + */ + if ( *idx + 2 >= list_size ) + return ( -1 ); + + /* + * Hard to know if one 2WORD and 0/1 WORD DMA would be better + * then 2/3 WORD DMAs. For now, skip 2WORD DMAs in favor of + * WORD DMAs. + */ + + /* + * Finish remaining size a 1WORD DMAs + */ + if ( size ) { + dma_list[(*idx)++] = ( size ) << DMA_COUNT_SHIFT | + val << DMA_VCC_SHIFT | DMA_WORD; + dma_list[*idx] = (u_long)DMA_GET_ADDR ( addr, size, 0, 0 ); + if ( dma_list[*idx] == NULL ) { + if ( rx ) + eup->eu_stats.eni_st_drv.drv_rv_segdma++; + else + eup->eu_stats.eni_st_drv.drv_xm_segdma++; + return ( -1 ); /* DMA_GET_ADDR failed */ + } else + (*idx)++; /* increment index */ + } + + /* + * Inserted descriptor okay + */ + return 0; +} + +/* + * Drain Transmit queue + * + * As PDUs are given to the adapter to be transmitted, we + * place them into a private ifqueue so that we can free + * any resources AFTER we know they've been successfully DMAed. + * As part of the output processing, we record the PDUs start + * and stop entries in the DMA list, and prevent wrapping. When + * we pull the top element off, we simply check that the current + * DMA location is outside this PDU and if so, it's okay to free + * things. + * + * PDUs are always in ascending order in the queue. + * + * Arguments: + * eup pointer to device unit structure + * + * Returns: + * none + * + */ +void +eni_xmit_drain ( eup ) + Eni_unit *eup; +{ + KBuffer *m; + Eni_vcc *evp; + struct vccb *vcp; + u_long pdulen; + u_long start, stop; + u_long dmap; + int s = splimp(); + + /* + * Pull the top element (PDU) off + */ + IF_DEQUEUE ( &eup->eu_txqueue, m ); + /* + * As long as there are valid elements + */ + while ( m ) { + u_long *up; + + /* + * Find start of buffer + */ + KB_DATASTART ( m, up, u_long * ); + + /* + * First word is the VCC for this PDU + */ + /* + * NOTE: There is a potential problem here in that + * if the VCC is closed after this buffer was transmitted + * but before we get here, that while evp is non-null, + * it will not reference a valid vccb. We need to either + * delay closing the VCC until all references are removed + * from the drain stacks, actually go through the drain + * stacks and remove any references, or find someway of + * indicating that this vccb is nolonger usable. + */ + evp = (Eni_vcc *)*up++; + /* + * Second word is the start and stop DMA pointers + */ + start = *up >> 16; + stop = *up++ & 0xffff; + /* + * Find out where the TX engine is at + */ + dmap = eup->eu_midway[MIDWAY_TX_RD]; + /* + * Check to see if TX engine has processed this + * PDU yet. Remember that everything is circular + * and that stop might be less than start numerically. + */ + if ( start > stop ) { + if ( !(dmap >= stop && dmap < start) ) { + /* + * Haven't finished this PDU yet - replace + * it as the head of list. + */ + IF_PREPEND ( &eup->eu_txqueue, m ); + /* + * If this one isn't done, none of the others + * are either. + */ + (void) splx(s); + return; + } + } else { + if ( dmap < stop && dmap >= start ) { + /* + * Haven't finished this PDU yet - replace + * it as the head of list. + */ + IF_PREPEND ( &eup->eu_txqueue, m ); + /* + * If this one isn't done, none of the others + * are either. + */ + (void) splx(s); + return; + } + } + + /* + * Count the PDU stats for this interface + */ + eup->eu_pif.pif_opdus++; + /* + * Third word is PDU length from eni_output(). + */ + pdulen = *up++; + eup->eu_txfirst = (eup->eu_txfirst + *up) & + (eup->eu_txsize - 1); + eup->eu_pif.pif_obytes += pdulen; + + /* + * Now lookup the VCC entry and counts the stats for + * this VC. + */ + if ( evp ) { + vcp = evp->ev_connvc->cvc_vcc; + if ( vcp ) { + vcp->vc_opdus++; + vcp->vc_obytes += pdulen; + /* + * If we also have a network interface, count the PDU + * there also. + */ + if ( vcp->vc_nif ) { + vcp->vc_nif->nif_obytes += pdulen; + vcp->vc_nif->nif_if.if_opackets++; +#if (defined(BSD) && (BSD >= 199103)) + vcp->vc_nif->nif_if.if_obytes += pdulen; +#endif + } + } + } + /* + * Free the buffer chain + */ + KB_FREEALL ( m ); + + /* + * Advance DMA write okay pointer + */ + eup->eu_txdmawr = stop; + + /* + * Look for next completed transmit PDU + */ + IF_DEQUEUE ( &eup->eu_txqueue, m ); + } + /* + * We've drained the queue... + */ + (void) splx(s); + return; +} + +/* + * Output a PDU + * + * This function is called via the common driver code after receiving a + * stack *_DATA* command. The common code has already validated most of + * the request so we just need to check a few more ENI-specific details. + * Then we just build a segmentation structure for the PDU and place the + * address into the DMA_Transmit_queue. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * m pointer to output PDU buffer chain head + * + * Returns: + * none + * + */ +void +eni_output ( cup, cvp, m ) + Cmn_unit *cup; + Cmn_vcc *cvp; + KBuffer *m; +{ + Eni_unit *eup = (Eni_unit *)cup; + Eni_vcc *evp = (Eni_vcc *)cvp; + int s, s2; + int pdulen = 0; + u_long size; + u_long buf_avail; + u_long dma_rd, dma_wr; + u_long dma[TEMP_DMA_SIZE]; + int aal5, i; + long j; + u_long dma_avail; + u_long dma_start; + Eni_mem tx_send; + u_long *up; + KBuffer *m0 = m, *m1, *mprev = NULL; + caddr_t cp, bfr; + u_int len, align; + int compressed = 0; + +#ifdef DIAGNOSTIC + if ( eni_pdu_print ) + atm_dev_pdu_print ( cup, cvp, m, "eni output" ); +#endif + + /* + * Re-entry point for after buffer compression (if needed) + */ +retry: + + /* + * We can avoid traversing the buffer list twice by building + * the middle (minus header and trailer) dma list at the + * same time we massage address and size alignments. Since + * this list remains local until we determine we've enough + * room, we're not going to trash anything by not checking + * sizes, etc. yet. Skip first entry to be used later to skip + * descriptor word. + */ + j = 2; + /* + * Do data positioning for address and length alignment + */ + while ( m ) { + u_long buf_addr; /* For passing addr to eni_set_dma() */ + + /* + * Get rid of any zero length buffers + */ + if ( KB_LEN ( m ) == 0 ) { + if ( mprev ) { + KB_UNLINK ( m, mprev, m1 ); + } else { + KB_UNLINKHEAD ( m, m1 ); + m0 = m1; + } + m = m1; + continue; + } + /* + * Get start of data onto full-word alignment + */ + KB_DATASTART ( m, cp, caddr_t ); + if ( align = ((u_int)cp) & (sizeof(u_long)-1)) { + /* + * Gotta slide the data up + */ + eup->eu_stats.eni_st_drv.drv_xm_segnoal; + bfr = cp - align; + KM_COPY ( cp, bfr, KB_LEN ( m ) ); + KB_HEADMOVE ( m, -align ); + } else { + /* + * Data already aligned + */ + bfr = cp; + } + /* + * Now work on getting the data length correct + */ + len = KB_LEN ( m ); + while ( ( align = ( len & (sizeof(u_long)-1))) && + (m1 = KB_NEXT ( m ) ) ) { + + /* + * Have to move some data from following buffer(s) + * to word-fill this buffer + */ + u_int ncopy = MIN ( sizeof(u_long) - align, + KB_LEN ( m1 ) ); + + if ( ncopy ) { + /* + * Move data to current buffer + */ + caddr_t dest; + + eup->eu_stats.eni_st_drv.drv_xm_seglen++; + KB_DATASTART ( m1, cp, caddr_t ); + dest = bfr + len; + KB_HEADADJ ( m1, -ncopy ); + KB_TAILADJ ( m, ncopy ); + len += ncopy; + while ( ncopy-- ) { + *dest++ = *cp++; + } + } + + /* + * If we've drained the buffer, free it + */ + if ( KB_LEN ( m1 ) == 0 ) { + KBuffer *m2; + + KB_UNLINK ( m1, m, m2 ); + } + } + + /* + * Address and size are now aligned. Build dma list + * using TX channel 0. Also, round length up to a word + * size which should only effect the last buffer in the + * chain. This works because the PDU length is maintained + * seperately and we're not really adjusting the buffer's + * idea of its length. + */ + KB_DATASTART ( m, buf_addr, u_long ); + if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0, + buf_addr, KB_LEN ( m ) ) < 0 ) { + /* + * Failed to build DMA list. First, we'll try to + * compress the buffer chain into a smaller number + * of buffers. After compressing, we'll try to send + * the new buffer chain. If we still fail, then + * we'll drop the pdu. + */ + if ( compressed ) { +#ifdef DO_LOG + log ( LOG_ERR, + "eni_output: eni_set_dma failed\n" ); +#endif + eup->eu_pif.pif_oerrors++; + KB_FREEALL ( m0 ); + return; + } + + eup->eu_stats.eni_st_drv.drv_xm_maxpdu++; + + m = atm_dev_compress ( m0 ); + if ( m == NULL ) { +#ifdef DO_LOG + log ( LOG_ERR, + "eni_output: atm_dev_compress() failed\n" ); +#endif + eup->eu_pif.pif_oerrors++; + return; + } + + /* + * Reset to new head of buffer chain + */ + m0 = m; + + /* + * Indicate we've been through here + */ + compressed = 1; + + /* + * Retry to build the DMA descriptors for the newly + * compressed buffer chain + */ + goto retry; + + } + + /* + * Now count the length + */ + pdulen += KB_LEN ( m ); + + /* + * Bump counters and get ready for next buffer + */ + mprev = m; + m = KB_NEXT ( m ); + } + + /* + * Get a buffer to use in a private queue so that we can + * reclaim resources after the DMA has finished. + */ + KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); + if ( m ) { + /* + * Link the PDU onto our new head + */ + KB_NEXT ( m ) = m0; + } else { + /* + * Drop this PDU and let the sender try again. + */ + eup->eu_stats.eni_st_drv.drv_xm_norsc++; +#ifdef DO_LOG + log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n"); +#endif + eup->eu_pif.pif_oerrors++; + KB_FREEALL ( m0 ); + return; + } + + s = splnet(); + + /* + * Calculate size of buffer necessary to store PDU. If this + * is an AAL5 PDU, we'll need to know where to stuff the length + * value in the trailer. + */ + /* + * AAL5 PDUs need an extra two words for control/length and + * CRC. Check for AAL5 and add requirements here. + */ + if (aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) + size = pdulen + 2 * sizeof(long); + else + size = pdulen; + /* + * Pad to next complete cell boundary + */ + size += (BYTES_PER_CELL - 1); + size -= size % BYTES_PER_CELL; + /* + * Convert size to words and add 2 words overhead for every + * PDU (descriptor and cell header). + */ + size = (size >> 2) + 2; + + /* + * First, check to see if there's enough buffer space to + * store the PDU. We do this by checking to see if the size + * required crosses the eu_txfirst pointer. However, we don't + * want to exactly fill the buffer, because we won't be able to + * distinguish between a full and empty buffer. + */ + if ( eup->eu_txpos == eup->eu_txfirst ) + buf_avail = eup->eu_txsize; + else + if ( eup->eu_txpos > eup->eu_txfirst ) + buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst ); + else + buf_avail = eup->eu_txfirst - eup->eu_txpos; + + if ( size >= buf_avail ) + { + /* + * No buffer space in the adapter to store this PDU. + * Drop PDU and return. + */ + eup->eu_stats.eni_st_drv.drv_xm_nobuf++; +#ifdef DO_LOG + log ( LOG_ERR, + "eni_output: not enough room in buffer\n" ); +#endif + eup->eu_pif.pif_oerrors++; + KB_FREEALL ( m ); + (void) splx(s); + return; + } + + /* + * Find out where current DMA pointers are at + */ + dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR]; + dma_rd = eup->eu_midway[MIDWAY_TX_RD]; + + /* + * Figure out how much DMA room we have available + */ + if ( dma_rd == dma_wr ) { /* Queue is empty */ + dma_avail = DMA_LIST_SIZE; + } else { + dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr ) + & ( DMA_LIST_SIZE - 1 ); + } + /* + * Check to see if we can describe this PDU or if we're: + * out of room, will wrap past recovered resources. + */ + if ( dma_avail < (j / 2 + 4) || + ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) { + /* + * No space to insert DMA list into queue. Drop this PDU. + */ + eup->eu_stats.eni_st_drv.drv_xm_nodma++; +#ifdef DO_LOG + log ( LOG_ERR, + "eni_output: not enough room in DMA queue\n" ); +#endif + eup->eu_pif.pif_oerrors++; + KB_FREEALL( m ); + (void) splx(s); + return; + } + + /* + * Create DMA descriptor for header. There is a descriptor word + * and also a cell header word which we'll set manually. + */ + dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) << + DMA_COUNT_SHIFT) | DMA_JK; + dma[1] = 0; + + /* + * JK for AAL5 trailer. Set END bit as well. + */ + if ( aal5 ) { + dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) << + DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK; + dma[j++] = 0; + } else { + dma[j-2] |= DMA_END_BIT; /* Backup and set END bit */ + } + + /* + * Find out where in adapter memory this TX buffer starts. + */ + tx_send = (Eni_mem) + ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) + + (int)eup->eu_ram); + + /* + * Set descriptor word + */ + tx_send[eup->eu_txpos] = + (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0) + | (size / WORDS_PER_CELL); + /* + * Set cell header + */ + tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] = + evp->ev_connvc->cvc_vcc->vc_vci << 4; + + /* + * We've got all our resources, count the stats + */ + if ( aal5 ) { + /* + * If this is an AAL5 PDU, we need to set the length + */ + tx_send[(eup->eu_txpos+size-2) & + (eup->eu_txsize-1)] = pdulen; + /* + * Increment AAL5 stats + */ + eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++; + eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL; + } else { + /* + * Increment AAL0 stats + */ + eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL; + } + /* + * Increment ATM stats + */ + eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL; + + /* + * Store the DMA list + */ + j = j >> 1; + for ( i = 0; i < j; i++ ) { + eup->eu_txdma[dma_wr*2] = dma[i*2]; + eup->eu_txdma[dma_wr*2+1] = dma[i*2+1]; + dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1); + } + + /* + * Build drain buffer + * + * We toss four words in to help keep track of this + * PDU. The first is a pointer to the VC control block + * so we can find which VCI this went out on, the second + * is the start and stop pointers for the DMA list which + * describes this PDU, the third is the PDU length + * since we'll want to know that for stats gathering, + * and the fourth is the number of DMA words. + */ + KB_DATASTART ( m, up, u_long * ); + *up++ = (u_long)cvp; + *up++ = dma_start << 16 | dma_wr; + *up++ = pdulen; + *up = size; + + /* + * Set length of our buffer + */ + KB_LEN ( m ) = 4 * sizeof ( long ); + + /* + * Place buffers onto transmit queue for draining + */ + s2 = splimp(); + IF_ENQUEUE ( &eup->eu_txqueue, m ); + (void) splx(s2); + + /* + * Update next word to be stored + */ + eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1)); + + /* + * Update MIDWAY_TX_WR pointer + */ + eup->eu_midway[MIDWAY_TX_WR] = dma_wr; + + (void) splx ( s ); + + return; +} + diff --git a/sys/dev/hea/eni_var.h b/sys/dev/hea/eni_var.h new file mode 100644 index 0000000..cc83a12 --- /dev/null +++ b/sys/dev/hea/eni_var.h @@ -0,0 +1,85 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_var.h,v 1.4 1998/06/29 19:55:26 jpt Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Local driver include files and global declarations + * + */ + +#ifndef _ENI_ENI_VAR_H +#define _ENI_ENI_VAR_H + +/* + * Global function declarations + */ + /* eni_buffer.c */ +int eni_init_memory __P((Eni_unit *)); +caddr_t eni_allocate_buffer __P((Eni_unit *, u_long *)); +void eni_free_buffer __P((Eni_unit *, caddr_t)); + + /* eni_if.c */ +int eni_atm_ioctl __P((int, caddr_t, caddr_t)); +void eni_zero_stats __P((Eni_unit *)); + + /* eni_init.c */ +int eni_init __P((Eni_unit *)); + + /* eni_intr.c */ +#if defined(BSD) && BSD < 199506 +int eni_intr __P((void *)); +#else +void eni_intr __P((void *)); +#endif + + /* eni_receive.c */ +void eni_do_service __P((Eni_unit *)); +void eni_recv_drain __P((Eni_unit *)); + + /* eni_transmit.c */ +int eni_set_dma __P((Eni_unit *, int, u_long *, int, long *, int, u_long, int )); +void eni_output __P((Cmn_unit *, Cmn_vcc *, KBuffer *)); +void eni_xmit_drain __P((Eni_unit *)); + + /* eni_vcm.c */ +int eni_instvcc __P((Cmn_unit *, Cmn_vcc *)); +int eni_openvcc __P((Cmn_unit *, Cmn_vcc *)); +int eni_closevcc __P((Cmn_unit *, Cmn_vcc *)); + +/* + * Global variable declarations + */ +extern Eni_unit *eni_units[]; +extern struct stack_defn *eni_services; +extern struct sp_info eni_nif_pool; +extern struct sp_info eni_vcc_pool; + +#endif /* _ENI_ENI_VAR_H */ diff --git a/sys/dev/hea/eni_vcm.c b/sys/dev/hea/eni_vcm.c new file mode 100644 index 0000000..36c2a3d --- /dev/null +++ b/sys/dev/hea/eni_vcm.c @@ -0,0 +1,283 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: eni_vcm.c,v 1.8 1998/06/29 23:03:18 mks Exp $ + * + */ + +/* + * Efficient ENI Adapter Support + * ----------------------------- + * + * Virtual Channel Managment + * + */ + + +#ifndef lint +static char *RCSid = "@(#) $Id: eni_vcm.c,v 1.8 1998/06/29 23:03:18 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + + +/* + * VCC Stack Instantiation + * + * This function is called via the common driver code during a device VCC + * stack instantiation. The common code has already validated some of + * the request so we just need to check a few more ENI-specific details. + * + * Called at splnet. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 instantiation successful + * err instantiation failed - reason indicated + * + */ +int +eni_instvcc(cup, cvp) + Cmn_unit *cup; + Cmn_vcc *cvp; +{ + Eni_unit *eup = (Eni_unit *)cup; + Eni_vcc *evp = (Eni_vcc *)cvp; + Atm_attributes *ap = &evp->ev_connvc->cvc_attr; + + /* + * Validate requested AAL + */ + switch (ap->aal.type) { + + case ATM_AAL0: + break; + + case ATM_AAL5: + if ((ap->aal.v.aal5.forward_max_SDU_size > ENI_IFF_MTU) || + (ap->aal.v.aal5.backward_max_SDU_size > ENI_IFF_MTU)) { + eup->eu_stats.eni_st_drv.drv_vc_maxpdu++; + return (EINVAL); + } + break; + + default: + return (EINVAL); + } + + return (0); +} + + +/* + * Open a VCC + * + * This function is called via the common driver code after receiving a + * stack *_INIT* command. The common code has already validated most of + * the request so we just need to check a few more ENI-specific details. + * + * Called at splimp. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 open sucessful + * err open failed + * + */ +int +eni_openvcc ( cup, cvp ) + Cmn_unit *cup; + Cmn_vcc *cvp; +{ + Eni_unit *eup = (Eni_unit *)cup; + Eni_vcc *evp = (Eni_vcc *)cvp; + struct vccb *vcp = evp->ev_connvc->cvc_vcc; + int err = 0; + + VCI_Table *vct; + int size; + int mode; + int nsize; + + /* + * Validate the VPI and VCI values + */ + if ( (vcp->vc_vpi > eup->eu_pif.pif_maxvpi) || + (vcp->vc_vci > eup->eu_pif.pif_maxvci) ) { + eup->eu_stats.eni_st_drv.drv_vc_badrng++; + return ( EFAULT ); + } + + /* + * Check if this VCI is already active + */ + vct = &eup->eu_vcitbl[ vcp->vc_vci ]; + if ( vct->vci_control >> VCI_MODE_SHIFT != VCI_MODE_TRASH ) { + return ( EEXIST ); + } + + /* + * Allocate some permanent adapter memory for the reassembly + * buffer. Special case the signalling channel(s) buffer size. + * Otherwise, the buffer size will be based on whether this is + * a server or client card. + */ + if ( vcp->vc_vci == UNI_SIG_VCI ) /* HACK */ + size = RX_SIG_BSIZE; + else + size = (eup->eu_ramsize > MAX_CLIENT_RAM * ENI_BUF_PGSZ) ? + RX_SERVER_BSIZE * ENI_BUF_PGSZ : + RX_CLIENT_BSIZE * ENI_BUF_PGSZ; + + if ( ( evp->ev_rxbuf = eni_allocate_buffer ( eup, (u_long *)&size ) ) + == (caddr_t)NULL ) { + return ( ENOMEM ); + } + evp->ev_rxpos = 0; + + /* + * We only need to open incoming VCI's so outbound VCI's + * just get set to CVS_ACTIVE state. + */ + if ( ( vcp->vc_type & VCC_IN ) == 0 ) { + /* + * Set the state and return - nothing else needs to be done. + */ + evp->ev_state = CVS_ACTIVE; + return ( 0 ); + } + + /* + * Set the VCI Table entry to start receiving + */ + mode = ( evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5 + ? VCI_MODE_AAL5 : VCI_MODE_AAL0 ); + size >>= ENI_LOC_PREDIV; /* Predivide by 256 WORDS */ + for ( nsize = -1; size; nsize++ ) + size >>= 1; + + vct->vci_control = mode << VCI_MODE_SHIFT | + PTI_MODE_TRASH << VCI_PTI_SHIFT | + ( (u_int)(evp->ev_rxbuf) >> ENI_LOC_PREDIV ) << VCI_LOC_SHIFT | + nsize << VCI_SIZE_SHIFT; + vct->vci_descr = 0; /* Descr = Rdptr = 0 */ + vct->vci_write = 0; /* WritePtr = CellCount = 0 */ + + /* + * Indicate VC active + */ + evp->ev_state = CVS_ACTIVE; + + return ( err ); +} + +/* + * Close a VCC + * + * This function is called via the common driver code after receiving a + * stack *_TERM* command. The common code has already validated most of + * the request so we just need to check a few more ENI-specific details. + * + * Called at splimp. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 close sucessful + * err close failed + * + */ +int +eni_closevcc ( cup, cvp ) + Cmn_unit *cup; + Cmn_vcc *cvp; +{ + Eni_unit *eup = (Eni_unit *)cup; + Eni_vcc *evp = (Eni_vcc *)cvp; + struct vccb *vcp = evp->ev_connvc->cvc_vcc; + int err = 0; + + VCI_Table *vct; + + /* + * Clear any references to this VCC in our transmit queue + */ + /* + * We'll simply allow any existing TX requests to be + * sent as that's easier then pulling them out of + * everywhere. Besides, they should be ignored at the + * receiver whenever the other end shuts down. + */ + + /* + * Free the adapter receive buffer + */ + (void) eni_free_buffer ( eup, (caddr_t)evp->ev_rxbuf ); + + /* + * If this is an outbound only VCI, then we can close + * immediately. + */ + if ( ( vcp->vc_type & VCC_IN ) == 0 ) { + /* + * The state will be set to TERM when we return + * to the *_TERM caller. + */ + return ( 0 ); + } + + /* + * Find VCI entry in VCI Table + */ + vct = &eup->eu_vcitbl[ vcp->vc_vci ]; + + /* + * Reset the VCI state + */ + vct->vci_control = ( vct->vci_control & VCI_MODE_MASK ) + /* | VCI_MODE_TRASH */; + DELAY ( MIDWAY_DELAY ); /* Give the adapter time to */ + /* make the transition */ + + /* + * Reset everything + */ + KM_ZERO ( (caddr_t)vct, sizeof(VCI_Table) ); + + return ( err ); +} + diff --git a/sys/dev/hfa/fore.h b/sys/dev/hfa/fore.h new file mode 100644 index 0000000..42f44f0 --- /dev/null +++ b/sys/dev/hfa/fore.h @@ -0,0 +1,144 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore.h,v 1.8 1998/08/26 23:28:57 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Protocol and implementation definitions + * + */ + +#ifndef _FORE_H +#define _FORE_H + +#ifndef FORE_DEV_NAME +#define FORE_DEV_NAME "hfa" +#endif + +#define FORE_MAX_UNITS 8 /* Maximum number of devices we support */ +#define FORE_MIN_UCODE 0x20300 /* Minimum microcode version we support */ + +#define FORE_IFF_MTU 9188 /* Network interface MTU */ +#define FORE_MAX_VCC 1024 /* Maximum number of open VCCs */ +#define FORE_MAX_VPI 0 /* Maximum VPI value */ +#define FORE_MAX_VCI 1023 /* Maximum VCI value */ +#define FORE_DEF_RATE 0x00000000 /* Default rate control = disabled */ + +#define XMIT_QUELEN 32 /* Length of transmit queue */ +#define RECV_QUELEN 32 /* Length of receive queue */ +#define CMD_QUELEN 8 /* Length of command queue */ + +#define FORE_TIME_TICK 5 /* Watchdog timer tick (seconds) */ +#define FORE_WATCHDOG 3 /* Device watchdog timeout (ticks) */ +#define FORE_RECV_RETRY 3 /* Wait for receive queue entry retry count */ +#define FORE_RECV_DELAY 10 /* Wait for receive queue entry delay (usec) */ + + +/* + * Receive Buffer strategies + */ +#define BUF_MIN_VCC 4 /* Minimum for buffer supply calculations */ + +#ifdef FORE_SBUS +#if defined(sun4c) +#define BUF_DATA_ALIGN 32 /* Fore-required data alignment */ +#elif defined(sun4m) +#define BUF_DATA_ALIGN 64 /* Fore-required data alignment */ +#endif +#endif +#ifdef FORE_PCI +#define BUF_DATA_ALIGN 4 /* Fore-required data alignment */ +#endif + +#if defined(BSD) +/* + * Strategy 1 Small - mbuf + * Strategy 1 Large - cluster mbuf + * + * XXX buffer controls - the RECV_MAX_SEGS calculation comes out wrong + * using the true buffer size values if the CP really only does full-cell + * filling of a particular buffer - we must clarify this...it also appears + * the minimum buffer size is 64, even if the CP can only fit in 1 cell. + */ +#define SIZEOF_Buf_handle 16 /* XXX sizeof(Buf_handle) */ + +#if BSD >= 199103 +#undef m_ext +typedef struct m_ext M_ext; +#define m_ext M_dat.MH.MH_dat.MH_ext +#define BUF1_SM_HOFF (sizeof(struct m_hdr)) /* Buffer-to-handle offset */ +#define BUF1_SM_HDR (sizeof(struct m_hdr) + sizeof(struct pkthdr)) +#define BUF1_SM_LEN (MHLEN) +#define BUF1_LG_HOFF (sizeof(struct m_hdr) + sizeof(struct pkthdr) \ + + sizeof(M_ext)) /* Buffer-to-handle offset */ +#else +#define BUF1_SM_HOFF (MMINOFF) /* Buffer-to-handle offset */ +#define BUF1_SM_HDR (MMINOFF) +#define BUF1_SM_LEN (MLEN) +#define BUF1_LG_HOFF (MMINOFF + 16) /* Buffer-to-handle offset */ +#endif + +/* + * BUF1_SM_DOFF - CP data offset into buffer data space + * BUF1_SM_SIZE - Buffer size + * + * These should be defined as follows, but we need compile-time constants: + * + * #define BUF1_SM_DOFF (roundup(BUF1_SM_HOFF + SIZEOF_Buf_handle, + * BUF_DATA_ALIGN) - BUF1_SM_HDR) + * #define BUF1_SM_SIZE MAX(BUF1_SM_LEN - BUF1_SM_DOFF, 64) + * + */ +#if ((BSD >= 199103) && defined(FORE_PCI)) +#define BUF1_SM_DOFF ((BUF1_SM_HOFF + SIZEOF_Buf_handle) - BUF1_SM_HDR) +#define BUF1_SM_SIZE (BUF1_SM_LEN - BUF1_SM_DOFF) +#endif +#if ((BSD < 199103) && defined(FORE_SBUS) && defined(sun4c)) +#define BUF1_SM_DOFF (BUF_DATA_ALIGN - BUF1_SM_HDR) +#define BUF1_SM_SIZE (BUF1_SM_LEN - BUF1_SM_DOFF) +#endif +#if ((BSD < 199103) && defined(FORE_SBUS) && defined(sun4m)) +#define BUF1_SM_DOFF (BUF_DATA_ALIGN - BUF1_SM_HDR) +#define BUF1_SM_SIZE (64) +#endif + +#define BUF1_SM_QUELEN 16 /* Entries in supply queue */ +#define BUF1_SM_CPPOOL 256 /* Buffers in CP-resident pool */ +#define BUF1_SM_ENTSIZE 8 /* Buffers in each supply queue entry */ + +#define BUF1_LG_DOFF 0 /* CP data offset into mbuf data space */ +#define BUF1_LG_SIZE MCLBYTES /* Buffer size */ +#define BUF1_LG_QUELEN 16 /* Entries in supply queue */ +#define BUF1_LG_CPPOOL 512 /* Buffers in CP-resident pool */ +#define BUF1_LG_ENTSIZE 8 /* Buffers in each supply queue entry */ + +#endif /* defined(BSD) */ + +#endif /* _FORE_H */ diff --git a/sys/dev/hfa/fore_aali.h b/sys/dev/hfa/fore_aali.h new file mode 100644 index 0000000..d59dcfc --- /dev/null +++ b/sys/dev/hfa/fore_aali.h @@ -0,0 +1,604 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_aali.h,v 1.5 1997/05/09 00:42:25 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * ATM Adaptation Layer Interface (AALI) definitions + * + */ + +#ifndef _FORE_AALI_H +#define _FORE_AALI_H + +/* + * This file contains the definitions required by the FORE ATM Adaptation + * Layer Interface (AALI) specification. + */ + + +/* + * Addressing/Pointer definitions + * + * The CP memory only supports 32-bit word accesses (read and write) - thus, + * all memory must be defined and accessed as 32-bit words. Also, since the + * data transfers are word-sized, we must take care of byte-swapping issues + * from/to little-endian hosts (the CP is an i960 processor, ie big-endian). + * + * All pointers to CP memory areas are actually offsets from the start of + * the adapter RAM address space. + * + * All CP-resident data structures are declared volatile. + */ +typedef void * H_addr; /* Host-resident address */ +typedef unsigned long H_dma; /* Host-resident DMA address */ +typedef unsigned long CP_word; /* CP-resident word */ +typedef unsigned long CP_addr; /* CP-resident CP memory offset */ +typedef unsigned long CP_dma; /* CP-resident DMA address */ + + +/* + * Structure defining the CP's shared memory interface to the mon960 program + */ +struct mon960 { + CP_word mon_xmitmon; /* Uart - host to mon960 (see below) */ + CP_word mon_xmithost; /* Uart - mon960 to host (see below) */ + CP_word mon_bstat; /* Boot status word (see below) */ + CP_addr mon_appl; /* Pointer to application memory area */ + CP_word mon_ver; /* Mon960 firmware version */ +}; +typedef volatile struct mon960 Mon960; + +/* + * Pseudo-UART usage + */ +#define UART_READY 0x00000000 /* UART is ready for more data */ +#define UART_VALID 0x01000000 /* UART character is valid */ +#define UART_DATAMASK 0x000000ff /* UART character data mask */ + +/* + * Boot Status Word + */ +#define BOOT_COLDSTART 0xc01dc01d /* CP is performing cold start */ +#define BOOT_MONREADY 0x02201958 /* Monitor is waiting for commands */ +#define BOOT_FAILTEST 0xadbadbad /* Monitor failed self-test */ +#define BOOT_RUNNING 0xce11feed /* Microcode downloaded and running */ + +#define BOOT_LOOPS 20 /* Loops to wait for CP to boot */ +#define BOOT_DELAY 100000 /* Delay (us) for each boot loop */ + + +/* + * Supported AALs + */ +enum fore_aal { + FORE_AAL_0 = 0, /* Cell Service */ + FORE_AAL_4 = 4, /* AAL 3/4 */ + FORE_AAL_5 = 5 /* AAL 5 */ +}; +typedef enum fore_aal Fore_aal; + + +/* + * Buffer strategy definition + */ +struct buf_strategy { + CP_word bfs_quelen; /* Buffer supply queue entries */ + CP_word bfs_bufsize; /* Buffer size */ + CP_word bfs_cppool; /* Buffers in CP-resident pool */ + CP_word bfs_entsize; /* Buffers in each supply queue entry */ +}; +typedef volatile struct buf_strategy Buf_strategy; + +/* + * Buffer strategy id + */ +#define BUF_STRAT_1 0 /* Buffer strategy one */ +#define BUF_STRAT_2 1 /* Buffer strategy two */ + + + +#ifdef ATM_KERNEL +/* + * Common Queue Element + * + * Used for Transmit, Receive and Buffer Supply Queues + */ +struct com_queue { + CP_dma cq_descr; /* Pointer to element descriptor */ + CP_dma cq_status; /* Pointer to element status word */ +}; +typedef volatile struct com_queue Com_queue; + + +/* + * Queue element status word + */ +typedef volatile unsigned long Q_status; + +#define QSTAT_PENDING 0x01 /* Operation is pending */ +#define QSTAT_COMPLETED 0x02 /* Operation successfully completed */ +#define QSTAT_FREE 0x04 /* Queue element is free/unused */ +#define QSTAT_ERROR 0x08 /* Operation encountered an error */ + +#define QSTAT_ALIGN 4 + + +/* + * PDU Transmit Queue + */ + +/* + * PDU Transmit Queue Element + */ +typedef volatile struct com_queue Xmit_queue; + + +/* + * PDU Transmit buffer segment descriptor + */ +struct xmit_seg_descr { + H_dma xsd_buffer; /* Buffer's DMA address */ + u_int xsd_len; /* Data length in buffer */ +}; +typedef struct xmit_seg_descr Xmit_seg_descr; + +#define XMIT_SEG_ALIGN 4 + + +/* + * PDU Transmit descriptor header + */ +struct xmit_descr_hdr { + u_long xdh_cell_hdr; /* Cell header (minus HEC) */ + u_long xdh_spec; /* Transmit specification (see below) */ + u_long xdh_rate; /* Rate control (data/idle cell ratio)*/ + u_long xdh_pad; /* Pad to quad-word boundary */ +}; +typedef struct xmit_descr_hdr Xmit_descr_hdr; + + +#define XMIT_BLK_BITS 5 /* Bits to encode block size */ +#define XMIT_MAX_BLK_BITS 4 /* Max bits we can use */ +#define XMIT_BLK_SIZE (1 << XMIT_BLK_BITS) +#define XMIT_SEGS_TO_BLKS(nseg) \ + ((((nseg) * sizeof(Xmit_seg_descr)) \ + + sizeof(Xmit_descr_hdr) + (XMIT_BLK_SIZE - 1)) \ + >> XMIT_BLK_BITS) +#define XMIT_MAX_BLKS ((1 << XMIT_MAX_BLK_BITS) - 1) +#define XMIT_HDR_SEGS ((XMIT_BLK_SIZE - sizeof(Xmit_descr_hdr)) \ + / sizeof(Xmit_seg_descr)) +#define XMIT_BLK_SEGS (XMIT_BLK_SIZE / sizeof(Xmit_seg_descr)) +#define XMIT_EXTRA_SEGS ((XMIT_MAX_BLKS - 1) * XMIT_BLK_SEGS) +#define XMIT_MAX_SEGS (XMIT_EXTRA_SEGS + XMIT_HDR_SEGS) + + +/* + * PDU Transmit descriptor + */ +struct xmit_descr { + Xmit_descr_hdr xd_hdr; /* Descriptor header */ + Xmit_seg_descr xd_seg[XMIT_MAX_SEGS]; /* PDU segments */ +}; +typedef struct xmit_descr Xmit_descr; + +#define xd_cell_hdr xd_hdr.xdh_cell_hdr +#define xd_spec xd_hdr.xdh_spec +#define xd_rate xd_hdr.xdh_rate + +/* + * Transmit specification + * + * Bits 0-15 - Total PDU length + * Bits 16-23 - Number of transmit segments + * Bits 24-27 - AAL type + * Bits 28-31 - Interrupt flag + */ +#define XDS_SET_SPEC(i,a,n,l) (((i) << 28) | ((a) << 24) | ((n) << 16) | (l)) +#define XDS_GET_LEN(s) ((s) & 0xffff) +#define XDS_GET_SEGS(s) (((s) >> 16) & 0xff) +#define XDS_GET_AAL(s) (((s) >> 24) & 0xf) +#define XDS_GET_INTR(s) (((s) >> 28) & 0xf) + +#define XMIT_MAX_PDULEN 65535 +#define XMIT_DESCR_ALIGN 32 + + + +/* + * PDU Receive Queue + */ + +/* + * PDU Receive Queue Element + */ +typedef volatile struct com_queue Recv_queue; + + +/* + * Receive PDU buffer segment description + */ +struct recv_seg_descr { + H_addr rsd_handle; /* Buffer handle (from supply) */ + u_int rsd_len; /* Data length in buffer */ +}; +typedef struct recv_seg_descr Recv_seg_descr; + + +/* + * PDU Receive descriptor header + */ +struct recv_descr_hdr { + u_long rdh_cell_hdr; /* Cell header (minus HEC) */ + u_long rdh_nsegs; /* Number of receive segments */ +}; +typedef struct recv_descr_hdr Recv_descr_hdr; + + +#define RECV_BLK_SIZE 32 +#define RECV_HDR_SEGS ((RECV_BLK_SIZE - sizeof(Recv_descr_hdr)) \ + / sizeof(Recv_seg_descr)) +#define RECV_BLK_SEGS (RECV_BLK_SIZE / sizeof(Recv_seg_descr)) +#define RECV_MAX_LG_SEGS ((FORE_IFF_MTU - BUF1_SM_SIZE \ + + (BUF1_LG_SIZE - 1)) / BUF1_LG_SIZE) +#define RECV_EXTRA_BLKS (((RECV_MAX_LG_SEGS + 1 - RECV_HDR_SEGS) \ + + (RECV_BLK_SEGS - 1)) / RECV_BLK_SEGS) +#define RECV_EXTRA_SEGS (RECV_EXTRA_BLKS * RECV_BLK_SEGS) +#define RECV_MAX_SEGS (RECV_EXTRA_SEGS + RECV_HDR_SEGS) + + +/* + * PDU Receive descriptor + */ +struct recv_descr { + Recv_descr_hdr rd_hdr; /* Descriptor header */ + Recv_seg_descr rd_seg[RECV_MAX_SEGS]; /* PDU segments */ +}; +typedef struct recv_descr Recv_descr; + +#define rd_cell_hdr rd_hdr.rdh_cell_hdr +#define rd_nsegs rd_hdr.rdh_nsegs + +#define RECV_DESCR_ALIGN 32 + + + +/* + * Buffer Supply Queue + */ + +/* + * Buffer Supply Queue Element + */ +typedef volatile struct com_queue Buf_queue; + + +/* + * Buffer supply descriptor for supplying receive buffers + */ +struct buf_descr { + H_addr bsd_handle; /* Host-specific buffer handle */ + H_dma bsd_buffer; /* Buffer DMA address */ +}; +typedef struct buf_descr Buf_descr; + +#define BUF_DESCR_ALIGN 32 + + + +/* + * Command Queue + */ + +/* + * Command Codes + */ +typedef volatile unsigned long Cmd_code; + +#define CMD_INIT 0x01 /* Initialize microcode */ +#define CMD_ACT_VCCIN 0x02 /* Activate incoming VCC */ +#define CMD_ACT_VCCOUT 0x03 /* Activate outgoing VCC */ +#define CMD_DACT_VCCIN 0x04 /* Deactivate incoming VCC */ +#define CMD_DACT_VCCOUT 0x05 /* Deactivate outgoing VCC */ +#define CMD_GET_STATS 0x06 /* Get adapter statistics */ +#define CMD_SET_OC3_REG 0x07 /* Set SUNI OC3 registers */ +#define CMD_GET_OC3_REG 0x08 /* Get SUNI OC3 registers */ +#define CMD_GET_PROM 0x09 /* Get PROM data */ +#define CMD_INTR_REQ 0x80 /* Request host interrupt */ + +#endif /* ATM_KERNEL */ + + +/* + * Structure defining the parameters for the Initialize command + */ +struct init_parms { + CP_word init_cmd; /* Command code */ + CP_word init_status; /* Completion status */ + CP_word init_indisc; /* Not used */ + CP_word init_numvcc; /* Number of VCC's supported */ + CP_word init_cmd_elem; /* # of command queue elements */ + CP_word init_xmit_elem; /* # of transmit queue elements */ + CP_word init_recv_elem; /* # of receive queue elements */ + CP_word init_recv_ext; /* # of extra receive descr SEGMENTS */ + CP_word init_xmit_ext; /* # of extra transmit descr SEGMENTS */ + CP_word init_cls_vcc; /* Not used */ + CP_word init_pad[2]; /* Pad to quad-word boundary */ + Buf_strategy init_buf1s; /* Buffer strategy - 1 small */ + Buf_strategy init_buf1l; /* Buffer strategy - 1 large */ + Buf_strategy init_buf2s; /* Buffer strategy - 2 small */ + Buf_strategy init_buf2l; /* Buffer strategy - 2 large */ +}; +typedef volatile struct init_parms Init_parms; + + +#ifdef ATM_KERNEL +/* + * Structure defining the parameters for the Activate commands + */ +struct activate_parms { + CP_word act_spec; /* Command specification (see below) */ + CP_word act_vccid; /* VCC id (VPI=0,VCI=id) */ + CP_word act_batch; /* # cells in batch (AAL=NULL) */ + CP_word act_pad; /* Pad to quad-word boundary */ +}; +typedef volatile struct activate_parms Activate_parms; + +/* + * Activate command specification + * + * Bits 0-7 - command code + * Bits 8-15 - AAL type + * Bits 16-23 - buffer strategy + * Bits 24-31 - reserved + */ +#define ACT_SET_SPEC(b,a,c) (((b) << 16) | ((a) << 8) | (c)) +#define ACT_GET_CMD(s) ((s) & 0xff) +#define ACT_GET_AAL(s) (((s) >> 8) & 0xff) +#define ACT_GET_STRAT(s) (((s) >> 16) & 0xff) + + +/* + * Structure defining the parameters for the Deactivate commands + */ +struct dactivate_parms { + CP_word dact_cmd; /* Command code */ + CP_word dact_vccid; /* VCC id (VPI=0,VCI=id) */ + CP_word dact_pad[2]; /* Pad to quad-word boundary */ +}; +typedef volatile struct dactivate_parms Dactivate_parms; + + +/* + * Structure defining the parameters for the Get Statistics command + */ +struct stats_parms { + CP_word stats_cmd; /* Command code */ + CP_dma stats_buffer; /* DMA address of host stats buffer */ + CP_word stats_pad[2]; /* Pad to quad-word boundary */ +}; +typedef volatile struct stats_parms Stats_parms; + + +/* + * Structure defining the parameters for the SUNI OC3 commands + */ +struct suni_parms { + CP_word suni_spec; /* Command specification (see below) */ + CP_dma suni_buffer; /* DMA address of host SUNI buffer */ + CP_word suni_pad[2]; /* Pad to quad-word boundary */ +}; +typedef volatile struct suni_parms Suni_parms; + +/* + * SUNI OC3 command specification + * + * Bits 0-7 - command code + * Bits 8-15 - SUNI register number + * Bits 16-23 - Value(s) to set in register + * Bits 24-31 - Mask selecting value bits + */ +#define SUNI_SET_SPEC(m,v,r,c) (((m) << 24) | ((v) << 16) | ((r) << 8) | (c)) +#define SUNI_GET_CMD(s) ((s) & 0xff) +#define SUNI_GET_REG(s) (((s) >> 8) & 0xff) +#define SUNI_GET_VALUE(s) (((s) >> 16) & 0xff) +#define SUNI_GET_MASK(s) (((s) >> 24) & 0xff) + + +/* + * Structure defining the parameters for the Get Prom command + */ +struct prom_parms { + CP_word prom_cmd; /* Command code */ + CP_dma prom_buffer; /* DMA address of host prom buffer */ + CP_word prom_pad[2]; /* Pad to quad-word boundary */ +}; +typedef volatile struct prom_parms Prom_parms; + + +/* + * Command Queue Element + */ +struct cmd_queue { + union { /* Command-specific parameters */ + Activate_parms cmdqu_act; + Dactivate_parms cmdqu_dact; + Stats_parms cmdqu_stats; + Suni_parms cmdqu_suni; + Prom_parms cmdqu_prom; + } cmdq_u; + CP_dma cmdq_status; /* Pointer to element status word */ + CP_word cmdq_pad[3]; /* Pad to quad-word boundary */ +}; +#define cmdq_act cmdq_u.cmdqu_act +#define cmdq_dact cmdq_u.cmdqu_dact +#define cmdq_stats cmdq_u.cmdqu_stats +#define cmdq_suni cmdq_u.cmdqu_suni +#define cmdq_prom cmdq_u.cmdqu_prom +typedef volatile struct cmd_queue Cmd_queue; + +#endif /* ATM_KERNEL */ + + + +/* + * Structure defining the CP's shared memory interface to the + * AALI firmware program (downloaded microcode) + */ +struct aali { + CP_addr aali_cmd_q; /* Pointer to command queue */ + CP_addr aali_xmit_q; /* Pointer to transmit queue */ + CP_addr aali_recv_q; /* Pointer to receive queue */ + CP_addr aali_buf1s_q; /* Pointer to strategy-1 small queue */ + CP_addr aali_buf1l_q; /* Pointer to strategy-1 large queue */ + CP_addr aali_buf2s_q; /* Pointer to strategy-2 small queue */ + CP_addr aali_buf2l_q; /* Pointer to strategy-2 large queue */ + CP_word aali_intr_ena; /* Enables interrupts if non-zero */ + CP_word aali_intr_sent; /* Interrupt issued if non-zero */ + CP_addr aali_heap; /* Pointer to application heap */ + CP_word aali_heaplen; /* Length of application heap */ + CP_word aali_hostlog; /* FORE internal use */ + CP_word aali_heartbeat; /* Monitor microcode health */ + CP_word aali_ucode_ver; /* Microcode firmware version */ + CP_word aali_mon_ver; /* Mon960 version */ + CP_word aali_xmit_tput; /* FORE internal use */ + + /* This must be on a quad-word boundary */ + Init_parms aali_init; /* Initialize command parameters */ +}; +typedef volatile struct aali Aali; + + +/* + * CP maintained statistics - DMA'd to host with CMD_GET_STATS command + */ +struct stats_taxi { + u_long taxi_bad_crc; /* Bad header CRC errors */ + u_long taxi_framing; /* Framing errors */ + u_long taxi_pad[2]; /* Pad to quad-word boundary */ +}; +typedef struct stats_taxi Stats_taxi; + +struct stats_oc3 { + u_long oc3_sect_bip8; /* Section 8-bit intrlv parity errors */ + u_long oc3_path_bip8; /* Path 8-bit intrlv parity errors */ + u_long oc3_line_bip24; /* Line 24-bit intrlv parity errors */ + u_long oc3_line_febe; /* Line far-end block errors */ + u_long oc3_path_febe; /* Path far-end block errors */ + u_long oc3_hec_corr; /* Correctible HEC errors */ + u_long oc3_hec_uncorr; /* Uncorrectible HEC errors */ + u_long oc3_pad; /* Pad to quad-word boundary */ +}; +typedef struct stats_oc3 Stats_oc3; + +struct stats_atm { + u_long atm_xmit; /* Cells transmitted */ + u_long atm_rcvd; /* Cells received */ + u_long atm_vpi_range; /* Cell drops - VPI out of range */ + u_long atm_vpi_noconn; /* Cell drops - no connect for VPI */ + u_long atm_vci_range; /* Cell drops - VCI out of range */ + u_long atm_vci_noconn; /* Cell drops - no connect for VCI */ + u_long atm_pad[2]; /* Pad to quad-word boundary */ +}; +typedef struct stats_atm Stats_atm; + +struct stats_aal0 { + u_long aal0_xmit; /* Cells transmitted */ + u_long aal0_rcvd; /* Cells received */ + u_long aal0_drops; /* Cell drops */ + u_long aal0_pad; /* Pad to quad-word boundary */ +}; +typedef struct stats_aal0 Stats_aal0; + +struct stats_aal4 { + u_long aal4_xmit; /* Cells transmitted */ + u_long aal4_rcvd; /* Cells received */ + u_long aal4_crc; /* Cells with payload CRC errors */ + u_long aal4_sar_cs; /* Cells with SAR/CS errors */ + u_long aal4_drops; /* Cell drops */ + u_long aal4_pdu_xmit; /* CS PDUs transmitted */ + u_long aal4_pdu_rcvd; /* CS PDUs received */ + u_long aal4_pdu_errs; /* CS layer protocol errors */ + u_long aal4_pdu_drops; /* CS PDUs dropped */ + u_long aal4_pad[3]; /* Pad to quad-word boundary */ +}; +typedef struct stats_aal4 Stats_aal4; + +struct stats_aal5 { + u_long aal5_xmit; /* Cells transmitted */ + u_long aal5_rcvd; /* Cells received */ + u_long aal5_crc_len; /* Cells with CRC/length errors */ + u_long aal5_drops; /* Cell drops */ + u_long aal5_pdu_xmit; /* CS PDUs transmitted */ + u_long aal5_pdu_rcvd; /* CS PDUs received */ + u_long aal5_pdu_crc; /* CS PDUs with CRC errors */ + u_long aal5_pdu_errs; /* CS layer protocol errors */ + u_long aal5_pdu_drops; /* CS PDUs dropped */ + u_long aal5_pad[3]; /* Pad to quad-word boundary */ +}; +typedef struct stats_aal5 Stats_aal5; + +struct stats_misc { + u_long buf1_sm_fail; /* Alloc fail: buffer strat 1 small */ + u_long buf1_lg_fail; /* Alloc fail: buffer strat 1 large */ + u_long buf2_sm_fail; /* Alloc fail: buffer strat 2 small */ + u_long buf2_lg_fail; /* Alloc fail: buffer strat 2 large */ + u_long rcvd_pdu_fail; /* Received PDU allocation failure */ + u_long carrier_status; /* Carrier status */ + u_long misc_pad[2]; /* Pad to quad-word boundary */ +}; +typedef struct stats_misc Stats_misc; + +struct fore_cp_stats { + Stats_taxi st_cp_taxi; /* TAXI layer statistics */ + Stats_oc3 st_cp_oc3; /* OC3 layer statistics */ + Stats_atm st_cp_atm; /* ATM layer statistics */ + Stats_aal0 st_cp_aal0; /* AAL0 layer statistics */ + Stats_aal4 st_cp_aal4; /* AAL3/4 layer statistics */ + Stats_aal5 st_cp_aal5; /* AAL5 layer statistics */ + Stats_misc st_cp_misc; /* Miscellaneous statistics */ +}; +typedef struct fore_cp_stats Fore_cp_stats; + +#define FORE_STATS_ALIGN 32 + +/* + * CP PROM data - DMA'd to host with CMD_GET_PROM command + */ +struct fore_prom { + u_long pr_hwver; /* Hardware version number */ + u_long pr_serno; /* Serial number */ + u_char pr_mac[8]; /* MAC address */ +}; +typedef struct fore_prom Fore_prom; + +#define FORE_PROM_ALIGN 32 + +#endif /* _FORE_AALI_H */ diff --git a/sys/dev/hfa/fore_buffer.c b/sys/dev/hfa/fore_buffer.c new file mode 100644 index 0000000..d8bdce4 --- /dev/null +++ b/sys/dev/hfa/fore_buffer.c @@ -0,0 +1,772 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_buffer.c,v 1.6 1997/05/06 22:09:21 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Buffer Supply queue management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_buffer.c,v 1.6 1997/05/06 22:09:21 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +static void fore_buf_drain __P((Fore_unit *)); +static void fore_buf_supply_1s __P((Fore_unit *)); +static void fore_buf_supply_1l __P((Fore_unit *)); + + +/* + * Allocate Buffer Supply Queues Data Structures + * + * Here we are allocating memory for both Strategy 1 Small and Large + * structures contiguously. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * 0 allocations successful + * else allocation failed + */ +int +fore_buf_allocate(fup) + Fore_unit *fup; +{ + caddr_t memp; + + /* + * Allocate non-cacheable memory for buffer supply status words + */ + memp = atm_dev_alloc( + sizeof(Q_status) * (BUF1_SM_QUELEN + BUF1_LG_QUELEN), + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_stat = (Q_status *) memp; + fup->fu_buf1l_stat = ((Q_status *) memp) + BUF1_SM_QUELEN; + + memp = DMA_GET_ADDR(fup->fu_buf1s_stat, + sizeof(Q_status) * (BUF1_SM_QUELEN + BUF1_LG_QUELEN), + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_statd = (Q_status *) memp; + fup->fu_buf1l_statd = ((Q_status *) memp) + BUF1_SM_QUELEN; + + /* + * Allocate memory for buffer supply descriptors + */ + memp = atm_dev_alloc(sizeof(Buf_descr) * + ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) + + (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)), + BUF_DESCR_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_desc = (Buf_descr *) memp; + fup->fu_buf1l_desc = ((Buf_descr *) memp) + + (BUF1_SM_QUELEN * BUF1_SM_ENTSIZE); + + memp = DMA_GET_ADDR(fup->fu_buf1s_desc, sizeof(Buf_descr) * + ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) + + (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)), + BUF_DESCR_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_descd = (Buf_descr *) memp; + fup->fu_buf1l_descd = ((Buf_descr *) memp) + + (BUF1_SM_QUELEN * BUF1_SM_ENTSIZE); + + return (0); +} + + +/* + * Buffer Supply Queues Initialization + * + * Allocate and initialize the host-resident buffer supply queue structures + * and then initialize the CP-resident queue structures. + * + * Called at interrupt level. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_buf_initialize(fup) + Fore_unit *fup; +{ + Aali *aap = fup->fu_aali; + Buf_queue *cqp; + H_buf_queue *hbp; + Buf_descr *bdp; + Buf_descr *bdp_dma; + Q_status *qsp; + Q_status *qsp_dma; + int i; + + /* + * Initialize Strategy 1 Small Queues + */ + + /* + * Point to CP-resident buffer supply queue + */ + cqp = (Buf_queue *)(fup->fu_ram + CP_READ(aap->aali_buf1s_q)); + + /* + * Point to host-resident buffer supply queue structures + */ + hbp = fup->fu_buf1s_q; + qsp = fup->fu_buf1s_stat; + qsp_dma = fup->fu_buf1s_statd; + bdp = fup->fu_buf1s_desc; + bdp_dma = fup->fu_buf1s_descd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < BUF1_SM_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hbp->hbq_cpelem = cqp; + hbp->hbq_status = qsp; + hbp->hbq_descr = bdp; + hbp->hbq_descr_dma = bdp_dma; + if (i == (BUF1_SM_QUELEN - 1)) + hbp->hbq_next = fup->fu_buf1s_q; + else + hbp->hbq_next = hbp + 1; + + /* + * Now let the CP into the game + */ + cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hbp++; + qsp++; + qsp_dma++; + bdp += BUF1_SM_ENTSIZE; + bdp_dma += BUF1_SM_ENTSIZE; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_buf1s_head = fup->fu_buf1s_tail = fup->fu_buf1s_q; + + + /* + * Initialize Strategy 1 Large Queues + */ + + /* + * Point to CP-resident buffer supply queue + */ + cqp = (Buf_queue *)(fup->fu_ram + CP_READ(aap->aali_buf1l_q)); + + /* + * Point to host-resident buffer supply queue structures + */ + hbp = fup->fu_buf1l_q; + qsp = fup->fu_buf1l_stat; + qsp_dma = fup->fu_buf1l_statd; + bdp = fup->fu_buf1l_desc; + bdp_dma = fup->fu_buf1l_descd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < BUF1_LG_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hbp->hbq_cpelem = cqp; + hbp->hbq_status = qsp; + hbp->hbq_descr = bdp; + hbp->hbq_descr_dma = bdp_dma; + if (i == (BUF1_LG_QUELEN - 1)) + hbp->hbq_next = fup->fu_buf1l_q; + else + hbp->hbq_next = hbp + 1; + + /* + * Now let the CP into the game + */ + cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hbp++; + qsp++; + qsp_dma++; + bdp += BUF1_LG_ENTSIZE; + bdp_dma += BUF1_LG_ENTSIZE; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_buf1l_head = fup->fu_buf1l_tail = fup->fu_buf1l_q; + + return; +} + + +/* + * Supply Buffers to CP + * + * This function will resupply the CP with buffers to be used to + * store incoming data. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_buf_supply(fup) + Fore_unit *fup; +{ + + /* + * First, clean out the supply queues + */ + fore_buf_drain(fup); + + /* + * Then, supply the buffers for each queue + */ + fore_buf_supply_1s(fup); + fore_buf_supply_1l(fup); + + return; +} + + +/* + * Supply Strategy 1 Small Buffers to CP + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_buf_supply_1s(fup) + Fore_unit *fup; +{ + H_buf_queue *hbp; + Buf_queue *cqp; + Buf_descr *bdp; + Buf_handle *bhp; + KBuffer *m; + int nvcc, nbuf, i; + + /* + * Figure out how many buffers we should be giving to the CP. + * We're basing this calculation on the current number of open + * VCCs thru this device, with certain minimum and maximum values + * enforced. This will then allow us to figure out how many more + * buffers we need to supply to the CP. This will be rounded up + * to fill a supply queue entry. + */ + nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC); + nbuf = nvcc * 4; + nbuf = MIN(nbuf, BUF1_SM_CPPOOL); + nbuf -= fup->fu_buf1s_cnt; + nbuf = roundup(nbuf, BUF1_SM_ENTSIZE); + + /* + * OK, now supply the buffers to the CP + */ + while (nbuf > 0) { + + /* + * Acquire a supply queue entry + */ + hbp = fup->fu_buf1s_tail; + if (!((*hbp->hbq_status) & QSTAT_FREE)) + break; + bdp = hbp->hbq_descr; + + /* + * Get a buffer for each descriptor in the queue entry + */ + for (i = 0; i < BUF1_SM_ENTSIZE; i++, bdp++) { + caddr_t cp; + + /* + * Get a small buffer + */ + KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA); + if (m == 0) { + break; + } + KB_HEADSET(m, BUF1_SM_DOFF); + + /* + * Point to buffer handle structure + */ + bhp = (Buf_handle *)((caddr_t)m + BUF1_SM_HOFF); + bhp->bh_type = BHT_S1_SMALL; + + /* + * Setup buffer descriptor + */ + bdp->bsd_handle = bhp; + KB_DATASTART(m, cp, caddr_t); + bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR( + cp, BUF1_SM_SIZE, BUF_DATA_ALIGN, 0); + if (bdp->bsd_buffer == NULL) { + /* + * Unable to assign dma address - free up + * this descriptor's buffer + */ + fup->fu_stats->st_drv.drv_bf_segdma++; + KB_FREEALL(m); + break; + } + + /* + * All set, so queue buffer (handle) + */ + ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq); + } + + /* + * If we we're not able to fill all the descriptors for + * an entry, free up what's been partially built + */ + if (i != BUF1_SM_ENTSIZE) { + + /* + * Clean up each used descriptor + */ + for (bdp = hbp->hbq_descr; i; i--, bdp++) { + + bhp = bdp->bsd_handle; + + DEQUEUE(bhp, Buf_handle, bh_qelem, + fup->fu_buf1s_bq); + + m = (KBuffer *) + ((caddr_t)bhp - BUF1_SM_HOFF); + KB_FREEALL(m); + } + break; + } + + /* + * Finally, we've got an entry ready for the CP. + * So claim the host queue entry and setup the CP-resident + * queue entry. The CP will (potentially) grab the supplied + * buffers when the descriptor pointer is set. + */ + fup->fu_buf1s_tail = hbp->hbq_next; + (*hbp->hbq_status) = QSTAT_PENDING; + cqp = hbp->hbq_cpelem; + cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma); + + /* + * Update counters, etc for supplied buffers + */ + fup->fu_buf1s_cnt += BUF1_SM_ENTSIZE; + nbuf -= BUF1_SM_ENTSIZE; + } + + return; +} + + +/* + * Supply Strategy 1 Large Buffers to CP + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_buf_supply_1l(fup) + Fore_unit *fup; +{ + H_buf_queue *hbp; + Buf_queue *cqp; + Buf_descr *bdp; + Buf_handle *bhp; + KBuffer *m; + int nvcc, nbuf, i; + + /* + * Figure out how many buffers we should be giving to the CP. + * We're basing this calculation on the current number of open + * VCCs thru this device, with certain minimum and maximum values + * enforced. This will then allow us to figure out how many more + * buffers we need to supply to the CP. This will be rounded up + * to fill a supply queue entry. + */ + nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC); + nbuf = nvcc * 4 * RECV_MAX_SEGS; + nbuf = MIN(nbuf, BUF1_LG_CPPOOL); + nbuf -= fup->fu_buf1l_cnt; + nbuf = roundup(nbuf, BUF1_LG_ENTSIZE); + + /* + * OK, now supply the buffers to the CP + */ + while (nbuf > 0) { + + /* + * Acquire a supply queue entry + */ + hbp = fup->fu_buf1l_tail; + if (!((*hbp->hbq_status) & QSTAT_FREE)) + break; + bdp = hbp->hbq_descr; + + /* + * Get a buffer for each descriptor in the queue entry + */ + for (i = 0; i < BUF1_LG_ENTSIZE; i++, bdp++) { + caddr_t cp; + + /* + * Get a cluster buffer + */ + KB_ALLOCEXT(m, BUF1_LG_SIZE, KB_F_NOWAIT, KB_T_DATA); + if (m == 0) { + break; + } + KB_HEADSET(m, BUF1_LG_DOFF); + + /* + * Point to buffer handle structure + */ + bhp = (Buf_handle *)((caddr_t)m + BUF1_LG_HOFF); + bhp->bh_type = BHT_S1_LARGE; + + /* + * Setup buffer descriptor + */ + bdp->bsd_handle = bhp; + KB_DATASTART(m, cp, caddr_t); + bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR( + cp, BUF1_LG_SIZE, BUF_DATA_ALIGN, 0); + if (bdp->bsd_buffer == NULL) { + /* + * Unable to assign dma address - free up + * this descriptor's buffer + */ + fup->fu_stats->st_drv.drv_bf_segdma++; + KB_FREEALL(m); + break; + } + + /* + * All set, so queue buffer (handle) + */ + ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq); + } + + /* + * If we we're not able to fill all the descriptors for + * an entry, free up what's been partially built + */ + if (i != BUF1_LG_ENTSIZE) { + + /* + * Clean up each used descriptor + */ + for (bdp = hbp->hbq_descr; i; i--, bdp++) { + bhp = bdp->bsd_handle; + + DEQUEUE(bhp, Buf_handle, bh_qelem, + fup->fu_buf1l_bq); + + m = (KBuffer *) + ((caddr_t)bhp - BUF1_LG_HOFF); + KB_FREEALL(m); + } + break; + } + + /* + * Finally, we've got an entry ready for the CP. + * So claim the host queue entry and setup the CP-resident + * queue entry. The CP will (potentially) grab the supplied + * buffers when the descriptor pointer is set. + */ + fup->fu_buf1l_tail = hbp->hbq_next; + (*hbp->hbq_status) = QSTAT_PENDING; + cqp = hbp->hbq_cpelem; + cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma); + + /* + * Update counters, etc for supplied buffers + */ + fup->fu_buf1l_cnt += BUF1_LG_ENTSIZE; + nbuf -= BUF1_LG_ENTSIZE; + } + + return; +} + + +/* + * Drain Buffer Supply Queues + * + * This function will free all completed entries at the head of each + * buffer supply queue. Since we consider the CP to "own" the buffers + * once we put them on a supply queue and since a completed supply queue + * entry is only telling us that the CP has accepted the buffers that we + * gave to it, there's not much to do here. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_buf_drain(fup) + Fore_unit *fup; +{ + H_buf_queue *hbp; + + /* + * Drain Strategy 1 Small Queue + */ + + /* + * Process each completed entry + */ + while (*fup->fu_buf1s_head->hbq_status & QSTAT_COMPLETED) { + + hbp = fup->fu_buf1s_head; + + if (*hbp->hbq_status & QSTAT_ERROR) { + /* + * XXX - what does this mean??? + */ + log(LOG_ERR, "fore_buf_drain: buf1s queue error\n"); + } + + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hbp->hbq_status = QSTAT_FREE; + fup->fu_buf1s_head = hbp->hbq_next; + } + + + /* + * Drain Strategy 1 Large Queue + */ + + /* + * Process each completed entry + */ + while (*fup->fu_buf1l_head->hbq_status & QSTAT_COMPLETED) { + + hbp = fup->fu_buf1l_head; + + if (*hbp->hbq_status & QSTAT_ERROR) { + /* + * XXX - what does this mean??? + */ + log(LOG_ERR, "fore_buf_drain: buf1l queue error\n"); + } + + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hbp->hbq_status = QSTAT_FREE; + fup->fu_buf1l_head = hbp->hbq_next; + } + + return; +} + + +/* + * Free Buffer Supply Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_buf_free(fup) + Fore_unit *fup; +{ + Buf_handle *bhp; + KBuffer *m; + + /* + * Free any previously supplied and not returned buffers + */ + if (fup->fu_flags & CUF_INITED) { + + /* + * Run through Strategy 1 Small queue + */ + while (bhp = Q_HEAD(fup->fu_buf1s_bq, Buf_handle)) { + caddr_t cp; + + /* + * Back off to buffer + */ + m = (KBuffer *)((caddr_t)bhp - BUF1_SM_HOFF); + + /* + * Dequeue handle and free buffer + */ + DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq); + + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0); + + KB_FREEALL(m); + } + + /* + * Run through Strategy 1 Large queue + */ + while (bhp = Q_HEAD(fup->fu_buf1l_bq, Buf_handle)) { + caddr_t cp; + + /* + * Back off to buffer + */ + m = (KBuffer *)((caddr_t)bhp - BUF1_LG_HOFF); + + /* + * Dequeue handle and free buffer + */ + DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq); + + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0); + + KB_FREEALL(m); + } + } + + /* + * Free the status words + */ + if (fup->fu_buf1s_stat) { + if (fup->fu_buf1s_statd) { + DMA_FREE_ADDR(fup->fu_buf1s_stat, fup->fu_buf1s_statd, + sizeof(Q_status) * + (BUF1_SM_QUELEN + BUF1_LG_QUELEN), + ATM_DEV_NONCACHE); + } + atm_dev_free((void *)fup->fu_buf1s_stat); + fup->fu_buf1s_stat = NULL; + fup->fu_buf1s_statd = NULL; + fup->fu_buf1l_stat = NULL; + fup->fu_buf1l_statd = NULL; + } + + /* + * Free the transmit descriptors + */ + if (fup->fu_buf1s_desc) { + if (fup->fu_buf1s_descd) { + DMA_FREE_ADDR(fup->fu_buf1s_desc, fup->fu_buf1s_descd, + sizeof(Buf_descr) * + ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) + + (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)), + 0); + } + atm_dev_free(fup->fu_buf1s_desc); + fup->fu_buf1s_desc = NULL; + fup->fu_buf1s_descd = NULL; + fup->fu_buf1l_desc = NULL; + fup->fu_buf1l_descd = NULL; + } + + return; +} + diff --git a/sys/dev/hfa/fore_command.c b/sys/dev/hfa/fore_command.c new file mode 100644 index 0000000..29b99c6 --- /dev/null +++ b/sys/dev/hfa/fore_command.c @@ -0,0 +1,445 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_command.c,v 1.10 1998/06/29 21:42:09 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Command queue management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_command.c,v 1.10 1998/06/29 21:42:09 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + +/* + * Local variables + */ +static struct t_atm_cause fore_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_TEMPORARY_FAILURE, + {0, 0, 0, 0} +}; + + +/* + * Allocate Command Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * 0 allocations successful + * else allocation failed + */ +int +fore_cmd_allocate(fup) + Fore_unit *fup; +{ + caddr_t memp; + + /* + * Allocate non-cacheable memory for command status words + */ + memp = atm_dev_alloc(sizeof(Q_status) * CMD_QUELEN, + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_cmd_stat = (Q_status *) memp; + + memp = DMA_GET_ADDR(fup->fu_cmd_stat, sizeof(Q_status) * CMD_QUELEN, + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_cmd_statd = (Q_status *) memp; + + /* + * Allocate memory for statistics buffer + */ + memp = atm_dev_alloc(sizeof(Fore_stats), FORE_STATS_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_stats = (Fore_stats *) memp; + +#ifdef FORE_PCI + /* + * Allocate memory for PROM buffer + */ + memp = atm_dev_alloc(sizeof(Fore_prom), FORE_PROM_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_prom = (Fore_prom *) memp; +#endif + + return (0); +} + + +/* + * Command Queue Initialization + * + * Allocate and initialize the host-resident command queue structures + * and then initialize the CP-resident queue structures. + * + * Called at interrupt level. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_cmd_initialize(fup) + Fore_unit *fup; +{ + Aali *aap = fup->fu_aali; + Cmd_queue *cqp; + H_cmd_queue *hcp; + Q_status *qsp; + Q_status *qsp_dma; + int i; + + /* + * Point to CP-resident command queue + */ + cqp = (Cmd_queue *)(fup->fu_ram + CP_READ(aap->aali_cmd_q)); + + /* + * Point to host-resident command queue structures + */ + hcp = fup->fu_cmd_q; + qsp = fup->fu_cmd_stat; + qsp_dma = fup->fu_cmd_statd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < CMD_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hcp->hcq_cpelem = cqp; + hcp->hcq_status = qsp; + if (i == (CMD_QUELEN - 1)) + hcp->hcq_next = fup->fu_cmd_q; + else + hcp->hcq_next = hcp + 1; + + /* + * Now let the CP into the game + */ + cqp->cmdq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hcp++; + qsp++; + qsp_dma++; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_cmd_head = fup->fu_cmd_tail = fup->fu_cmd_q; + + return; +} + + +/* + * Drain Command Queue + * + * This function will process and free all completed entries at the head + * of the command queue. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_cmd_drain(fup) + Fore_unit *fup; +{ + H_cmd_queue *hcp; + Fore_vcc *fvp; + + /* + * Process each completed entry + */ + while (*fup->fu_cmd_head->hcq_status & QSTAT_COMPLETED) { + + hcp = fup->fu_cmd_head; + + /* + * Process command completion + */ + switch (hcp->hcq_code) { + + case CMD_ACT_VCCIN: + case CMD_ACT_VCCOUT: + fvp = hcp->hcq_arg; + if (*hcp->hcq_status & QSTAT_ERROR) { + /* + * VCC activation failed - just abort vcc + */ + if (fvp) + atm_cm_abort(fvp->fv_connvc, + &fore_cause); + fup->fu_pif.pif_cmderrors++; + } else { + /* + * Successful VCC activation + */ + if (fvp) { + fvp->fv_state = CVS_ACTIVE; + fup->fu_open_vcc++; + } + } + break; + + case CMD_DACT_VCCIN: + case CMD_DACT_VCCOUT: + fvp = hcp->hcq_arg; + if (*hcp->hcq_status & QSTAT_ERROR) { + /* + * VCC dactivation failed - whine + */ + log(LOG_ERR, + "fore_cmd_drain: DACT failed, vcc=(%d,%d)\n", + fvp->fv_connvc->cvc_vcc->vc_vpi, + fvp->fv_connvc->cvc_vcc->vc_vci); + fup->fu_pif.pif_cmderrors++; + } else { + /* + * Successful VCC dactivation - so what? + */ + } + break; + + case CMD_GET_STATS: + if (*hcp->hcq_status & QSTAT_ERROR) { + /* + * Couldn't get stats + */ + fup->fu_pif.pif_cmderrors++; + fup->fu_stats_ret = EIO; + } else { + /* + * Stats are now in unit buffer + */ + fup->fu_stats_ret = 0; + } + DMA_FREE_ADDR(fup->fu_stats, fup->fu_statsd, + sizeof(Fore_cp_stats), 0); + fup->fu_flags &= ~FUF_STATCMD; + + /* + * Flush received stats data + */ +#ifdef VAC + if (vac) + vac_pageflush((addr_t)fup->fu_stats); +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN + /* + * Little endian machines receives the stats in + * wrong byte order. Instead of swapping in user + * land, swap here so that everything going out + * of the kernel is in correct host order. + */ + { + u_long *bp = (u_long *)fup->fu_stats; + int loop; + + for ( loop = 0; loop < sizeof(Fore_cp_stats)/ + sizeof(long); loop++, bp++ ) + *bp = ntohl(*bp); + } +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + + /* + * Poke whoever is waiting on the stats + */ + wakeup((caddr_t)&fup->fu_stats); + break; + +#ifdef FORE_PCI + case CMD_GET_PROM: + if (*hcp->hcq_status & QSTAT_ERROR) { + /* + * Couldn't get PROM data + */ + fup->fu_pif.pif_cmderrors++; + log(LOG_ERR, + "fore_cmd_drain: %s%d: GET_PROM failed\n", + fup->fu_pif.pif_name, + fup->fu_pif.pif_unit); + } else { + Fore_prom *fp = fup->fu_prom; + + /* + * Flush received PROM data + */ +#ifdef VAC + if (vac) + vac_pageflush((addr_t)fp); +#endif + /* + * Copy PROM info into config areas + */ + KM_COPY(&fp->pr_mac[2], + &fup->fu_pif.pif_macaddr, + sizeof(struct mac_addr)); + fup->fu_config.ac_macaddr = + fup->fu_pif.pif_macaddr; + sprintf(fup->fu_config.ac_hard_vers, "%d.%d.%d", + (fp->pr_hwver >> 16) & 0xff, + (fp->pr_hwver >> 8) & 0xff, + fp->pr_hwver & 0xff); + fup->fu_config.ac_serial = fp->pr_serno; + } + + DMA_FREE_ADDR(fup->fu_prom, fup->fu_promd, + sizeof(Fore_prom), 0); + break; +#endif /* FORE_PCI */ + + default: + log(LOG_ERR, "fore_cmd_drain: unknown command %d\n", + hcp->hcq_code); + } + + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hcp->hcq_status = QSTAT_FREE; + fup->fu_cmd_head = hcp->hcq_next; + } + + return; +} + + +/* + * Free Command Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_cmd_free(fup) + Fore_unit *fup; +{ + H_cmd_queue *hcp; + + /* + * Deal with any commands left on the queue + */ + if (fup->fu_flags & CUF_INITED) { + while (*fup->fu_cmd_head->hcq_status != QSTAT_FREE) { + hcp = fup->fu_cmd_head; + + switch (hcp->hcq_code) { + + case CMD_GET_STATS: + /* + * Just in case someone is sleeping on this + */ + fup->fu_stats_ret = EIO; + wakeup((caddr_t)&fup->fu_stats); + break; + } + + *hcp->hcq_status = QSTAT_FREE; + fup->fu_cmd_head = hcp->hcq_next; + } + } + + /* + * Free the statistics buffer + */ + if (fup->fu_stats) { + atm_dev_free(fup->fu_stats); + fup->fu_stats = NULL; + } + +#ifdef FORE_PCI + /* + * Free the PROM buffer + */ + if (fup->fu_prom) { + atm_dev_free(fup->fu_prom); + fup->fu_prom = NULL; + } +#endif + + /* + * Free the status words + */ + if (fup->fu_cmd_stat) { + if (fup->fu_cmd_statd) { + DMA_FREE_ADDR(fup->fu_cmd_stat, fup->fu_cmd_statd, + sizeof(Q_status) * CMD_QUELEN, + ATM_DEV_NONCACHE); + } + atm_dev_free((void *)fup->fu_cmd_stat); + fup->fu_cmd_stat = NULL; + fup->fu_cmd_statd = NULL; + } + + return; +} + diff --git a/sys/dev/hfa/fore_globals.c b/sys/dev/hfa/fore_globals.c new file mode 100644 index 0000000..4abc5fa --- /dev/null +++ b/sys/dev/hfa/fore_globals.c @@ -0,0 +1,119 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_globals.c,v 1.6 1997/05/06 22:09:31 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Global variable definitions + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_globals.c,v 1.6 1997/05/06 22:09:31 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Supported device models + */ +Fore_device fore_devices[] = { +#ifdef FORE_SBUS + {SBA200E_PROM_NAME, DEV_FORE_SBA200E}, + {SBA200_PROM_NAME, DEV_FORE_SBA200}, +#endif + {""} +}; + + +/* + * Device unit table + */ +Fore_unit *fore_units[FORE_MAX_UNITS] = {NULL}; +int fore_nunits = 0; + + +/* + * ATM Interface services + */ +static struct stack_defn fore_svaal5 = { + NULL, + SAP_CPCS_AAL5, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +static struct stack_defn fore_svaal4 = { + &fore_svaal5, + SAP_CPCS_AAL3_4, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +static struct stack_defn fore_svaal0 = { + &fore_svaal4, + SAP_ATM, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +struct stack_defn *fore_services = &fore_svaal0; + + +/* + * Storage pools + */ +struct sp_info fore_nif_pool = { + "fore nif pool", /* si_name */ + sizeof(struct atm_nif), /* si_blksiz */ + 5, /* si_blkcnt */ + 20 /* si_maxallow */ +}; + +struct sp_info fore_vcc_pool = { + "fore vcc pool", /* si_name */ + sizeof(Fore_vcc), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + + +/* + * Watchdog timer + */ +struct atm_time fore_timer = {0, 0}; + diff --git a/sys/dev/hfa/fore_if.c b/sys/dev/hfa/fore_if.c new file mode 100644 index 0000000..7d3b3b6 --- /dev/null +++ b/sys/dev/hfa/fore_if.c @@ -0,0 +1,205 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_if.c,v 1.6 1998/08/26 23:28:58 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Network interface layer support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_if.c,v 1.6 1998/08/26 23:28:58 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Handle netatm core service interface ioctl requests + * + * Called at splnet. + * + * Arguments: + * code ioctl function (sub)code + * data data to/from ioctl + * arg optional code-specific argument + * + * Returns: + * 0 request processed successfully + * error request failed - reason code + */ +int +fore_atm_ioctl(code, data, arg) + int code; + caddr_t data; + caddr_t arg; +{ + struct atminfreq *aip = (struct atminfreq *)data; + struct atm_pif *pip; + Fore_unit *fup; + caddr_t buf = aip->air_buf_addr; + struct air_vinfo_rsp *avr; + int count, len, buf_len = aip->air_buf_len; + int err = 0; + char ifname[2*IFNAMSIZ]; + + + ATM_DEBUG2("fore_atm_ioctl: code=%d, opcode=%d\n", + code, aip->air_opcode); + + switch ( aip->air_opcode ) { + + case AIOCS_INF_VST: + /* + * Get vendor statistics + */ + pip = (struct atm_pif *)arg; + fup = (Fore_unit *)pip; + if ( pip == NULL ) + return ( ENXIO ); + sprintf ( ifname, "%s%d", pip->pif_name, pip->pif_unit ); + + /* + * Cast response structure onto user's buffer + */ + avr = (struct air_vinfo_rsp *)buf; + + /* + * How large is the response structure? + */ + len = sizeof(struct air_vinfo_rsp); + + /* + * Sanity check - enough room for response structure? + */ + if ( buf_len < len ) + return ( ENOSPC ); + + /* + * Copy interface name into response structure + */ + if ( err = copyout ( ifname, avr->avsp_intf, IFNAMSIZ ) ) + break; + + /* + * Advance the buffer address and decrement the size + */ + buf += len; + buf_len -= len; + + /* + * Get the vendor stats from the hardware + */ + count = 0; + if ( ( err = fore_get_stats ( fup ) ) == 0 ) + { + /* + * Stick as much of it as we have room for + * into the response + */ + count = min ( sizeof(Fore_stats), buf_len ); + + /* + * Copy stats into user's buffer. Return value is + * amount of data copied. + */ + if (err = copyout((caddr_t)fup->fu_stats, buf, count)) + break; + buf += count; + buf_len -= count; + if ( count < sizeof(Fore_stats) ) + err = ENOSPC; + } + + /* + * Record amount we're returning as vendor info... + */ + if (err = copyout(&count, &avr->avsp_len, sizeof(int))) + break; + + /* + * Update the reply pointers and lengths + */ + aip->air_buf_addr = buf; + aip->air_buf_len = buf_len; + break; + + default: + err = ENOSYS; /* Operation not supported */ + break; + } + + return (err); +} + + +/* + * Free Fore-specific device resources + * + * Frees all dynamically acquired resources for a device unit. Before + * this function is called, the CP will have been reset and our interrupt + * vectors removed. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + * + */ +void +fore_interface_free(fup) + Fore_unit *fup; +{ + + /* + * Free up all of our allocated memory + */ + fore_xmit_free(fup); + fore_recv_free(fup); + fore_buf_free(fup); + fore_cmd_free(fup); + + /* + * Clear device initialized + */ + if (fup->fu_flags & CUF_INITED) { + fup->fu_flags &= ~CUF_INITED; + } + + if (fup->fu_flags & FUF_STATCMD) { + DMA_FREE_ADDR(fup->fu_stats, fup->fu_statsd, + sizeof(Fore_cp_stats), 0); + fup->fu_flags &= ~FUF_STATCMD; + } + return; +} + diff --git a/sys/dev/hfa/fore_include.h b/sys/dev/hfa/fore_include.h new file mode 100644 index 0000000..b146a3c --- /dev/null +++ b/sys/dev/hfa/fore_include.h @@ -0,0 +1,139 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_include.h,v 1.8 1998/02/19 20:10:18 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Local driver include files and global declarations + * + */ + +#ifndef _FORE_INCLUDE_H +#define _FORE_INCLUDE_H + +#include <netatm/kern_include.h> + +/* + * If not specified elsewhere, guess which type of bus support we want + */ +#if !(defined(FORE_PCI) || defined(FORE_SBUS)) +#if defined(sparc) +#define FORE_SBUS +#elif defined(__i386__) +#define FORE_PCI +#endif +#endif + +#ifdef FORE_PCI +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#endif + +#include <dev/hfa/fore.h> +#include <dev/hfa/fore_aali.h> +#include <dev/hfa/fore_slave.h> +#include <dev/hfa/fore_stats.h> +#include <dev/hfa/fore_var.h> + +/* + * Global function declarations + */ + /* fore_buffer.c */ +int fore_buf_allocate __P((Fore_unit *)); +void fore_buf_initialize __P((Fore_unit *)); +void fore_buf_supply __P((Fore_unit *)); +void fore_buf_free __P((Fore_unit *)); + + /* fore_command.c */ +int fore_cmd_allocate __P((Fore_unit *)); +void fore_cmd_initialize __P((Fore_unit *)); +void fore_cmd_drain __P((Fore_unit *)); +void fore_cmd_free __P((Fore_unit *)); + + /* fore_if.c */ +int fore_atm_ioctl __P((int, caddr_t, caddr_t)); +void fore_interface_free __P((Fore_unit *)); + + /* fore_init.c */ +void fore_initialize __P((Fore_unit *)); +void fore_initialize_complete __P((Fore_unit *)); + + /* fore_intr.c */ +#if defined(sun) +int fore_poll __P((void)); +#endif +#if (defined(BSD) && (BSD <= 199306)) +int fore_intr __P((void *)); +#else +void fore_intr __P((void *)); +#endif +void fore_watchdog __P((Fore_unit *)); + + /* fore_load.c */ + + /* fore_output.c */ +void fore_output __P((Cmn_unit *, Cmn_vcc *, KBuffer *)); + + /* fore_receive.c */ +int fore_recv_allocate __P((Fore_unit *)); +void fore_recv_initialize __P((Fore_unit *)); +void fore_recv_drain __P((Fore_unit *)); +void fore_recv_free __P((Fore_unit *)); + + /* fore_stats.c */ +int fore_get_stats __P((Fore_unit *)); + + /* fore_timer.c */ +void fore_timeout __P((struct atm_time *)); + + /* fore_transmit.c */ +int fore_xmit_allocate __P((Fore_unit *)); +void fore_xmit_initialize __P((Fore_unit *)); +void fore_xmit_drain __P((Fore_unit *)); +void fore_xmit_free __P((Fore_unit *)); + + /* fore_vcm.c */ +int fore_instvcc __P((Cmn_unit *, Cmn_vcc *)); +int fore_openvcc __P((Cmn_unit *, Cmn_vcc *)); +int fore_closevcc __P((Cmn_unit *, Cmn_vcc *)); + + +/* + * Global variable declarations + */ +extern Fore_device fore_devices[]; +extern Fore_unit *fore_units[]; +extern int fore_nunits; +extern struct stack_defn *fore_services; +extern struct sp_info fore_nif_pool; +extern struct sp_info fore_vcc_pool; +extern struct atm_time fore_timer; + +#endif /* _FORE_INCLUDE_H */ diff --git a/sys/dev/hfa/fore_init.c b/sys/dev/hfa/fore_init.c new file mode 100644 index 0000000..61f4f01 --- /dev/null +++ b/sys/dev/hfa/fore_init.c @@ -0,0 +1,314 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_init.c,v 1.7 1997/05/06 22:09:43 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Cell Processor (CP) initialization routines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_init.c,v 1.7 1997/05/06 22:09:43 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +#ifdef FORE_PCI +static void fore_get_prom __P((Fore_unit *)); +#endif + + +/* + * Begin CP Initialization + * + * This function will poll for the successful downloading and starting of + * the CP microcode program. After the microcode is running, we will allocate + * any needed kernel memory (must do it in non-interrupt mode), build the CP + * queue configurations and issue an Initialize command to the CP. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_initialize(fup) + Fore_unit *fup; +{ + Aali *aap; + Init_parms *inp; + caddr_t errmsg; + u_long vers; + + /* + * Must wait until firmware has been downloaded and is running + */ + if (CP_READ(fup->fu_mon->mon_bstat) != BOOT_RUNNING) { + + /* + * Try again later + */ + fup->fu_thandle = + timeout((KTimeout_ret(*) __P((void *)))fore_initialize, + (void *)fup, hz); + return; + } else + callout_handle_init(&fup->fu_thandle); + + /* + * Allocate queues and whatever else is needed + */ + if (fore_xmit_allocate(fup)) { + errmsg = "transmit queue allocation"; + goto failed; + } + if (fore_recv_allocate(fup)) { + errmsg = "receive queue allocation"; + goto failed; + } + if (fore_buf_allocate(fup)) { + errmsg = "buffer supply queue allocation"; + goto failed; + } + if (fore_cmd_allocate(fup)) { + errmsg = "command queue allocation"; + goto failed; + } + + /* + * CP microcode is downloaded - locate shared memory interface + */ + aap = (Aali *)(fup->fu_ram + CP_READ(fup->fu_mon->mon_appl)); + fup->fu_aali = aap; + + /* + * Pick out any interesting info from the microcode + */ + vers = CP_READ(aap->aali_ucode_ver); + if (vers < FORE_MIN_UCODE) { + errmsg = "unsupported microcode version"; + goto failed; + } + sprintf(fup->fu_config.ac_firm_vers, "%d.%d.%d", + (vers >> 16) & 0xff, (vers >> 8) & 0xff, vers & 0xff); + +#ifdef notdef + /* + * Turn on CP debugging + */ + aap->aali_hostlog = 1; +#endif + + /* + * Build the initialization block + */ + inp = &aap->aali_init; + inp->init_numvcc = CP_WRITE(FORE_MAX_VCC); + inp->init_cmd_elem = CP_WRITE(CMD_QUELEN); + inp->init_xmit_elem = CP_WRITE(XMIT_QUELEN); + inp->init_recv_elem = CP_WRITE(RECV_QUELEN); + inp->init_recv_ext = CP_WRITE(RECV_EXTRA_SEGS); + inp->init_xmit_ext = CP_WRITE(XMIT_EXTRA_SEGS); + inp->init_buf1s.bfs_quelen = CP_WRITE(BUF1_SM_QUELEN); + inp->init_buf1s.bfs_bufsize = CP_WRITE(BUF1_SM_SIZE); + inp->init_buf1s.bfs_cppool = CP_WRITE(BUF1_SM_CPPOOL); + inp->init_buf1s.bfs_entsize = CP_WRITE(BUF1_SM_ENTSIZE); + inp->init_buf1l.bfs_quelen = CP_WRITE(BUF1_LG_QUELEN); + inp->init_buf1l.bfs_bufsize = CP_WRITE(BUF1_LG_SIZE); + inp->init_buf1l.bfs_cppool = CP_WRITE(BUF1_LG_CPPOOL); + inp->init_buf1l.bfs_entsize = CP_WRITE(BUF1_LG_ENTSIZE); + inp->init_buf2s.bfs_quelen = CP_WRITE(0); + inp->init_buf2s.bfs_bufsize = CP_WRITE(0); + inp->init_buf2s.bfs_cppool = CP_WRITE(0); + inp->init_buf2s.bfs_entsize = CP_WRITE(0); + inp->init_buf2l.bfs_quelen = CP_WRITE(0); + inp->init_buf2l.bfs_bufsize = CP_WRITE(0); + inp->init_buf2l.bfs_cppool = CP_WRITE(0); + inp->init_buf2l.bfs_entsize = CP_WRITE(0); + + /* + * Enable device interrupts + */ + aap->aali_intr_ena = CP_WRITE(1); + + /* + * Issue the Initialize command to the CP and wait for + * the CP to interrupt to signal completion + */ + inp->init_status = CP_WRITE(QSTAT_PENDING); + inp->init_cmd = CP_WRITE(CMD_INIT | CMD_INTR_REQ); + return; + +failed: + /* + * Initialization failure + */ + fore_interface_free(fup); + log(LOG_ERR, "fore initialization failed: intf=%s%d, err=%s\n", + fup->fu_pif.pif_name, fup->fu_pif.pif_unit, errmsg); + return; +} + + +/* + * Complete CP Initialization + * + * Called after the CP has successfully completed processing of the + * Initialize command. We will now finish off our part of the + * initialization process by setting up all the host-based queue + * management structures. + * + * Called at interrupt level. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_initialize_complete(fup) + Fore_unit *fup; +{ + Aali *aap = fup->fu_aali; + + /* + * Log an initialization failure + */ + if (CP_READ(aap->aali_init.init_status) & QSTAT_ERROR) { + + log(LOG_ERR, + "fore initialization failed: intf=%s%d, hbeat=0x%x\n", + fup->fu_pif.pif_name, fup->fu_pif.pif_unit, + CP_READ(aap->aali_heartbeat)); + return; + } + + ATM_DEBUG1("heap=0x%x\n", aap->aali_heap); + ATM_DEBUG1("heaplen=0x%x\n", aap->aali_heaplen); + ATM_DEBUG1("cmd_q=0x%x\n", aap->aali_cmd_q); + ATM_DEBUG1("xmit_q=0x%x\n", aap->aali_xmit_q); + ATM_DEBUG1("recv_q=0x%x\n", aap->aali_recv_q); + ATM_DEBUG1("buf1s_q=0x%x\n", aap->aali_buf1s_q); + ATM_DEBUG1("buf1l_q=0x%x\n", aap->aali_buf1l_q); + ATM_DEBUG1("buf2s_q=0x%x\n", aap->aali_buf2s_q); + ATM_DEBUG1("buf2l_q=0x%x\n", aap->aali_buf2l_q); + + /* + * Initialize all of our queues + */ + fore_xmit_initialize(fup); + fore_recv_initialize(fup); + fore_buf_initialize(fup); + fore_cmd_initialize(fup); + + /* + * Mark device initialization completed + */ + fup->fu_flags |= CUF_INITED; + +#ifdef FORE_PCI + fore_get_prom(fup); +#endif + return; +} + + +#ifdef FORE_PCI +/* + * Get device PROM values from CP + * + * This function will issue a GET_PROM command to the CP in order to + * initiate the DMA transfer of the CP's PROM structure to the host. + * This will be called after CP initialization has completed. + * There is (currently) no retry if this fails. + * + * Called at interrupt level. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + * + */ +static void +fore_get_prom(fup) + Fore_unit *fup; +{ + H_cmd_queue *hcp; + Cmd_queue *cqp; + + /* + * Queue command at end of command queue + */ + hcp = fup->fu_cmd_tail; + if ((*hcp->hcq_status) & QSTAT_FREE) { + + /* + * Queue entry available, so set our view of things up + */ + hcp->hcq_code = CMD_GET_PROM; + hcp->hcq_arg = NULL; + fup->fu_cmd_tail = hcp->hcq_next; + + /* + * Now set the CP-resident queue entry - the CP will grab + * the command when the op-code is set. + */ + cqp = hcp->hcq_cpelem; + (*hcp->hcq_status) = QSTAT_PENDING; + + fup->fu_promd = DMA_GET_ADDR(fup->fu_prom, sizeof(Fore_prom), + FORE_PROM_ALIGN, 0); + if (fup->fu_promd == NULL) { + fup->fu_stats->st_drv.drv_cm_nodma++; + return; + } + cqp->cmdq_prom.prom_buffer = (CP_dma) CP_WRITE(fup->fu_promd); + cqp->cmdq_prom.prom_cmd = CP_WRITE(CMD_GET_PROM | CMD_INTR_REQ); + + } else { + /* + * Command queue full + */ + fup->fu_stats->st_drv.drv_cm_full++; + } + + return; +} +#endif /* FORE_PCI */ + diff --git a/sys/dev/hfa/fore_intr.c b/sys/dev/hfa/fore_intr.c new file mode 100644 index 0000000..f295b62 --- /dev/null +++ b/sys/dev/hfa/fore_intr.c @@ -0,0 +1,268 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_intr.c,v 1.7 1997/05/06 22:09:48 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Interrupt processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_intr.c,v 1.7 1997/05/06 22:09:48 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + +#if defined(sun) +/* + * Polling interrupt routine + * + * Polling interrupts are handled by calling all interrupt service + * routines for a given level until someone claims to have "handled" the + * interrupt. + * + * Called at interrupt level. + * + * Arguments: + * none + * + * Returns: + * 1 an interrupt has been serviced + * 0 no interrupts serviced + * + */ +int +fore_poll() +{ + int serviced = 0; + int unit; + + /* + * See if any of our devices are interrupting + */ + for ( unit = 0; unit < fore_nunits; unit++ ) + { + Fore_unit *fup = fore_units[unit]; + + if (fup == NULL) + continue; + + serviced += fore_intr((void *)fup); + } + + /* + * Indicate if we handled an interrupt + */ + return (serviced ? 1 : 0); +} +#endif /* defined(sun) */ + + +/* + * Device interrupt routine + * + * Called at interrupt level. + * + * Arguments: + * arg pointer to device unit structure + * + * Returns: + * 1 device interrupt was serviced + * 0 no interrupts serviced + * + */ +#if (defined(BSD) && (BSD <= 199306)) +int +#else +void +#endif +fore_intr(arg) + void *arg; +{ + Fore_unit *fup = arg; + Aali *aap; +#if (defined(BSD) && (BSD <= 199306)) + int serviced = 0; +#endif + + /* + * Try to prevent stuff happening after we've paniced + */ + if (panicstr) { + goto done; + } + + /* + * Get to the microcode shared memory interface + */ + if ((aap = fup->fu_aali) == NULL) + goto done; + + /* + * Has this card issued an interrupt?? + */ +#ifdef FORE_PCI + if (*fup->fu_psr) { +#else + if (aap->aali_intr_sent) { +#endif + + /* + * Indicate that we've serviced an interrupt. + */ +#if (defined(BSD) && (BSD <= 199306)) + serviced = 1; +#endif + + /* + * Clear the device interrupt + */ + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + SBA200E_HCR_SET(*fup->fu_ctlreg, SBA200E_CLR_SBUS_INTR); + break; + + case DEV_FORE_SBA200: + *fup->fu_ctlreg = SBA200_CLR_SBUS_INTR; + break; +#endif +#ifdef FORE_PCI + case DEV_FORE_PCA200E: + PCA200E_HCR_SET(*fup->fu_ctlreg, PCA200E_CLR_HBUS_INT); + break; +#endif + + } + aap->aali_intr_sent = CP_WRITE(0); + + /* + * Reset the watchdog timer + */ + fup->fu_timer = FORE_WATCHDOG; + + /* + * Device initialization handled separately + */ + if ((fup->fu_flags & CUF_INITED) == 0) { + + /* + * We're just initializing device now, so see if + * the initialization command has completed + */ + if (CP_READ(aap->aali_init.init_status) & + QSTAT_COMPLETED) + fore_initialize_complete(fup); + + /* + * If we're still not inited, none of the host + * queues are setup yet + */ + if ((fup->fu_flags & CUF_INITED) == 0) + goto done; + } + + /* + * Drain the queues of completed work + */ + fore_cmd_drain(fup); + fore_recv_drain(fup); + fore_xmit_drain(fup); + + /* + * Supply more buffers to the CP + */ + fore_buf_supply(fup); + } + +done: +#if (defined(BSD) && (BSD <= 199306)) + return(serviced); +#else + return; +#endif +} + + +/* + * Watchdog timeout routine + * + * Called when we haven't heard from the card in a while. Just in case + * we missed an interrupt, we'll drain the queues and try to resupply the + * CP with more receive buffers. If the CP is partially wedged, hopefully + * this will be enough to get it going again. + * + * Called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + * + */ +void +fore_watchdog(fup) + Fore_unit *fup; +{ + /* + * Try to prevent stuff happening after we've paniced + */ + if (panicstr) { + return; + } + + /* + * Reset the watchdog timer + */ + fup->fu_timer = FORE_WATCHDOG; + + /* + * If the device is initialized, nudge it (wink, wink) + */ + if (fup->fu_flags & CUF_INITED) { + + /* + * Drain the queues of completed work + */ + fore_cmd_drain(fup); + fore_recv_drain(fup); + fore_xmit_drain(fup); + + /* + * Supply more buffers to the CP + */ + fore_buf_supply(fup); + } + + return; +} diff --git a/sys/dev/hfa/fore_load.c b/sys/dev/hfa/fore_load.c new file mode 100644 index 0000000..4250ddc --- /dev/null +++ b/sys/dev/hfa/fore_load.c @@ -0,0 +1,1618 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_load.c,v 1.12 1998/06/29 21:42:14 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Loadable kernel module and device identification support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_load.c,v 1.12 1998/06/29 21:42:14 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +static int fore_start __P((void)); +static int fore_stop __P((void)); +static int fore_doload __P((void)); +static int fore_dounload __P((void)); +#ifdef sun +static int fore_identify __P((char *)); +static int fore_attach __P((struct devinfo *)); +#endif +#ifdef __FreeBSD__ +static char * fore_pci_probe __P((pcici_t, pcidi_t)); +static void fore_pci_attach __P((pcici_t, int)); +#if BSD < 199506 +static int fore_pci_shutdown __P((struct kern_devconf *, int)); +#else +static void fore_pci_shutdown __P((int, void *)); +#endif +#endif +static void fore_unattach __P((Fore_unit *)); +static void fore_reset __P((Fore_unit *)); + + +/* + * Local variables + */ +static int fore_inited = 0; + +/* + * Driver entry points + */ +#ifdef sun +static struct dev_ops fore_ops = { + 1, /* revision */ + fore_identify, /* identify */ + fore_attach, /* attach */ + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* strategy */ + NULL, /* dump */ + NULL, /* psize */ + NULL, /* ioctl */ + NULL, /* reset */ + NULL /* mmap */ +}; +#endif + +#ifdef __FreeBSD__ +static u_long fore_pci_count = 0; + +static struct pci_device fore_pci_device = { + FORE_DEV_NAME, + fore_pci_probe, + fore_pci_attach, + &fore_pci_count, +#if BSD < 199506 + fore_pci_shutdown +#else + NULL +#endif +}; + +DATA_SET(pcidevice_set, fore_pci_device); +#endif + + +/* + * Initialize driver processing + * + * This will be called during module loading. Not much to do here, as + * we must wait for our identify/attach routines to get called before + * we know what we're in for. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +fore_start() +{ + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: fore=%d.%d kernel=%d.%d\n", + ATM_VERS_MAJ(ATM_VERSION), ATM_VERS_MIN(ATM_VERSION), + ATM_VERS_MAJ(atm_version), ATM_VERS_MIN(atm_version)); + return (EINVAL); + } + + /* + * Initialize DMA mapping + */ + DMA_INIT(); + + /* + * Start up watchdog timer + */ + atm_timeout(&fore_timer, ATM_HZ * FORE_TIME_TICK, fore_timeout); + + fore_inited = 1; + + return (0); +} + + +/* + * Halt driver processing + * + * This will be called just prior to unloading the module from memory. + * Everything we've setup since we've been loaded must be undone here. + * + * Arguments: + * none + * + * Returns: + * 0 shutdown was successful + * errno shutdown failed - reason indicated + * + */ +static int +fore_stop() +{ + int err = 0; + int s = splimp(); + int i; + + /* + * Stop the watchdog timer + */ + (void) atm_untimeout(&fore_timer); + + /* + * Clean up each device (if any) + */ + for ( i = 0; i < fore_nunits; i++ ) { + Fore_unit *fup = fore_units[i]; + + if (fup == NULL) + continue; + + /* + * Deregister device from kernel services + */ + if (err = atm_physif_deregister((Cmn_unit *)fup)) { + (void) splx(s); + return (err); + } + + /* + * Unattach the device from the system + */ + fore_unattach(fup); + + /* + * Free any Fore-specific device resources + */ + fore_interface_free(fup); + + /* + * Free the unit structure + */ + atm_dev_free(fup); + fore_units[i] = NULL; + } + + fore_nunits = 0; + + /* + * Now free our global resources + */ + + /* + * Release our storage pools + */ + atm_release_pool(&fore_vcc_pool); + atm_release_pool(&fore_nif_pool); + + /* + * Release all DMA mappings + */ + DMA_RELEASE(); + + fore_inited = 0; + + (void) splx(s); + + return (0); +} + + +#ifdef sun +/* + * Device identify routine + * + * Determine if this driver will support the named device. If we claim to + * support the device, our attach routine will (later) be called for the + * device. + * + * Arguments: + * name pointer to identifier string from device + * + * Returns: + * 1 driver claims support for this device + * 0 device not claimed by this driver + * + */ +static int +fore_identify(name) + char *name; +{ + int ret = 0; + int i = 0; + + /* + * Initialize driver stuff + */ + if (fore_inited == 0) { + if (fore_start()) + return (0); + } + + while (fore_devices[i].fd_name) { + if (strcmp(fore_devices[i].fd_name, name) == 0) { + + /* + * We support this device!! + */ + if (fore_nunits < FORE_MAX_UNITS) { + fore_nunits++; + ret = 1; + } else { + log(LOG_ERR, + "fore_identify: Too many devices\n"); + } + break; + } + i++; + } + return (ret); +} + + +/* + * Device attach routine + * + * Attach a device we've previously claimed to support. Walk through its + * register set and map, as required. Determine what level the device will + * be interrupting at and then register an interrupt handler for it. If we + * succeed, then reset the adapter and read useful info from its PROM. + * Last, register the interface with the kernel ATM services. + * + * Arguments: + * devinfo_p pointer to device information structure + * + * Returns: + * 0 attach was successful + * -1 attach failed + * + */ +static int +fore_attach(devinfo_p) + struct dev_info *devinfo_p; +{ + struct dev_reg *dev_reg_p; + struct dev_intr *dev_intr_p; + Fore_unit *fup; + Atm_config *fcp; + addr_t valp; + int val; + int i; + int err_count = BOOT_LOOPS; + static int unit = 0; + + /* + * Sanity check + */ + if (devinfo_p == NULL) + return (-1); + + /* + * Make sure this isn't a duplicate unit + */ + if (fore_units[unit] != NULL) + return (-1); + + /* + * Allocate a new unit structure + */ + fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0); + if (fup == NULL) + return (-1); + + /* + * Start initializing it + */ + fup->fu_unit = unit; + fup->fu_mtu = FORE_IFF_MTU; + fup->fu_devinfo = devinfo_p; + fup->fu_vcc_pool = &fore_vcc_pool; + fup->fu_nif_pool = &fore_nif_pool; + fup->fu_ioctl = fore_atm_ioctl; + fup->fu_instvcc = fore_instvcc; + fup->fu_openvcc = fore_openvcc; + fup->fu_closevcc = fore_closevcc; + fup->fu_output = fore_output; + + /* + * Consider this unit assigned + */ + fore_units[unit] = fup; + unit++; + + ATM_DEBUG1("fore_attach: fup=0x%x\n", (int)fup); + ATM_DEBUG2("\tfu_xmit_q=0x%x fu_xmit_head=0x%x\n", + (int)fup->fu_xmit_q, (int)&fup->fu_xmit_head); + ATM_DEBUG2("\tfu_recv_q=0x%x fu_recv_head=0x%x\n", + (int)fup->fu_recv_q, (int)&fup->fu_recv_head); + ATM_DEBUG2("\tfu_buf1s_q=0x%x fu_buf1s_head=0x%x\n", + (int)fup->fu_buf1s_q, (int)&fup->fu_buf1s_head); + ATM_DEBUG2("\tfu_buf1l_q=0x%x fu_buf1l_head=0x%x\n", + (int)fup->fu_buf1l_q, (int)&fup->fu_buf1l_head); + ATM_DEBUG2("\tfu_cmd_q=0x%x fu_cmd_head=0x%x\n", + (int)fup->fu_cmd_q, (int)&fup->fu_cmd_head); + ATM_DEBUG1("\tfu_stats=0x%x\n", + (int)&fup->fu_stats); + + /* + * Tell kernel our unit number + */ + devinfo_p->devi_unit = fup->fu_unit; + + /* + * Figure out what type of device we've got. This should always + * work since we've already done this at identify time! + */ + i = 0; + while (fore_devices[i].fd_name) { + if (strcmp(fore_devices[i].fd_name, devinfo_p->devi_name) == 0) + break; + i++; + } + if (fore_devices[i].fd_name == NULL) + return (-1); + + fup->fu_config.ac_device = fore_devices[i].fd_devtyp; + + /* + * Walk through the OPENPROM register information + * mapping register banks as they are found. + */ + for ( dev_reg_p = devinfo_p->devi_reg, i = 1; + i <= devinfo_p->devi_nreg; i++, ++dev_reg_p ) + { + if ( dev_reg_p == NULL ) + { + /* + * Can't happen... + */ + return ( -1 ); + } + + /* + * Each device type has different register sets + */ + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + + switch ( i ) + { + /* + * Host Control Register (HCR) + */ + case 1: + if ( sizeof(Fore_reg) != dev_reg_p->reg_size ) + { + return ( -1 ); + } + fup->fu_ctlreg = (Fore_reg *) + map_regs ( dev_reg_p->reg_addr, + sizeof(Fore_reg), + dev_reg_p->reg_bustype ); + if ( fup->fu_ctlreg == NULL ) + { + return ( -1 ); + } + break; + + /* + * SBus Burst Transfer Configuration Register + */ + case 2: + /* + * Not used + */ + break; + + /* + * SBus Interrupt Level Select Register + */ + case 3: + if ( sizeof (Fore_reg) != dev_reg_p->reg_size ) + { + return ( -1 ); + } + fup->fu_intlvl = (Fore_reg *) + map_regs ( dev_reg_p->reg_addr, + sizeof(Fore_reg), + dev_reg_p->reg_bustype ); + if ( fup->fu_intlvl == NULL ) + { + return ( -1 ); + } + break; + + /* + * i960 RAM + */ + case 4: + fup->fu_ram = (Fore_mem *) + map_regs ( dev_reg_p->reg_addr, + dev_reg_p->reg_size, + dev_reg_p->reg_bustype ); + if ( fup->fu_ram == NULL ) + { + return ( -1 ); + } + fup->fu_ramsize = dev_reg_p->reg_size; + + /* + * Various versions of the Sun PROM mess with + * the reg_addr value in unpredictable (to me, + * at least) ways, so just use the "memoffset" + * property, which should give us the RAM + * offset directly. + */ + val = getprop(devinfo_p->devi_nodeid, + "memoffset", -1); + if (val == -1) { + return (-1); + } + fup->fu_config.ac_ram = val; + fup->fu_config.ac_ramsize = fup->fu_ramsize; + + /* + * Set monitor interface for initializing + */ + fup->fu_mon = (Mon960 *) + (fup->fu_ram + MON960_BASE); + break; + + default: + log(LOG_ERR, + "fore_attach: Too many registers\n"); + return ( -1 ); + } + break; + + case DEV_FORE_SBA200: + + switch ( i ) + { + /* + * Board Control Register (BCR) + */ + case 1: + if ( sizeof(Fore_reg) != dev_reg_p->reg_size ) + { + return ( -1 ); + } + fup->fu_ctlreg = (Fore_reg *) + map_regs ( dev_reg_p->reg_addr, + sizeof(Fore_reg), + dev_reg_p->reg_bustype ); + if ( fup->fu_ctlreg == NULL ) + { + return ( -1 ); + } + break; + + /* + * i960 RAM + */ + case 2: + fup->fu_ram = (Fore_mem *) + map_regs ( dev_reg_p->reg_addr, + dev_reg_p->reg_size, + dev_reg_p->reg_bustype ); + if ( fup->fu_ram == NULL ) + { + return ( -1 ); + } + fup->fu_ramsize = dev_reg_p->reg_size; + + /* + * Various versions of the Sun PROM mess with + * the reg_addr value in unpredictable (to me, + * at least) ways, so just use the "memoffset" + * property, which should give us the RAM + * offset directly. + */ + val = getprop(devinfo_p->devi_nodeid, + "memoffset", -1); + if (val == -1) { + return (-1); + } + fup->fu_config.ac_ram = val; + fup->fu_config.ac_ramsize = fup->fu_ramsize; + + /* + * Set monitor interface for initializing + */ + fup->fu_mon = (Mon960 *) + (fup->fu_ram + MON960_BASE); + break; + + default: + log(LOG_ERR, + "fore_attach: Too many registers\n"); + return ( -1 ); + } + break; +#endif /* FORE_SBUS */ + + default: + log(LOG_ERR, + "fore_attach: Unsupported device type %d\n", + fup->fu_config.ac_device); + return (-1); + } + } + + /* + * Install the device in the interrupt chain. + * + * dev_intr_p may be null IFF devi_nintr is zero. + */ + dev_intr_p = devinfo_p->devi_intr; + for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p ) + { + + if ( dev_intr_p == NULL ) + { + /* + * Can't happen. + */ + return ( -1 ); + } + + /* + * Convert hardware ipl (0-15) into spl level. + */ + if ( ipltospl ( dev_intr_p->int_pri ) > fup->fu_intrpri ) + { + fup->fu_intrpri = ipltospl ( dev_intr_p->int_pri ); + + /* + * If SBA-200E card, set SBus interrupt level + * into board register + */ + if ( fup->fu_intlvl ) { +#if defined(sun4c) + *(fup->fu_intlvl) = dev_intr_p->int_pri; +#elif defined(sun4m) + extern int svimap[]; + + *(fup->fu_intlvl) = + svimap[dev_intr_p->int_pri & 0xf]; +#else + #error PORT ME; +#endif + } + } + + DEVICE_LOCK((Cmn_unit *)fup); + + /* + * Register our interrupt routine. + */ + (void) addintr ( dev_intr_p->int_pri, fore_poll, + devinfo_p->devi_name, devinfo_p->devi_unit ); + + /* + * If we can do DMA (we can), then DVMA routines need + * to know the highest IPL level we will interrupt at. + */ + adddma ( dev_intr_p->int_pri ); + + DEVICE_UNLOCK((Cmn_unit *)fup); + } + + /* + * Poke the hardware...boot the CP and prepare it for downloading + */ + fore_reset(fup); + + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + /* + * Enable interrupts + */ + SBA200E_HCR_SET(*fup->fu_ctlreg, SBA200E_SBUS_ENA); + break; +#endif /* FORE_SBUS */ + } + + /* + * Wait for monitor to perform self-test + */ + while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) { + if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) { + log(LOG_ERR, "fore_attach: Unit %d failed self-test\n", + fup->fu_unit); + return (-1); + + } else if ( --err_count == 0 ) { + log(LOG_ERR, "fore_attach: Unit %d unable to boot\n", + fup->fu_unit); + return (-1); + } + DELAY ( BOOT_DELAY ); + } + + /* + * Write a one line message to the console informing + * that we've attached the device. + */ + report_dev ( devinfo_p ); + + /* + * Get the mac address from the card PROM + */ + val = getprop ( devinfo_p->devi_nodeid, "macaddress1", -1 ); + if ( val != -1 ) { + fup->fu_pif.pif_macaddr.ma_data[0] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress2", -1 ); + fup->fu_pif.pif_macaddr.ma_data[1] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress3", -1 ); + fup->fu_pif.pif_macaddr.ma_data[2] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress4", -1 ); + fup->fu_pif.pif_macaddr.ma_data[3] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress5", -1 ); + fup->fu_pif.pif_macaddr.ma_data[4] = val & 0xff; + val = getprop ( devinfo_p->devi_nodeid, "macaddress6", -1 ); + fup->fu_pif.pif_macaddr.ma_data[5] = val & 0xff; + } else { + /* + * Newer PROM - mac addresses have been combined. Also, + * macaddrlo2 reflects the board serial number. + */ + val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrlo2", -1)); + KM_COPY ( (caddr_t)&val, + (caddr_t)&fup->fu_pif.pif_macaddr.ma_data[2], + sizeof(val) ); + val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrhi4", -1)); + KM_COPY ( (caddr_t)&val, + (caddr_t)fup->fu_pif.pif_macaddr.ma_data, + sizeof(val) ); + } + + /* + * Setup the adapter config info + */ + fcp = &fup->fu_config; + fcp->ac_vendor = VENDOR_FORE; + fcp->ac_vendapi = VENDAPI_FORE_1; + fcp->ac_macaddr = fup->fu_pif.pif_macaddr; + val = getprop ( devinfo_p->devi_nodeid, "promversion", -1 ); + if ( val == -1 ) { + val = getprop ( devinfo_p->devi_nodeid, "hw-version", -1 ); + } + if (val != -1) { + sprintf(fcp->ac_hard_vers, "%d.%d.%d", + (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); + } else + sprintf(fcp->ac_hard_vers, "Unknown"); + + val = getprop ( devinfo_p->devi_nodeid, "serialnumber", -1 ); + if ( val != -1 ) + fcp->ac_serial = val; + + valp = (addr_t)getlongprop ( devinfo_p->devi_nodeid, "model" ); + if ( valp ) + { + /* + * Media Type + */ + switch (fcp->ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + fcp->ac_media = MEDIA_OC3C; + fup->fu_pif.pif_pcr = ATM_PCR_OC3C; + break; + + case DEV_FORE_SBA200: + /* + * Look at the /SSS trailer to determine 4B5B speed + * TAXI-100 = 125; TAXI-140 = 175 + * Assume that OC3 has no /SSS speed identifier. + */ + while (*valp && *valp != '/') + valp++; + if (*valp == NULL) { + fcp->ac_media = MEDIA_OC3C; + fup->fu_pif.pif_pcr = ATM_PCR_OC3C; + } else if (strcmp(valp, "/125") == 0) { + fcp->ac_media = MEDIA_TAXI_100; + fup->fu_pif.pif_pcr = ATM_PCR_TAXI100; + } else { + fcp->ac_media = MEDIA_TAXI_140; + fup->fu_pif.pif_pcr = ATM_PCR_TAXI140; + } + break; +#endif /* FORE_SBUS */ + } + + /* + * Free property space + */ + KM_FREE(valp, getproplen(devinfo_p->devi_nodeid, "model"), 0); + } + + /* + * Bus information + */ + fcp->ac_busslot = +#ifdef SBUS_SIZE + (long)(devinfo_p->devi_reg->reg_addr - SBUS_BASE) / SBUS_SIZE; +#else + sbusslot((u_long)devinfo_p->devi_reg->reg_addr); +#endif + + val = getprop(devinfo_p->devi_parent->devi_nodeid, "burst-sizes", 0); + if (val & SBUS_BURST32) + fcp->ac_bustype = BUS_SBUS_B32; + else + fcp->ac_bustype = BUS_SBUS_B16; + + /* + * Set device capabilities + */ + fup->fu_pif.pif_maxvpi = FORE_MAX_VPI; + fup->fu_pif.pif_maxvci = FORE_MAX_VCI; + + /* + * Register this interface with ATM core services + */ + if ( atm_physif_register + ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 ) + { + /* + * Registration failed - back everything out + */ + /* + * Modload calls UNLOAD if it get's a failure - don't + * call fore_unload() here. + */ + return ( -1 ); + } + + /* + * Initialize the CP microcode program. + */ + fore_initialize(fup); + + return (0); +} +#endif /* sun */ + + +#ifdef __FreeBSD__ +/* + * Device probe routine + * + * Determine if this driver will support the identified device. If we claim + * to support the device, our attach routine will (later) be called for the + * device. + * + * Arguments: + * config_id device's PCI configuration ID + * device_id device's PCI Vendor/Device ID + * + * Returns: + * name device identification string + * NULL device not claimed by this driver + * + */ +static char * +fore_pci_probe(config_id, device_id) + pcici_t config_id; + pcidi_t device_id; +{ + + /* + * Initialize driver stuff + */ + if (fore_inited == 0) { + if (fore_start()) + return (NULL); + } + + if ((device_id & 0xffff) != FORE_VENDOR_ID) + return (NULL); + + if (((device_id >> 16) & 0xffff) == FORE_PCA200E_ID) + return ("FORE Systems PCA-200E ATM"); + + return (NULL); +} + + +/* + * Device attach routine + * + * Attach a device we've previously claimed to support. Walk through its + * register set and map, as required. Determine what level the device will + * be interrupting at and then register an interrupt handler for it. If we + * succeed, then reset the adapter and initialize the microcode. + * Last, register the interface with the kernel ATM services. + * + * Arguments: + * config_id device's PCI configuration ID + * unit device unit number + * + * Returns: + * none + * + */ +static void +fore_pci_attach(config_id, unit) + pcici_t config_id; + int unit; +{ + Fore_unit *fup; + vm_offset_t va; + vm_offset_t pa; + pcidi_t device_id; + long val; + int err_count = BOOT_LOOPS; + + /* + * Just checking... + */ + if (unit >= FORE_MAX_UNITS) { + log(LOG_ERR, "%s%d: too many devices\n", + FORE_DEV_NAME, unit); + return; + } + + /* + * Make sure this isn't a duplicate unit + */ + if (fore_units[unit] != NULL) + return; + + /* + * Allocate a new unit structure + */ + fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0); + if (fup == NULL) + return; + + /* + * Start initializing it + */ + fup->fu_unit = unit; + fup->fu_mtu = FORE_IFF_MTU; + fup->fu_pcitag = config_id; + fup->fu_vcc_pool = &fore_vcc_pool; + fup->fu_nif_pool = &fore_nif_pool; + fup->fu_ioctl = fore_atm_ioctl; + fup->fu_instvcc = fore_instvcc; + fup->fu_openvcc = fore_openvcc; + fup->fu_closevcc = fore_closevcc; + fup->fu_output = fore_output; + callout_handle_init(&fup->fu_thandle); + + /* + * Get our device type + */ + device_id = pci_conf_read ( config_id, PCI_ID_REG ); + switch ((device_id >> 16) & 0xffff) { + + case FORE_PCA200E_ID: + fup->fu_config.ac_device = DEV_FORE_PCA200E; + break; + + default: + fup->fu_config.ac_device = DEV_UNKNOWN; + } + + /* + * Map RAM + */ + if ((pci_map_mem(config_id, PCA200E_PCI_MEMBASE, &va, &pa)) == 0) { + log(LOG_ERR, "%s%d: unable to map memory\n", + FORE_DEV_NAME, unit); + goto failed; + } + fup->fu_ram = (Fore_mem *)va; + fup->fu_ramsize = PCA200E_RAM_SIZE; + fup->fu_mon = (Mon960 *)(fup->fu_ram + MON960_BASE); + fup->fu_ctlreg = (Fore_reg *)(va + PCA200E_HCR_OFFSET); + fup->fu_imask = (Fore_reg *)(va + PCA200E_IMASK_OFFSET); + fup->fu_psr = (Fore_reg *)(va + PCA200E_PSR_OFFSET); + + /* + * Convert Endianess of Slave RAM accesses + */ + val = pci_conf_read(config_id, PCA200E_PCI_MCTL); + val |= PCA200E_MCTL_SWAP; + pci_conf_write(config_id, PCA200E_PCI_MCTL, val); + + /* + * Map interrupt in + */ + if ( !pci_map_int( config_id, fore_intr, fup, &net_imask ) ) { + log(LOG_ERR, "%s%d: unable to map interrupt\n", + FORE_DEV_NAME, unit); + goto failed; + } + + /* + * Poke the hardware - boot the CP and prepare it for downloading + */ + fore_reset(fup); + + /* + * Wait for the monitor to perform self-test + */ + while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) { + if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) { + log(LOG_ERR, "%s%d: failed self-test\n", + FORE_DEV_NAME, unit); + goto failed; + } else if ( --err_count == 0 ) { + log(LOG_ERR, "%s%d: unable to boot - status=0x%x\n", + FORE_DEV_NAME, unit, + CP_READ(fup->fu_mon->mon_bstat)); + goto failed; + } + DELAY ( BOOT_DELAY ); + } + + /* + * Setup the adapter config info - at least as much as we can + */ + fup->fu_config.ac_vendor = VENDOR_FORE; + fup->fu_config.ac_vendapi = VENDAPI_FORE_1; + fup->fu_config.ac_media = MEDIA_OC3C; + fup->fu_pif.pif_pcr = ATM_PCR_OC3C; + fup->fu_config.ac_bustype = BUS_PCI; + fup->fu_config.ac_busslot = config_id->bus << 8 | config_id->slot; + + /* + * Save device ram info for user-level programs + */ + fup->fu_config.ac_ram = (long)fup->fu_ram; + fup->fu_config.ac_ramsize = fup->fu_ramsize; + + /* + * Set device capabilities + */ + fup->fu_pif.pif_maxvpi = FORE_MAX_VPI; + fup->fu_pif.pif_maxvci = FORE_MAX_VCI; + + /* + * Register this interface with ATM core services + */ + if ( atm_physif_register + ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 ) + { + /* + * Registration failed - back everything out + */ + goto failed; + } + + fore_units[unit] = fup; + fore_nunits++; + +#if BSD >= 199506 + /* + * Add hook to our shutdown function + */ + at_shutdown(fore_pci_shutdown, fup, SHUTDOWN_POST_SYNC); +#endif + + /* + * Initialize the CP microcode program. + */ + fore_initialize(fup); + + return; + +failed: + /* + * Unattach the device from the system + */ + fore_unattach(fup); + + /* + * Free any Fore-specific device resources + */ + fore_interface_free(fup); + + atm_dev_free(fup); + + return; +} + + +#if BSD < 199506 +/* + * Device shutdown routine + * + * Arguments: + * kdc pointer to device's configuration table + * force forced shutdown flag + * + * Returns: + * none + * + */ +static int +fore_pci_shutdown(kdc, force) + struct kern_devconf *kdc; + int force; +{ + Fore_unit *fup; + + if (kdc->kdc_unit < fore_nunits) { + + fup = fore_units[kdc->kdc_unit]; + if (fup != NULL) { + fore_reset(fup); + } + } + + (void) dev_detach(kdc); + return (0); +} +#else +/* + * Device shutdown routine + * + * Arguments: + * howto type of shutdown + * fup pointer to device unit structure + * + * Returns: + * none + * + */ +static void +fore_pci_shutdown(howto, fup) + int howto; + void *fup; +{ + + fore_reset((Fore_unit *) fup); + + return; +} +#endif /* BSD < 199506 */ +#endif /* __FreeBSD__ */ + + +/* + * Device unattach routine + * + * Reset the physical device, remove any pending timeouts, + * unmap any register sets, and unregister any interrupts. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_unattach(fup) + Fore_unit *fup; +{ +#ifdef sun + struct dev_info *devinfo_p = fup->fu_devinfo; + struct dev_reg *dev_reg_p; + struct dev_intr *dev_intr_p; +#endif + int i; + + + /* + * Reset the board and return it to cold_start state. + * Hopefully, this will prevent use of resources as + * we're trying to free things up. + */ + fore_reset(fup); + + /* + * Lock out all device interrupts + */ + DEVICE_LOCK((Cmn_unit *)fup); + + /* + * Remove any pending timeout()'s + */ + (void)untimeout((KTimeout_ret(*) __P((void *)))fore_initialize, + (void *)fup, fup->fu_thandle); + +#ifdef sun + /* + * Remove any mappings of the device + */ + for ( dev_reg_p = devinfo_p->devi_reg, i = 1; + i <= devinfo_p->devi_nreg; i++, ++dev_reg_p ) + { + if ( dev_reg_p == NULL ) + { + /* + * Can't happen... + */ + break; + } + + /* + * Each device type has different register sets + */ + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + + switch ( i ) + { + /* + * Host Control Register (HCR) + */ + case 1: + unmap_regs((addr_t)fup->fu_ctlreg, + sizeof(Fore_reg)); + break; + + /* + * SBus Burst Transfer Configuration Register + */ + case 2: + /* + * Not used + */ + break; + + /* + * SBus Interrupt Level Select Register + */ + case 3: + unmap_regs((addr_t)fup->fu_intlvl, + sizeof(Fore_reg)); + break; + + /* + * i960 RAM + */ + case 4: + unmap_regs((addr_t)fup->fu_ram, + fup->fu_ramsize); + break; + } + break; + + case DEV_FORE_SBA200: + + switch ( i ) + { + /* + * Board Control Register (BCR) + */ + case 1: + unmap_regs((addr_t)fup->fu_ctlreg, + sizeof(Fore_reg)); + break; + + /* + * i960 RAM + */ + case 2: + unmap_regs((addr_t)fup->fu_ram, + fup->fu_ramsize); + break; + } + break; +#endif /* FORE_SBUS */ + } + } + + /* + * Remove the interrupt vector(s) + */ + dev_intr_p = devinfo_p->devi_intr; + for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p ) + { + if ( dev_intr_p == NULL ) + { + /* + * Can't happen... + */ + break; + } + (void) remintr ( dev_intr_p->int_pri, fore_poll ); + } +#endif /* sun */ + +#ifdef __FreeBSD__ + /* + * Unmap the device interrupt + */ + (void) pci_unmap_int(fup->fu_pcitag); + + /* + * Unmap memory + */ +#ifdef notdef + (void) pci_unmap_mem(fup->fu_pcitag, PCA200E_PCI_MEMBASE); +#endif +#endif /* __FreeBSD__ */ + + DEVICE_UNLOCK((Cmn_unit *)fup); +} + + +/* + * Device reset routine + * + * Reset the physical device + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_reset(fup) + Fore_unit *fup; +{ + int s = splimp(); + + /* + * Reset the board and return it to cold_start state + */ + if (fup->fu_mon) + fup->fu_mon->mon_bstat = CP_WRITE(BOOT_COLDSTART); + + if (fup->fu_ctlreg) { + + switch (fup->fu_config.ac_device) { + +#ifdef FORE_SBUS + case DEV_FORE_SBA200E: + /* + * Reset i960 by setting and clearing RESET + */ + SBA200E_HCR_INIT(*fup->fu_ctlreg, SBA200E_RESET); + SBA200E_HCR_CLR(*fup->fu_ctlreg, SBA200E_RESET); + break; + + case DEV_FORE_SBA200: + /* + * Reset i960 by setting and clearing RESET + * + * SBA200 will NOT reset if bit is OR'd in! + */ + *fup->fu_ctlreg = SBA200_RESET; + *fup->fu_ctlreg = SBA200_RESET_CLR; + break; +#endif /* FORE_SBUS */ +#ifdef FORE_PCI + case DEV_FORE_PCA200E: + /* + * Reset i960 by setting and clearing RESET + */ + PCA200E_HCR_INIT(*fup->fu_ctlreg, PCA200E_RESET); + DELAY(10000); + PCA200E_HCR_CLR(*fup->fu_ctlreg, PCA200E_RESET); + break; + +#endif + } + } + + (void) splx(s); + return; +} + + +#ifndef ATM_LINKED +/* + ******************************************************************* + * + * Loadable Module Support + * + ******************************************************************* + */ + +/* + * Generic module load processing + * + * This function is called by an OS-specific function when this + * module is being loaded. + * + * Arguments: + * none + * + * Returns: + * 0 load was successful + * errno load failed - reason indicated + * + */ +static int +fore_doload() +{ + int err = 0; + + /* + * Start us up + */ + err = fore_start(); + if (err) + /* Problems, clean up */ + (void)fore_stop(); + + return (err); +} + + +/* + * Generic module unload processing + * + * This function is called by an OS-specific function when this + * module is being unloaded. + * + * Arguments: + * none + * + * Returns: + * 0 unload was successful + * errno unload failed - reason indicated + * + */ +static int +fore_dounload() +{ + int err = 0; + + /* + * OK, try to clean up our mess + */ + err = fore_stop(); + + return (err); +} + + +#ifdef sun +/* + * Loadable driver description + */ +static struct vdldrv fore_drv = { + VDMAGIC_DRV, /* Device Driver */ + "fore_mod", /* name */ + &fore_ops, /* dev_ops */ + NULL, /* bdevsw */ + NULL, /* cdevsw */ + 0, /* blockmajor */ + 0 /* charmajor */ +}; + + +/* + * Loadable module support entry point + * + * This is the routine called by the vd driver for all loadable module + * functions for this pseudo driver. This routine name must be specified + * on the modload(1) command. This routine will be called whenever the + * modload(1), modunload(1) or modstat(1) commands are issued for this + * module. + * + * Arguments: + * cmd vd command code + * vdp pointer to vd driver's structure + * vdi pointer to command-specific vdioctl_* structure + * vds pointer to status structure (VDSTAT only) + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +int +fore_mod(cmd, vdp, vdi, vds) + int cmd; + struct vddrv *vdp; + caddr_t vdi; + struct vdstat *vds; +{ + int err = 0; + + switch (cmd) { + + case VDLOAD: + /* + * Module Load + * + * We dont support any user configuration + */ + err = fore_doload(); + if (err == 0) + /* Let vd driver know about us */ + vdp->vdd_vdtab = (struct vdlinkage *)&fore_drv; + break; + + case VDUNLOAD: + /* + * Module Unload + */ + err = fore_dounload(); + break; + + case VDSTAT: + /* + * Module Status + */ + + /* Not much to say at the moment */ + + break; + + default: + log(LOG_ERR, "fore_mod: Unknown vd command 0x%x\n", cmd); + err = EINVAL; + } + + return (err); +} +#endif /* sun */ + +#ifdef __FreeBSD__ +#ifdef notdef + +/* + * Driver entry points + */ +static struct cdevsw fore_cdev = { + (d_open_t *)enodev, /* open */ + (d_close_t *)enodev, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* stop */ + NULL, /* reset */ + NULL, /* devtotty */ + NULL, /* select */ + NULL, /* mmap */ + NULL /* strategy */ +}; + + +/* + * Loadable device driver module description + */ +#if BSD < 199506 +MOD_DEV("fore_mod", LM_DT_CHAR, -1, (void *)&fore_cdev); +#else +MOD_DEV(fore, LM_DT_CHAR, -1, (void *)&fore_cdev); +#endif + + +/* + * Loadable module support "load" entry point + * + * This is the routine called by the lkm driver whenever the + * modload(1) command is issued for this module. + * + * Arguments: + * lkmtp pointer to lkm drivers's structure + * cmd lkm command code + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +static int +fore_load(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(fore_doload()); +} + + +/* + * Loadable module support "unload" entry point + * + * This is the routine called by the lkm driver whenever the + * modunload(1) command is issued for this module. + * + * Arguments: + * lkmtp pointer to lkm drivers's structure + * cmd lkm command code + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +static int +fore_unload(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(fore_dounload()); +} + + +/* + * Loadable module support entry point + * + * This is the routine called by the lkm driver for all loadable module + * functions for this driver. This routine name must be specified + * on the modload(1) command. This routine will be called whenever the + * modload(1), modunload(1) or modstat(1) commands are issued for this + * module. + * + * Arguments: + * lkmtp pointer to lkm drivers's structure + * cmd lkm command code + * ver lkm version + * + * Returns: + * 0 command was successful + * errno command failed - reason indicated + * + */ +int +fore_mod(lkmtp, cmd, ver) + struct lkm_table *lkmtp; + int cmd; + int ver; +{ +#if BSD < 199506 + DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, nosys); +#else + DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, lkm_nullcmd); +#endif +} +#endif /* notdef */ +#endif /* __FreeBSD__ */ + +#endif /* ATM_LINKED */ + diff --git a/sys/dev/hfa/fore_output.c b/sys/dev/hfa/fore_output.c new file mode 100644 index 0000000..59c82c9 --- /dev/null +++ b/sys/dev/hfa/fore_output.c @@ -0,0 +1,415 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_output.c,v 1.7 1998/02/19 20:10:34 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * PDU output processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_output.c,v 1.7 1998/02/19 20:10:34 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +static KBuffer * fore_xmit_segment __P((Fore_unit *, KBuffer *, + H_xmit_queue *, u_int *, u_int *)); + + +/* + * Output a PDU + * + * This function is called via the common driver code after receiving a + * stack *_DATA* command. The common code has already validated most of + * the request so we just need to check a few more Fore-specific details. + * Then we just build a transmit descriptor request for the PDU and issue + * the command to the CP. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * m pointer to output PDU buffer chain head + * + * Returns: + * none + * + */ +void +fore_output(cup, cvp, m) + Cmn_unit *cup; + Cmn_vcc *cvp; + KBuffer *m; +{ + Fore_unit *fup = (Fore_unit *)cup; + Fore_vcc *fvp = (Fore_vcc *)cvp; + struct vccb *vcp; + H_xmit_queue *hxp; + Xmit_queue *cqp; + Xmit_descr *xdp; + u_int retry, nsegs, pdulen; + int s; + +#ifdef DIAGNOSTIC + if (atm_dev_print) + atm_dev_pdu_print(cup, cvp, m, "fore_output"); +#endif + + vcp = fvp->fv_connvc->cvc_vcc; + + /* + * If we're still waiting for activation to finish, delay for + * a little while before we toss the PDU + */ + if (fvp->fv_state == CVS_INITED) { + retry = 3; + while (retry-- && (fvp->fv_state == CVS_INITED)) + DELAY(1000); + if (fvp->fv_state != CVS_ACTIVE) { + /* + * Activation still hasn't finished, oh well.... + */ + fup->fu_stats->st_drv.drv_xm_notact++; + vcp->vc_oerrors++; + if (vcp->vc_nif) + vcp->vc_nif->nif_if.if_oerrors++; + KB_FREEALL(m); + return; + } + } + + /* + * Queue PDU at end of transmit queue + * + * If queue is full we'll delay a bit before tossing the PDU + */ + s = splnet(); + hxp = fup->fu_xmit_tail; + if (!((*hxp->hxq_status) & QSTAT_FREE)) { + + fup->fu_stats->st_drv.drv_xm_full++; + retry = 3; + do { + DELAY(1000); + + DEVICE_LOCK((Cmn_unit *)fup); + fore_xmit_drain(fup); + DEVICE_UNLOCK((Cmn_unit *)fup); + + } while (--retry && (!((*hxp->hxq_status) & QSTAT_FREE))); + + if (!((*hxp->hxq_status) & QSTAT_FREE)) { + /* + * Queue is still full, bye-bye PDU + */ + fup->fu_pif.pif_oerrors++; + vcp->vc_oerrors++; + if (vcp->vc_nif) + vcp->vc_nif->nif_if.if_oerrors++; + KB_FREEALL(m); + (void) splx(s); + return; + } + } + + /* + * We've got a free transmit queue entry + */ + + /* + * Now build the transmit segment descriptors for this PDU + */ + m = fore_xmit_segment(fup, m, hxp, &nsegs, &pdulen); + if (m == NULL) { + /* + * The build failed, buffer chain has been freed + */ + vcp->vc_oerrors++; + if (vcp->vc_nif) + vcp->vc_nif->nif_if.if_oerrors++; + (void) splx(s); + return; + } + + /* + * Set up the descriptor header + */ + xdp = hxp->hxq_descr; + xdp->xd_cell_hdr = ATM_HDR_SET(vcp->vc_vpi, vcp->vc_vci, 0, 0); + xdp->xd_spec = XDS_SET_SPEC(0, fvp->fv_aal, nsegs, pdulen); + xdp->xd_rate = FORE_DEF_RATE; + + /* + * Everything is ready to go, so officially claim the host queue + * entry and setup the CP-resident queue entry. The CP will grab + * the PDU when the descriptor pointer is set. + */ + fup->fu_xmit_tail = hxp->hxq_next; + hxp->hxq_buf = m; + hxp->hxq_vcc = fvp; + (*hxp->hxq_status) = QSTAT_PENDING; + cqp = hxp->hxq_cpelem; + cqp->cq_descr = (CP_dma) + CP_WRITE((u_long)hxp->hxq_descr_dma | XMIT_SEGS_TO_BLKS(nsegs)); + + (void) splx(s); + + /* + * See if there are any completed queue entries + */ + DEVICE_LOCK((Cmn_unit *)fup); + fore_xmit_drain(fup); + DEVICE_UNLOCK((Cmn_unit *)fup); + + return; +} + + +/* + * Build Transmit Segment Descriptors + * + * This function will take a supplied buffer chain of data to be transmitted + * and build the transmit segment descriptors for the data. This will include + * the dreaded operation of ensuring that the data for each transmit segment + * is full-word aligned and (except for the last segment) is an integral number + * of words in length. If the data isn't already aligned and sized as + * required, then the data must be shifted (copied) into place - a sure + * performance killer. Note that we rely on the fact that all buffer data + * areas are allocated with (at least) full-word alignments/lengths. + * + * If any errors are encountered, the buffer chain will be freed. + * + * Arguments: + * fup pointer to device unit + * m pointer to output PDU buffer chain head + * hxp pointer to host transmit queue entry + * segp pointer to return the number of transmit segments + * lenp pointer to return the pdu length + * + * Returns: + * m build successful, pointer to (possibly new) head of + * output PDU buffer chain + * NULL build failed, buffer chain freed + * + */ +static KBuffer * +fore_xmit_segment(fup, m, hxp, segp, lenp) + Fore_unit *fup; + KBuffer *m; + H_xmit_queue *hxp; + u_int *segp; + u_int *lenp; +{ + Xmit_descr *xdp = hxp->hxq_descr; + Xmit_seg_descr *xsp; + H_dma *sdmap; + KBuffer *m0, *m1, *mprev; + caddr_t cp, bfr; + void *dma; + u_int pdulen, nsegs, len, align; + int compressed = 0; + + m0 = m; + +retry: + xsp = xdp->xd_seg; + sdmap = hxp->hxq_dma; + mprev = NULL; + pdulen = 0; + nsegs = 0; + + /* + * Loop thru each buffer in the chain, performing the necessary + * data positioning and then building a segment descriptor for + * that data. + */ + while (m) { + /* + * Get rid of any zero-length buffers + */ + if (KB_LEN(m) == 0) { + if (mprev) { + KB_UNLINK(m, mprev, m1); + } else { + KB_UNLINKHEAD(m, m1); + m0 = m1; + } + m = m1; + continue; + } + + /* + * Make sure we don't try to use too many segments + */ + if (nsegs >= XMIT_MAX_SEGS) { + /* + * Try to compress buffer chain (but only once) + */ + if (compressed) { + KB_FREEALL(m0); + return (NULL); + } + + fup->fu_stats->st_drv.drv_xm_maxpdu++; + + m = atm_dev_compress(m0); + if (m == NULL) { + return (NULL); + } + + /* + * Build segment descriptors for compressed chain + */ + m0 = m; + compressed = 1; + goto retry; + } + + /* + * Get start of data onto full-word alignment + */ + KB_DATASTART(m, cp, caddr_t); + if (align = ((u_int)cp) & (XMIT_SEG_ALIGN - 1)) { + /* + * Gotta slide the data up + */ + fup->fu_stats->st_drv.drv_xm_segnoal++; + bfr = cp - align; + KM_COPY(cp, bfr, KB_LEN(m)); + KB_HEADMOVE(m, -align); + } else { + /* + * Data already aligned + */ + bfr = cp; + } + + /* + * Now work on getting the data length correct + */ + len = KB_LEN(m); + while ((align = (len & (XMIT_SEG_ALIGN - 1))) && + (m1 = KB_NEXT(m))) { + + /* + * Have to move some data from following buffer(s) + * to word-fill this buffer + */ + u_int ncopy = MIN(XMIT_SEG_ALIGN - align, KB_LEN(m1)); + + if (ncopy) { + /* + * Move data to current buffer + */ + caddr_t dest; + + fup->fu_stats->st_drv.drv_xm_seglen++; + KB_DATASTART(m1, cp, caddr_t); + dest = bfr + len; + KB_HEADADJ(m1, -ncopy); + KB_TAILADJ(m, ncopy); + len += ncopy; + while (ncopy--) { + *dest++ = *cp++; + } + } + + /* + * If we've drained the buffer, free it + */ + if (KB_LEN(m1) == 0) { + KBuffer *m2; + + KB_UNLINK(m1, m, m2); + } + } + + /* + * Finally, build the segment descriptor + */ + + /* + * Round last segment to fullword length (if needed) + */ + if (len & (XMIT_SEG_ALIGN - 1)) + xsp->xsd_len = KB_LEN(m) = + (len + XMIT_SEG_ALIGN) & ~(XMIT_SEG_ALIGN - 1); + else + xsp->xsd_len = KB_LEN(m) = len; + + /* + * Get a DMA address for the data + */ + dma = DMA_GET_ADDR(bfr, xsp->xsd_len, XMIT_SEG_ALIGN, 0); + if (dma == NULL) { + + fup->fu_stats->st_drv.drv_xm_segdma++; + KB_FREEALL(m0); + return (NULL); + } + + /* + * Now we're really ready to call it a segment + */ + *sdmap++ = xsp->xsd_buffer = (H_dma) dma; + + /* + * Bump counters and get ready for next buffer + */ + pdulen += len; + nsegs++; + xsp++; + mprev = m; + m = KB_NEXT(m); + } + + /* + * Validate PDU length + */ + if (pdulen > XMIT_MAX_PDULEN) { + fup->fu_stats->st_drv.drv_xm_maxpdu++; + KB_FREEALL(m0); + return (NULL); + } + + /* + * Return the good news to the caller + */ + *segp = nsegs; + *lenp = pdulen; + + return (m0); +} + diff --git a/sys/dev/hfa/fore_receive.c b/sys/dev/hfa/fore_receive.c new file mode 100644 index 0000000..f9a9d19 --- /dev/null +++ b/sys/dev/hfa/fore_receive.c @@ -0,0 +1,582 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_receive.c,v 1.10 1998/07/17 20:19:35 root Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Receive queue management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_receive.c,v 1.10 1998/07/17 20:19:35 root Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +static void fore_recv_stack __P((void *, KBuffer *)); + + +/* + * Allocate Receive Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * 0 allocations successful + * else allocation failed + */ +int +fore_recv_allocate(fup) + Fore_unit *fup; +{ + caddr_t memp; + + /* + * Allocate non-cacheable memory for receive status words + */ + memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN, + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_recv_stat = (Q_status *) memp; + + memp = DMA_GET_ADDR(fup->fu_recv_stat, sizeof(Q_status) * RECV_QUELEN, + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_recv_statd = (Q_status *) memp; + + /* + * Allocate memory for receive descriptors + */ + memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN, + RECV_DESCR_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_recv_desc = (Recv_descr *) memp; + + memp = DMA_GET_ADDR(fup->fu_recv_desc, + sizeof(Recv_descr) * RECV_QUELEN, RECV_DESCR_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_recv_descd = (Recv_descr *) memp; + + return (0); +} + + +/* + * Receive Queue Initialization + * + * Allocate and initialize the host-resident receive queue structures + * and then initialize the CP-resident queue structures. + * + * Called at interrupt level. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_recv_initialize(fup) + Fore_unit *fup; +{ + Aali *aap = fup->fu_aali; + Recv_queue *cqp; + H_recv_queue *hrp; + Recv_descr *rdp; + Recv_descr *rdp_dma; + Q_status *qsp; + Q_status *qsp_dma; + int i; + + /* + * Point to CP-resident receive queue + */ + cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q)); + + /* + * Point to host-resident receive queue structures + */ + hrp = fup->fu_recv_q; + qsp = fup->fu_recv_stat; + qsp_dma = fup->fu_recv_statd; + rdp = fup->fu_recv_desc; + rdp_dma = fup->fu_recv_descd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < RECV_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hrp->hrq_cpelem = cqp; + hrp->hrq_status = qsp; + hrp->hrq_descr = rdp; + hrp->hrq_descr_dma = rdp_dma; + if (i == (RECV_QUELEN - 1)) + hrp->hrq_next = fup->fu_recv_q; + else + hrp->hrq_next = hrp + 1; + + /* + * Now let the CP into the game + */ + cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma); + cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hrp++; + qsp++; + qsp_dma++; + rdp++; + rdp_dma++; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_recv_head = fup->fu_recv_q; + + return; +} + + +/* + * Drain Receive Queue + * + * This function will process all completed entries at the head of the + * receive queue. The received segments will be linked into a received + * PDU buffer chain and it will then be passed up the PDU's VCC stack for + * processing by the next higher protocol layer. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_recv_drain(fup) + Fore_unit *fup; +{ + H_recv_queue *hrp = NULL; + Recv_descr *rdp; + Recv_seg_descr *rsp; + Buf_handle *bhp; + Fore_vcc *fvp; + struct vccb *vcp; + KBuffer *m, *mhead, *mtail; + caddr_t cp; + u_long hdr, nsegs; + u_int seglen, type0; + int i, pdulen, retries = 0, error; + + /* Silence the compiler */ + mtail = NULL; + type0 = 0; + + /* + * Process each completed entry + */ +retry: + while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) { + + /* + * Get completed entry's receive descriptor + */ + hrp = fup->fu_recv_head; + rdp = hrp->hrq_descr; + +#ifdef VAC + /* + * Cache flush receive descriptor + */ + if (vac) { + vac_flush((addr_t)rdp, sizeof(Recv_descr)); + } +#endif + + hdr = rdp->rd_cell_hdr; + nsegs = rdp->rd_nsegs; + + pdulen = 0; + error = 0; + mhead = NULL; + + /* + * Locate incoming VCC for this PDU + */ + fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup, + ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN); + + /* + * Check for a receive error + * + * Apparently the receive descriptor itself contains valid + * information, but the received pdu data is probably bogus. + * We'll arrange for the receive buffer segments to be tossed. + */ + if (*hrp->hrq_status & QSTAT_ERROR) { + + fup->fu_pif.pif_ierrors++; + if (fvp) { + vcp = fvp->fv_connvc->cvc_vcc; + vcp->vc_ierrors++; + if (vcp->vc_nif) + vcp->vc_nif->nif_if.if_ierrors++; + } + ATM_DEBUG1("fore receive error: hdr=0x%x\n", hdr); + error = 1; + } + + /* + * Build PDU buffer chain from receive segments + */ + for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) { + + bhp = rsp->rsd_handle; + seglen = rsp->rsd_len; + + /* + * Remove buffer from our supplied queue and get + * to the underlying buffer + */ + switch (bhp->bh_type) { + + case BHT_S1_SMALL: + DEQUEUE(bhp, Buf_handle, bh_qelem, + fup->fu_buf1s_bq); + fup->fu_buf1s_cnt--; + m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF); + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0); + break; + + case BHT_S1_LARGE: + DEQUEUE(bhp, Buf_handle, bh_qelem, + fup->fu_buf1l_bq); + fup->fu_buf1l_cnt--; + m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF); + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0); + break; + + default: + log(LOG_ERR, + "fore_recv_drain: bhp=0x%x type=0x%x\n", + (int)bhp, bhp->bh_type); + panic("fore_recv_drain: bad buffer type"); + } + + /* + * Toss any zero-length or receive error buffers + */ + if ((seglen == 0) || error) { + KB_FREEALL(m); + continue; + } + + /* + * Link buffer into chain + */ + if (mhead == NULL) { + type0 = bhp->bh_type; + KB_LINKHEAD(m, mhead); + mhead = m; + } else { + KB_LINK(m, mtail); + } + KB_LEN(m) = seglen; + pdulen += seglen; + mtail = m; + + /* + * Flush received buffer data + */ +#ifdef VAC + if (vac) { + addr_t dp; + + KB_DATASTART(m, dp, addr_t); + vac_pageflush(dp); + } +#endif + } + + /* + * Make sure we've got a non-null PDU + */ + if (mhead == NULL) { + goto free_ent; + } + + /* + * We only support user data PDUs (for now) + */ + if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) { + KB_FREEALL(mhead); + goto free_ent; + } + + /* + * Toss the data if there's no VCC + */ + if (fvp == NULL) { + fup->fu_stats->st_drv.drv_rv_novcc++; + KB_FREEALL(mhead); + goto free_ent; + } + +#ifdef DIAGNOSTIC + if (atm_dev_print) + atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp, + mhead, "fore_recv"); +#endif + + /* + * Make sure we have our queueing headroom at the front + * of the buffer chain + */ + if (type0 != BHT_S1_SMALL) { + + /* + * Small buffers already have headroom built-in, but + * if CP had to use a large buffer for the first + * buffer, then we have to allocate a buffer here to + * contain the headroom. + */ + fup->fu_stats->st_drv.drv_rv_nosbf++; + + KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) { + fup->fu_stats->st_drv.drv_rv_nomb++; + KB_FREEALL(mhead); + goto free_ent; + } + + /* + * Put new buffer at head of PDU chain + */ + KB_LINKHEAD(m, mhead); + KB_LEN(m) = 0; + KB_HEADSET(m, BUF1_SM_DOFF); + mhead = m; + } + + /* + * It looks like we've got a valid PDU - count it quick!! + */ + KB_PLENSET(mhead, pdulen); + fup->fu_pif.pif_ipdus++; + fup->fu_pif.pif_ibytes += pdulen; + vcp = fvp->fv_connvc->cvc_vcc; + vcp->vc_ipdus++; + vcp->vc_ibytes += pdulen; + if (vcp->vc_nif) { + vcp->vc_nif->nif_ibytes += pdulen; + vcp->vc_nif->nif_if.if_ipackets++; +#if (defined(BSD) && (BSD >= 199103)) + vcp->vc_nif->nif_if.if_ibytes += pdulen; +#endif + } + + /* + * The STACK_CALL needs to happen at splnet() in order + * for the stack sequence processing to work. Schedule an + * interrupt queue callback at splnet() since we are + * currently at device level. + */ + + /* + * Prepend callback function pointer and token value to buffer. + * We have already guaranteed that the space is available + * in the first buffer. + */ + KB_HEADADJ(mhead, sizeof(atm_intr_func_t) + sizeof(int)); + KB_DATASTART(mhead, cp, caddr_t); + *((atm_intr_func_t *)cp) = fore_recv_stack; + cp += sizeof(atm_intr_func_t); + *((void **)cp) = (void *)fvp; + + /* + * Schedule callback + */ + if (!IF_QFULL(&atm_intrq)) { + IF_ENQUEUE(&atm_intrq, mhead); + SCHED_ATM; + } else { + fup->fu_stats->st_drv.drv_rv_ifull++; + KB_FREEALL(mhead); + goto free_ent; + } + +free_ent: + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hrp->hrq_status = QSTAT_FREE; + hrp->hrq_cpelem->cq_descr = + (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma); + fup->fu_recv_head = hrp->hrq_next; + } + + /* + * Nearly all of the interrupts generated by the CP will be due + * to PDU reception. However, we may receive an interrupt before + * the CP has completed the status word DMA to host memory. Thus, + * if we haven't processed any PDUs during this interrupt, we will + * wait a bit for completed work on the receive queue, rather than + * having to field an extra interrupt very soon. + */ + if (hrp == NULL) { + if (++retries <= FORE_RECV_RETRY) { + DELAY(FORE_RECV_DELAY); + goto retry; + } + } + + return; +} + + +/* + * Pass Incoming PDU up Stack + * + * This function is called via the core ATM interrupt queue callback + * set in fore_recv_drain(). It will pass the supplied incoming + * PDU up the incoming VCC's stack. + * + * Called at splnet. + * + * Arguments: + * tok token to identify stack instantiation + * m pointer to incoming PDU buffer chain + * + * Returns: + * none + */ +static void +fore_recv_stack(tok, m) + void *tok; + KBuffer *m; +{ + Fore_vcc *fvp = (Fore_vcc *)tok; + int err; + + /* + * Send the data up the stack + */ + STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper, + fvp->fv_toku, fvp->fv_connvc, (int)m, 0, err); + if (err) + KB_FREEALL(m); + + return; +} + + +/* + * Free Receive Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_recv_free(fup) + Fore_unit *fup; +{ + /* + * We'll just let fore_buf_free() take care of freeing any + * buffers sitting on the receive queue (which are also still + * on the fu_*_bq queue). + */ + if (fup->fu_flags & CUF_INITED) { + } + + /* + * Free the status words + */ + if (fup->fu_recv_stat) { + if (fup->fu_recv_statd) { + DMA_FREE_ADDR(fup->fu_recv_stat, fup->fu_recv_statd, + sizeof(Q_status) * RECV_QUELEN, + ATM_DEV_NONCACHE); + } + atm_dev_free((void *)fup->fu_recv_stat); + fup->fu_recv_stat = NULL; + fup->fu_recv_statd = NULL; + } + + /* + * Free the receive descriptors + */ + if (fup->fu_recv_desc) { + if (fup->fu_recv_descd) { + DMA_FREE_ADDR(fup->fu_recv_desc, fup->fu_recv_descd, + sizeof(Recv_descr) * RECV_QUELEN, 0); + } + atm_dev_free(fup->fu_recv_desc); + fup->fu_recv_desc = NULL; + fup->fu_recv_descd = NULL; + } + + return; +} + diff --git a/sys/dev/hfa/fore_slave.h b/sys/dev/hfa/fore_slave.h new file mode 100644 index 0000000..05e7b5b --- /dev/null +++ b/sys/dev/hfa/fore_slave.h @@ -0,0 +1,191 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_slave.h,v 1.5 1997/08/22 19:45:50 jpt Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Slave Interface definitions + * + */ + +#ifndef _FORE_SLAVE_H +#define _FORE_SLAVE_H + +/* + * This file contains the (mostly hardware) definitions for each of the + * supported 200-series slave interfaces. + */ + + +/* + * Structure defining the supported FORE 200-series interfaces + */ +struct fore_device { + char *fd_name; /* Device name (from PROM) */ + Atm_device fd_devtyp; /* Device type */ +}; +typedef struct fore_device Fore_device; + + + +/* + * Common definitions + * ------------------ + */ +#define MON960_BASE 0x400 /* Address offset of Mon960 */ +#define AALI_BASE 0x4d40 /* Address offset of Aali */ + +typedef volatile unsigned int Fore_reg; /* Slave control register */ +typedef volatile unsigned char Fore_mem; /* Slave memory */ + + +/* + * SBA-200E SBus Slave Interface + * ----------------------------- + */ + +#define SBA200E_PROM_NAME "FORE,sba-200e" + +/* + * SBA-200E Host Control Register (HCR) + */ +#define SBA200E_READ_BITS 0x1ff /* Valid read data bits */ +#define SBA200E_WRITE_BITS 0x01f /* Valid write data bits */ +#define SBA200E_STICKY_BITS 0x013 /* Sticky data bits */ + +/* Read access */ +#define SBA200E_SBUS_INTR_RD 0x100 /* State of SBus interrupt */ +#define SBA200E_TEST_MODE 0x080 /* Device is in test-mode */ +#define SBA200E_IFIFO_FULL 0x040 /* Input FIFO almost full (when 0) */ +#define SBA200E_ESP_HOLD_RD 0x020 /* State of ESP bus hold */ +#define SBA200E_SBUS_ENA_RD 0x010 /* State of SBus interrupt enable */ +#define SBA200E_OFIFO_FULL 0x008 /* Output FIFO almost full */ +#define SBA200E_SELFTEST_FAIL 0x004 /* i960 self-test failed (when 0) */ +#define SBA200E_HOLD_LOCK_RD 0x002 /* State of i960 hold lock signal */ +#define SBA200E_RESET_RD 0x001 /* State of board reset signal */ + +/* Write access - bit set (clear) */ +#define SBA200E_SBUS_ENA 0x010 /* Enable (disable) SBus interrupts */ +#define SBA200E_CLR_SBUS_INTR 0x008 /* Clear SBus interrupt */ +#define SBA200E_I960_INTR 0x004 /* Issue interrupt to i960 */ +#define SBA200E_HOLD_LOCK 0x002 /* Set (clear) i960 hold lock signal */ +#define SBA200E_RESET 0x001 /* Set (clear) board reset signal */ + +#define SBA200E_HCR_INIT(hcr,bits) \ + ((hcr) = (SBA200E_WRITE_BITS & (bits))) +#define SBA200E_HCR_SET(hcr,bits) \ + ((hcr) = (((hcr) & SBA200E_STICKY_BITS) | (bits))) +#define SBA200E_HCR_CLR(hcr,bits) \ + ((hcr) = ((hcr) & (SBA200E_STICKY_BITS ^ (bits)))) + + + +/* + * SBA-200 SBus Slave Interface + * ---------------------------- + */ + +#define SBA200_PROM_NAME "FORE,sba-200" + +/* + * SBA-200 Board Control Register (BCR) + */ +/* Write access - bit set */ +#define SBA200_CLR_SBUS_INTR 0x04 /* Clear SBus interrupt */ +#define SBA200_RESET 0x01 /* Assert board reset signal */ + +/* Write access - bit clear */ +#define SBA200_RESET_CLR 0x00 /* Clear board reset signal */ + + + +/* + * PCA-200E PCI Bus Slave Interface + * -------------------------------- + */ + +/* + * PCI Identifiers + */ +#define FORE_VENDOR_ID 0x1127 +#define FORE_PCA200E_ID 0x0300 + +/* + * PCA-200E PCI Configuration Space + */ +#define PCA200E_PCI_MEMBASE 0x10 /* Memory base address */ +#define PCA200E_PCI_MCTL 0x40 /* Master control */ + +/* + * PCA-200E Address Space + */ +#define PCA200E_RAM_SIZE 0x100000 +#define PCA200E_HCR_OFFSET 0x100000 +#define PCA200E_IMASK_OFFSET 0x100004 +#define PCA200E_PSR_OFFSET 0x100008 +#define PCA200E_MMAP_SIZE 0x10000c + +/* + * PCA-200E Master Control + */ +#define PCA200E_MCTL_SWAP 0x4000 /* Convert Slave endianess */ + +/* + * PCA-200E Host Control Register (HCR) + */ +#define PCA200E_READ_BITS 0x0ff /* Valid read data bits */ +#define PCA200E_WRITE_BITS 0x01f /* Valid write data bits */ +#define PCA200E_STICKY_BITS 0x000 /* Sticky data bits */ + +/* Read access */ +#define PCA200E_TEST_MODE 0x080 /* Device is in test-mode */ +#define PCA200E_IFIFO_FULL 0x040 /* Input FIFO almost full */ +#define PCA200E_ESP_HOLD_RD 0x020 /* State of ESP hold bus */ +#define PCA200E_OFIFO_FULL 0x010 /* Output FIFO almost full */ +#define PCA200E_HOLD_ACK 0x008 /* State of Hold Ack */ +#define PCA200E_SELFTEST_FAIL 0x004 /* i960 self-test failed */ +#define PCA200E_HOLD_LOCK_RD 0x002 /* State of i960 hold lock signal */ +#define PCA200E_RESET_BD 0x001 /* State of board reset signal */ + +/* Write access */ +#define PCA200E_CLR_HBUS_INT 0x010 /* Clear host bus interrupt */ +#define PCA200E_I960_INTRA 0x008 /* Set slave interrupt A */ +#define PCA200E_I960_INTRB 0x004 /* Set slave interrupt B */ +#define PCA200E_HOLD_LOCK 0x002 /* Set (clear) i960 hold lock signal */ +#define PCA200E_RESET 0x001 /* Set (clear) board reset signal */ + +#define PCA200E_HCR_INIT(hcr,bits) \ + ((hcr) = (PCA200E_WRITE_BITS & (bits))) +#define PCA200E_HCR_SET(hcr,bits) \ + ((hcr) = (bits)) +#define PCA200E_HCR_CLR(hcr,bits) \ + ((hcr) = 0) + +#endif /* _FORE_SLAVE_H */ diff --git a/sys/dev/hfa/fore_stats.c b/sys/dev/hfa/fore_stats.c new file mode 100644 index 0000000..a8271a2 --- /dev/null +++ b/sys/dev/hfa/fore_stats.c @@ -0,0 +1,164 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_stats.c,v 1.5 1997/08/22 18:41:21 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Device statistics routines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_stats.c,v 1.5 1997/08/22 18:41:21 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Get device statistics from CP + * + * This function will issue a GET_STATS command to the CP in order to + * initiate the DMA transfer of the CP's statistics structure to the host. + * We will then sleep pending command completion. This must only be called + * from the ioctl system call handler. + * + * Called at splnet. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * 0 stats retrieval successful + * errno stats retrieval failed - reason indicated + * + */ +int +fore_get_stats(fup) + Fore_unit *fup; +{ + H_cmd_queue *hcp; + Cmd_queue *cqp; + int s, sst; + + ATM_DEBUG1("fore_get_stats: fup=0x%x\n", (int)fup); + + /* + * Make sure device has been initialized + */ + if ((fup->fu_flags & CUF_INITED) == 0) { + return (EIO); + } + + /* + * If someone has already initiated a stats request, we'll + * just wait for that one to complete + */ + s = splimp(); + if (fup->fu_flags & FUF_STATCMD) { + +#if (defined(BSD) && (BSD >= 199103)) + sst = tsleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH, "fore", 0); +#else + sst = sleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH); + if (sst != 0) + sst = EINTR; +#endif + (void) splx(s); + return (sst ? sst : fup->fu_stats_ret); + } + + /* + * Limit stats gathering to once a second or so + */ + if (time_second == fup->fu_stats_time) { + (void) splx(s); + return (0); + } else + fup->fu_stats_time = time_second; + + /* + * Queue command at end of command queue + */ + hcp = fup->fu_cmd_tail; + if ((*hcp->hcq_status) & QSTAT_FREE) { + void *dma; + + /* + * Queue entry available, so set our view of things up + */ + hcp->hcq_code = CMD_GET_STATS; + hcp->hcq_arg = NULL; + fup->fu_cmd_tail = hcp->hcq_next; + + /* + * Now set the CP-resident queue entry - the CP will grab + * the command when the op-code is set. + */ + cqp = hcp->hcq_cpelem; + (*hcp->hcq_status) = QSTAT_PENDING; + + dma = DMA_GET_ADDR(fup->fu_stats, sizeof(Fore_cp_stats), + FORE_STATS_ALIGN, 0); + if (dma == NULL) { + fup->fu_stats->st_drv.drv_cm_nodma++; + (void) splx(s); + return (EIO); + } + fup->fu_statsd = dma; + cqp->cmdq_stats.stats_buffer = (CP_dma) CP_WRITE(dma); + + fup->fu_flags |= FUF_STATCMD; + cqp->cmdq_stats.stats_cmd = + CP_WRITE(CMD_GET_STATS | CMD_INTR_REQ); + + /* + * Now wait for command to finish + */ +#if (defined(BSD) && (BSD >= 199103)) + sst = tsleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH, "fore", 0); +#else + sst = sleep((caddr_t)&fup->fu_stats, PWAIT|PCATCH); + if (sst != 0) + sst = EINTR; +#endif + (void) splx(s); + return (sst ? sst : fup->fu_stats_ret); + + } else { + /* + * Command queue full + */ + fup->fu_stats->st_drv.drv_cm_full++; + (void) splx(s); + return (EIO); + } +} + diff --git a/sys/dev/hfa/fore_stats.h b/sys/dev/hfa/fore_stats.h new file mode 100644 index 0000000..3803ddd --- /dev/null +++ b/sys/dev/hfa/fore_stats.h @@ -0,0 +1,83 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_stats.h,v 1.3 1997/05/06 22:10:21 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Driver statistics definitions + * + */ + +#ifndef _FORE_STATS_H +#define _FORE_STATS_H + + +/* + * Fore Driver Statistics + */ +struct Stats_driver { + u_long drv_xm_notact; /* PDU drops out - VCC not active */ + u_long drv_xm_full; /* Xmit queue full */ + u_long drv_xm_maxpdu; /* PDU drops out - max segment/size */ + u_long drv_xm_segnoal; /* Non-aligned segments */ + u_long drv_xm_seglen; /* Padded length segments */ + u_long drv_xm_segdma; /* PDU drops out - no dma address */ + u_long drv_rv_novcc; /* PDU drops in - no VCC */ + u_long drv_rv_nosbf; /* No small buffers */ + u_long drv_rv_nomb; /* PDU drops in - no buffer */ + u_long drv_rv_ifull; /* PDU drops in - intr queue full */ + u_long drv_bf_segdma; /* Buffer supply - no dma address */ + u_long drv_cm_full; /* Command queue full */ + u_long drv_cm_nodma; /* Command failed - no dma address */ +}; +typedef struct Stats_driver Stats_driver; + + +/* + * Fore Device Statistics + * + * This structure is used by pass all statistics (including CP maintained + * and driver maintained) data to user space (atm command). + */ +struct fore_stats { + Fore_cp_stats st_cpstat; /* CP stats */ + Stats_driver st_drv; /* Driver maintained stats */ +}; +typedef struct fore_stats Fore_stats; + +#define st_taxi st_cpstat.st_cp_taxi +#define st_oc3 st_cpstat.st_cp_oc3 +#define st_atm st_cpstat.st_cp_atm +#define st_aal0 st_cpstat.st_cp_aal0 +#define st_aal4 st_cpstat.st_cp_aal4 +#define st_aal5 st_cpstat.st_cp_aal5 +#define st_misc st_cpstat.st_cp_misc + +#endif /* _FORE_STATS_H */ diff --git a/sys/dev/hfa/fore_timer.c b/sys/dev/hfa/fore_timer.c new file mode 100644 index 0000000..e0d0c0e --- /dev/null +++ b/sys/dev/hfa/fore_timer.c @@ -0,0 +1,97 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_timer.c,v 1.5 1997/05/06 22:10:24 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Timer processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_timer.c,v 1.5 1997/05/06 22:10:24 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Process a Fore timer tick + * + * This function is called every FORE_TIME_TICK seconds in order to update + * all of the unit watchdog timers. + * + * Called at splnet. + * + * Arguments: + * tip pointer to fore timer control block + * + * Returns: + * none + * + */ +void +fore_timeout(tip) + struct atm_time *tip; +{ + Fore_unit *fup; + int i; + + + /* + * Schedule next timeout + */ + atm_timeout(&fore_timer, ATM_HZ * FORE_TIME_TICK, fore_timeout); + + /* + * Run through all units, updating each active timer. + * If an expired timer is found, notify that unit. + */ + for (i = 0; i < fore_nunits; i++) { + + if ((fup = fore_units[i]) == NULL) + continue; + + /* + * Decrement timer, if it's active + */ + if (fup->fu_timer && (--fup->fu_timer == 0)) { + + /* + * Timeout occurred - go check out the queues + */ + ATM_DEBUG0("fore_timeout\n"); + DEVICE_LOCK((Cmn_unit *)fup); + fore_watchdog(fup); + DEVICE_UNLOCK((Cmn_unit *)fup); + } + } +} + diff --git a/sys/dev/hfa/fore_transmit.c b/sys/dev/hfa/fore_transmit.c new file mode 100644 index 0000000..744e775 --- /dev/null +++ b/sys/dev/hfa/fore_transmit.c @@ -0,0 +1,371 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_transmit.c,v 1.8 1998/07/17 20:19:37 root Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Transmit queue management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_transmit.c,v 1.8 1998/07/17 20:19:37 root Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Allocate Transmit Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * 0 allocations successful + * else allocation failed + */ +int +fore_xmit_allocate(fup) + Fore_unit *fup; +{ + void *memp; + H_xmit_queue *hxp; + int i; + + /* + * Allocate non-cacheable memory for transmit status words + */ + memp = atm_dev_alloc(sizeof(Q_status) * XMIT_QUELEN, + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_xmit_stat = (Q_status *) memp; + + memp = DMA_GET_ADDR(fup->fu_xmit_stat, sizeof(Q_status) * XMIT_QUELEN, + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_xmit_statd = (Q_status *) memp; + + /* + * Allocate memory for transmit descriptors + * + * We will allocate the transmit descriptors individually rather than + * as a single memory block, which will often be larger than a memory + * page. On some systems (eg. FreeBSD) the physical addresses of + * adjacent virtual memory pages are not contiguous. + */ + hxp = fup->fu_xmit_q; + for (i = 0; i < XMIT_QUELEN; i++, hxp++) { + + /* + * Allocate a transmit descriptor for this queue entry + */ + hxp->hxq_descr = atm_dev_alloc(sizeof(Xmit_descr), + XMIT_DESCR_ALIGN, 0); + if (hxp->hxq_descr == NULL) { + return (1); + } + + hxp->hxq_descr_dma = DMA_GET_ADDR(hxp->hxq_descr, + sizeof(Xmit_descr), XMIT_DESCR_ALIGN, 0); + if (hxp->hxq_descr_dma == NULL) { + return (1); + } + } + + return (0); +} + + +/* + * Transmit Queue Initialization + * + * Allocate and initialize the host-resident transmit queue structures + * and then initialize the CP-resident queue structures. + * + * Called at interrupt level. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_xmit_initialize(fup) + Fore_unit *fup; +{ + Aali *aap = fup->fu_aali; + Xmit_queue *cqp; + H_xmit_queue *hxp; + Q_status *qsp; + Q_status *qsp_dma; + int i; + + /* + * Point to CP-resident transmit queue + */ + cqp = (Xmit_queue *)(fup->fu_ram + CP_READ(aap->aali_xmit_q)); + + /* + * Point to host-resident transmit queue structures + */ + hxp = fup->fu_xmit_q; + qsp = fup->fu_xmit_stat; + qsp_dma = fup->fu_xmit_statd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < XMIT_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hxp->hxq_cpelem = cqp; + hxp->hxq_status = qsp; + if (i == (XMIT_QUELEN - 1)) + hxp->hxq_next = fup->fu_xmit_q; + else + hxp->hxq_next = hxp + 1; + + /* + * Now let the CP into the game + */ + cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hxp++; + qsp++; + qsp_dma++; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_xmit_head = fup->fu_xmit_tail = fup->fu_xmit_q; + + return; +} + + +/* + * Drain Transmit Queue + * + * This function will free all completed entries at the head of the + * transmit queue. Freeing the entry includes releasing the transmit + * buffers (buffer chain) back to the kernel. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_xmit_drain(fup) + Fore_unit *fup; +{ + H_xmit_queue *hxp; + H_dma *sdmap; + Fore_vcc *fvp; + struct vccb *vcp; + KBuffer *m; + + /* + * Process each completed entry + */ + while (*fup->fu_xmit_head->hxq_status & QSTAT_COMPLETED) { + + hxp = fup->fu_xmit_head; + + /* + * Release the entry's DMA addresses and buffer chain + */ + for (m = hxp->hxq_buf, sdmap = hxp->hxq_dma; m; + m = KB_NEXT(m), sdmap++) { + caddr_t cp; + + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0); + } + KB_FREEALL(hxp->hxq_buf); + + /* + * Get VCC over which data was sent (may be null if + * VCC has been closed in the meantime) + */ + fvp = hxp->hxq_vcc; + + /* + * Now collect some statistics + */ + if (*hxp->hxq_status & QSTAT_ERROR) { + /* + * CP ran into problems, not much we can do + * other than record the event + */ + fup->fu_pif.pif_oerrors++; + if (fvp) { + vcp = fvp->fv_connvc->cvc_vcc; + vcp->vc_oerrors++; + if (vcp->vc_nif) + vcp->vc_nif->nif_if.if_oerrors++; + } + } else { + /* + * Good transmission + */ + int len = XDS_GET_LEN(hxp->hxq_descr->xd_spec); + + fup->fu_pif.pif_opdus++; + fup->fu_pif.pif_obytes += len; + if (fvp) { + vcp = fvp->fv_connvc->cvc_vcc; + vcp->vc_opdus++; + vcp->vc_obytes += len; + if (vcp->vc_nif) { + vcp->vc_nif->nif_obytes += len; + vcp->vc_nif->nif_if.if_opackets++; +#if (defined(BSD) && (BSD >= 199103)) + vcp->vc_nif->nif_if.if_obytes += len; +#endif + } + } + } + + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hxp->hxq_status = QSTAT_FREE; + fup->fu_xmit_head = hxp->hxq_next; + } + + return; +} + + +/* + * Free Transmit Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_xmit_free(fup) + Fore_unit *fup; +{ + H_xmit_queue *hxp; + H_dma *sdmap; + KBuffer *m; + int i; + + /* + * Free any transmit buffers left on the queue + */ + if (fup->fu_flags & CUF_INITED) { + while (*fup->fu_xmit_head->hxq_status != QSTAT_FREE) { + + hxp = fup->fu_xmit_head; + + /* + * Release the entry's DMA addresses and buffer chain + */ + for (m = hxp->hxq_buf, sdmap = hxp->hxq_dma; m; + m = KB_NEXT(m), sdmap++) { + caddr_t cp; + + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, *sdmap, KB_LEN(m), 0); + } + KB_FREEALL(hxp->hxq_buf); + + *hxp->hxq_status = QSTAT_FREE; + fup->fu_xmit_head = hxp->hxq_next; + } + } + + /* + * Free the status words + */ + if (fup->fu_xmit_stat) { + if (fup->fu_xmit_statd) { + DMA_FREE_ADDR(fup->fu_xmit_stat, fup->fu_xmit_statd, + sizeof(Q_status) * XMIT_QUELEN, + ATM_DEV_NONCACHE); + } + atm_dev_free((void *)fup->fu_xmit_stat); + fup->fu_xmit_stat = NULL; + fup->fu_xmit_statd = NULL; + } + + /* + * Free the transmit descriptors + */ + hxp = fup->fu_xmit_q; + for (i = 0; i < XMIT_QUELEN; i++, hxp++) { + + /* + * Free the transmit descriptor for this queue entry + */ + if (hxp->hxq_descr_dma) { + DMA_FREE_ADDR(hxp->hxq_descr, hxp->hxq_descr_dma, + sizeof(Xmit_descr), 0); + hxp->hxq_descr_dma = NULL; + } + + if (hxp->hxq_descr) { + atm_dev_free(hxp->hxq_descr); + hxp->hxq_descr = NULL; + } + } + + return; +} + diff --git a/sys/dev/hfa/fore_var.h b/sys/dev/hfa/fore_var.h new file mode 100644 index 0000000..25d2131 --- /dev/null +++ b/sys/dev/hfa/fore_var.h @@ -0,0 +1,269 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_var.h,v 1.8 1998/02/19 20:10:53 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Host protocol control blocks + * + */ + +#ifndef _FORE_VAR_H +#define _FORE_VAR_H + +/* + * Device VCC Entry + * + * Contains the common and Fore-specific information for each VCC + * which is opened through a Fore device. + */ +struct fore_vcc { + struct cmn_vcc fv_cmn; /* Common VCC stuff */ + Fore_aal fv_aal; /* CP version of AAL */ +}; +typedef struct fore_vcc Fore_vcc; + +#define fv_next fv_cmn.cv_next +#define fv_toku fv_cmn.cv_toku +#define fv_upper fv_cmn.cv_upper +#define fv_connvc fv_cmn.cv_connvc +#define fv_state fv_cmn.cv_state +#define fv_flags fv_cmn.cv_flags + +/* + * VCC Flags + */ +#define FVF_ACTCMD 0x01 /* Activate command issued */ + + +/* + * Host Transmit Queue Element + * + * Defines the host's view of the CP PDU Transmit Queue + */ +struct h_xmit_queue { + struct h_xmit_queue *hxq_next; /* Next element in queue */ + Xmit_queue *hxq_cpelem; /* CP queue element */ + Q_status *hxq_status; /* Element status word */ + Xmit_descr *hxq_descr; /* Element's transmit descriptor */ + Xmit_descr *hxq_descr_dma; /* Element's transmit descriptor */ + Fore_vcc *hxq_vcc; /* Data's VCC */ + KBuffer *hxq_buf; /* Data's buffer chain head */ + H_dma hxq_dma[XMIT_MAX_SEGS]; /* DMA addresses for segments */ +}; +typedef struct h_xmit_queue H_xmit_queue; + + + +/* + * Host Receive Queue Element + * + * Defines the host's view of the CP PDU Receive Queue + */ +struct h_recv_queue { + struct h_recv_queue *hrq_next; /* Next element in queue */ + Recv_queue *hrq_cpelem; /* CP queue element */ + Q_status *hrq_status; /* Element status word */ + Recv_descr *hrq_descr; /* Element's receive descriptor */ + Recv_descr *hrq_descr_dma; /* Element's receive descriptor */ +}; +typedef struct h_recv_queue H_recv_queue; + + + +/* + * Host Buffer Supply Queue Element + * + * Defines the host's view of the CP Buffer Supply Queue + */ +struct h_buf_queue { + struct h_buf_queue *hbq_next; /* Next element in queue */ + Buf_queue *hbq_cpelem; /* CP queue element */ + Q_status *hbq_status; /* Element status word */ + Buf_descr *hbq_descr; /* Element's buffer descriptor array */ + Buf_descr *hbq_descr_dma; /* Element's buffer descriptor array */ +}; +typedef struct h_buf_queue H_buf_queue; + + + +/* + * Host Command Queue Element + * + * Defines the host's view of the CP Command Queue + */ +struct h_cmd_queue { + struct h_cmd_queue *hcq_next; /* Next element in queue */ + Cmd_queue *hcq_cpelem; /* CP queue element */ + Q_status *hcq_status; /* Element status word */ + Cmd_code hcq_code; /* Command code */ + void *hcq_arg; /* Command-specific argument */ +}; +typedef struct h_cmd_queue H_cmd_queue; + + + +/* + * Host Buffer Handle + * + * For each buffer supplied to the CP, there will be one of these structures + * embedded into the non-data portion of the buffer. This will allow us to + * track which buffers are currently "controlled" by the CP. The address of + * this structure will supplied to/returned from the CP as the buffer handle. + */ +struct buf_handle { + Qelem_t bh_qelem; /* Queuing element */ + u_int bh_type; /* Buffer type (see below) */ + H_dma bh_dma; /* Buffer DMA address */ +}; +typedef struct buf_handle Buf_handle; +#define SIZEOF_Buf_handle 16 + +/* + * Buffer Types + */ +#define BHT_S1_SMALL 1 /* Buffer strategy 1, small */ +#define BHT_S1_LARGE 2 /* Buffer strategy 1, large */ +#define BHT_S2_SMALL 3 /* Buffer strategy 2, small */ +#define BHT_S2_LARGE 4 /* Buffer strategy 2, large */ + + + +/* + * Device Unit Structure + * + * Contains all the information for a single device (adapter). + */ +struct fore_unit { + Cmn_unit fu_cmn; /* Common unit stuff */ +#ifdef sun + struct dev_info *fu_devinfo; /* Device node for this unit */ +#endif + Fore_reg *fu_ctlreg; /* Device control register */ +#ifdef FORE_SBUS + Fore_reg *fu_intlvl; /* Interrupt level register */ +#endif +#ifdef FORE_PCI + Fore_reg *fu_imask; /* Interrupt mask register */ + Fore_reg *fu_psr; /* PCI specific register */ + pcici_t fu_pcitag; /* PCI tag */ +#endif + Fore_mem *fu_ram; /* Device RAM */ + u_int fu_ramsize; /* Size of device RAM */ + Mon960 *fu_mon; /* Monitor program interface */ + Aali *fu_aali; /* Microcode program interface */ + u_int fu_timer; /* Watchdog timer value */ + + /* Transmit Queue */ + H_xmit_queue fu_xmit_q[XMIT_QUELEN]; /* Host queue */ + H_xmit_queue *fu_xmit_head; /* Queue head */ + H_xmit_queue *fu_xmit_tail; /* Queue tail */ + Q_status *fu_xmit_stat; /* Status array (host) */ + Q_status *fu_xmit_statd; /* Status array (DMA) */ + + /* Receive Queue */ + H_recv_queue fu_recv_q[RECV_QUELEN]; /* Host queue */ + H_recv_queue *fu_recv_head; /* Queue head */ + Q_status *fu_recv_stat; /* Status array (host) */ + Q_status *fu_recv_statd; /* Status array (DMA) */ + Recv_descr *fu_recv_desc; /* Descriptor array (host) */ + Recv_descr *fu_recv_descd; /* Descriptor array (DMA) */ + + /* Buffer Supply Queue - Strategy 1 Small */ + H_buf_queue fu_buf1s_q[BUF1_SM_QUELEN]; /* Host queue */ + H_buf_queue *fu_buf1s_head; /* Queue head */ + H_buf_queue *fu_buf1s_tail; /* Queue tail */ + Q_status *fu_buf1s_stat; /* Status array (host) */ + Q_status *fu_buf1s_statd;/* Status array (DMA) */ + Buf_descr *fu_buf1s_desc; /* Descriptor array (host) */ + Buf_descr *fu_buf1s_descd;/* Descriptor array (DMA) */ + Queue_t fu_buf1s_bq; /* Queue of supplied buffers */ + u_int fu_buf1s_cnt; /* Count of supplied buffers */ + + /* Buffer Supply Queue - Strategy 1 Large */ + H_buf_queue fu_buf1l_q[BUF1_LG_QUELEN]; /* Host queue */ + H_buf_queue *fu_buf1l_head; /* Queue head */ + H_buf_queue *fu_buf1l_tail; /* Queue tail */ + Q_status *fu_buf1l_stat; /* Status array (host) */ + Q_status *fu_buf1l_statd;/* Status array (DMA) */ + Buf_descr *fu_buf1l_desc; /* Descriptor array (host) */ + Buf_descr *fu_buf1l_descd;/* Descriptor array (DMA) */ + Queue_t fu_buf1l_bq; /* Queue of supplied buffers */ + u_int fu_buf1l_cnt; /* Count of supplied buffers */ + + /* Command Queue */ + H_cmd_queue fu_cmd_q[CMD_QUELEN]; /* Host queue */ + H_cmd_queue *fu_cmd_head; /* Queue head */ + H_cmd_queue *fu_cmd_tail; /* Queue tail */ + Q_status *fu_cmd_stat; /* Status array (host) */ + Q_status *fu_cmd_statd; /* Status array (DMA) */ + + Fore_stats *fu_stats; /* Device statistics buffer */ + Fore_stats *fu_statsd; /* Device statistics buffer (DMA) */ + time_t fu_stats_time; /* Last stats request timestamp */ + int fu_stats_ret; /* Stats request return code */ +#ifdef FORE_PCI + Fore_prom *fu_prom; /* Device PROM buffer */ + Fore_prom *fu_promd; /* Device PROM buffer (DMA) */ +#endif + struct callout_handle fu_thandle; /* Timer handle */ +}; +typedef struct fore_unit Fore_unit; + +#define fu_pif fu_cmn.cu_pif +#define fu_unit fu_cmn.cu_unit +#define fu_flags fu_cmn.cu_flags +#define fu_mtu fu_cmn.cu_mtu +#define fu_open_vcc fu_cmn.cu_open_vcc +#define fu_vcc fu_cmn.cu_vcc +#define fu_intrpri fu_cmn.cu_intrpri +#define fu_savepri fu_cmn.cu_savepri +#define fu_vcc_pool fu_cmn.cu_vcc_pool +#define fu_nif_pool fu_cmn.cu_nif_pool +#define fu_ioctl fu_cmn.cu_ioctl +#define fu_instvcc fu_cmn.cu_instvcc +#define fu_openvcc fu_cmn.cu_openvcc +#define fu_closevcc fu_cmn.cu_closevcc +#define fu_output fu_cmn.cu_output +#define fu_config fu_cmn.cu_config + +/* + * Device flags (in addition to CUF_* flags) + */ +#define FUF_STATCMD 0x80 /* Statistics request in progress */ + + +/* + * Macros to access CP memory + */ +#define CP_READ(x) ntohl((u_long)(x)) +#define CP_WRITE(x) htonl((u_long)(x)) + +#endif /* _FORE_VAR_H */ diff --git a/sys/dev/hfa/fore_vcm.c b/sys/dev/hfa/fore_vcm.c new file mode 100644 index 0000000..3efea6a --- /dev/null +++ b/sys/dev/hfa/fore_vcm.c @@ -0,0 +1,321 @@ +/* + * + * =================================== + * HARP | Host ATM Research Platform + * =================================== + * + * + * This Host ATM Research Platform ("HARP") file (the "Software") is + * made available by Network Computing Services, Inc. ("NetworkCS") + * "AS IS". NetworkCS does not provide maintenance, improvements or + * support of any kind. + * + * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, + * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE + * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. + * In no event shall NetworkCS be responsible for any damages, including + * but not limited to consequential damages, arising from or relating to + * any use of the Software or related support. + * + * Copyright 1994-1998 Network Computing Services, Inc. + * + * Copies of this Software may be made, however, the above copyright + * notice must be reproduced on all copies. + * + * @(#) $Id: fore_vcm.c,v 1.7 1998/06/29 21:42:20 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Virtual Channel Management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_vcm.c,v 1.7 1998/06/29 21:42:20 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * VCC Stack Instantiation + * + * This function is called via the common driver code during a device VCC + * stack instantiation. The common code has already validated some of + * the request so we just need to check a few more Fore-specific details. + * + * Called at splnet. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 instantiation successful + * err instantiation failed - reason indicated + * + */ +int +fore_instvcc(cup, cvp) + Cmn_unit *cup; + Cmn_vcc *cvp; +{ + Fore_vcc *fvp = (Fore_vcc *)cvp; + Atm_attributes *ap = &fvp->fv_connvc->cvc_attr; + + /* + * Validate requested AAL + */ + switch (ap->aal.type) { + + case ATM_AAL0: + fvp->fv_aal = FORE_AAL_0; + break; + + case ATM_AAL3_4: + fvp->fv_aal = FORE_AAL_4; + if ((ap->aal.v.aal4.forward_max_SDU_size > FORE_IFF_MTU) || + (ap->aal.v.aal4.backward_max_SDU_size > FORE_IFF_MTU)) + return (EINVAL); + break; + + case ATM_AAL5: + fvp->fv_aal = FORE_AAL_5; + if ((ap->aal.v.aal5.forward_max_SDU_size > FORE_IFF_MTU) || + (ap->aal.v.aal5.backward_max_SDU_size > FORE_IFF_MTU)) + return (EINVAL); + break; + + default: + return (EINVAL); + } + + return (0); +} + + +/* + * Open a VCC + * + * This function is called via the common driver code after receiving a + * stack *_INIT command. The common code has already validated most of + * the request so we just need to check a few more Fore-specific details. + * Then we just issue the command to the CP. Note that we can't wait around + * for the CP to process the command, so we return success for now and abort + * the connection if the command later fails. + * + * Called at splimp. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 open successful + * else open failed + * + */ +int +fore_openvcc(cup, cvp) + Cmn_unit *cup; + Cmn_vcc *cvp; +{ + Fore_unit *fup = (Fore_unit *)cup; + Fore_vcc *fvp = (Fore_vcc *)cvp; + H_cmd_queue *hcp; + Cmd_queue *cqp; + struct vccb *vcp; + + vcp = fvp->fv_connvc->cvc_vcc; + + ATM_DEBUG4("fore_openvcc: fup=0x%x, fvp=0x%x, vcc=(%d,%d)\n", + (int)fup, (int)fvp, vcp->vc_vpi, vcp->vc_vci); + + /* + * Validate the VPI and VCI values + */ + if ((vcp->vc_vpi > fup->fu_pif.pif_maxvpi) || + (vcp->vc_vci > fup->fu_pif.pif_maxvci)) { + return (1); + } + + /* + * Only need to tell the CP about incoming VCCs + */ + if ((vcp->vc_type & VCC_IN) == 0) { + DEVICE_LOCK((Cmn_unit *)fup); + fup->fu_open_vcc++; + fvp->fv_state = CVS_ACTIVE; + DEVICE_UNLOCK((Cmn_unit *)fup); + return (0); + } + + /* + * Queue command at end of command queue + */ + hcp = fup->fu_cmd_tail; + if ((*hcp->hcq_status) & QSTAT_FREE) { + + /* + * Queue entry available, so set our view of things up + */ + hcp->hcq_code = CMD_ACT_VCCIN; + hcp->hcq_arg = fvp; + fup->fu_cmd_tail = hcp->hcq_next; + fvp->fv_flags |= FVF_ACTCMD; + + /* + * Now set the CP-resident queue entry - the CP will grab + * the command when the op-code is set. + */ + cqp = hcp->hcq_cpelem; + (*hcp->hcq_status) = QSTAT_PENDING; + cqp->cmdq_act.act_vccid = CP_WRITE(vcp->vc_vci); + if (fvp->fv_aal == FORE_AAL_0) + cqp->cmdq_act.act_batch = CP_WRITE(1); + cqp->cmdq_act.act_spec = CP_WRITE( + ACT_SET_SPEC(BUF_STRAT_1, fvp->fv_aal, + CMD_ACT_VCCIN | CMD_INTR_REQ)); + } else { + /* + * Command queue full + */ + fup->fu_stats->st_drv.drv_cm_full++; + return (1); + } + + return (0); +} + + +/* + * Close a VCC + * + * This function is called via the common driver code after receiving a + * stack *_TERM command. The common code has already validated most of + * the request so we just need to check a few more Fore-specific details. + * Then we just issue the command to the CP. Note that we can't wait around + * for the CP to process the command, so we return success for now and whine + * if the command later fails. + * + * Called at splimp. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 close successful + * else close failed + * + */ +int +fore_closevcc(cup, cvp) + Cmn_unit *cup; + Cmn_vcc *cvp; +{ + Fore_unit *fup = (Fore_unit *)cup; + Fore_vcc *fvp = (Fore_vcc *)cvp; + H_xmit_queue *hxp; + H_cmd_queue *hcp; + Cmd_queue *cqp; + struct vccb *vcp; + int i, err = 0; + + vcp = fvp->fv_connvc->cvc_vcc; + + ATM_DEBUG4("fore_closevcc: fup=0x%x, fvp=0x%x, vcc=(%d,%d)\n", + (int)fup, (int)fvp, vcp->vc_vpi, vcp->vc_vci); + + DEVICE_LOCK((Cmn_unit *)fup); + + /* + * Clear any references to this VCC in our transmit queue + */ + for (hxp = fup->fu_xmit_head, i = 0; + (*hxp->hxq_status != QSTAT_FREE) && (i < XMIT_QUELEN); + hxp = hxp->hxq_next, i++) { + if (hxp->hxq_vcc == fvp) { + hxp->hxq_vcc = NULL; + } + } + + /* + * Clear any references to this VCC in our command queue + */ + for (hcp = fup->fu_cmd_head, i = 0; + (*hcp->hcq_status != QSTAT_FREE) && (i < CMD_QUELEN); + hcp = hcp->hcq_next, i++) { + switch (hcp->hcq_code) { + + case CMD_ACT_VCCIN: + case CMD_ACT_VCCOUT: + if (hcp->hcq_arg == fvp) { + hcp->hcq_arg = NULL; + } + break; + } + } + + /* + * If this VCC has been previously activated, then we need to tell + * the CP to deactivate it. + */ + if (fvp->fv_flags & FVF_ACTCMD) { + + /* + * Queue command at end of command queue + */ + hcp = fup->fu_cmd_tail; + if ((*hcp->hcq_status) & QSTAT_FREE) { + + /* + * Queue entry available, so set our view of things up + */ + hcp->hcq_code = CMD_DACT_VCCIN; + hcp->hcq_arg = fvp; + fup->fu_cmd_tail = hcp->hcq_next; + + /* + * Now set the CP-resident queue entry - the CP will + * grab the command when the op-code is set. + */ + cqp = hcp->hcq_cpelem; + (*hcp->hcq_status) = QSTAT_PENDING; + cqp->cmdq_dact.dact_vccid = CP_WRITE(vcp->vc_vci); + cqp->cmdq_dact.dact_cmd = + CP_WRITE(CMD_DACT_VCCIN|CMD_INTR_REQ); + } else { + /* + * Command queue full + * + * If we get here, we'll be getting out-of-sync with + * the CP because we can't (for now at least) do + * anything about close errors in the common code. + * This won't be too bad, since we'll just toss any + * PDUs received from the VCC and the sigmgr's will + * always get open failures when trying to use this + * (vpi,vci)...oh, well...always gotta have that one + * last bug to fix! XXX + */ + fup->fu_stats->st_drv.drv_cm_full++; + err = 1; + } + } + + /* + * Finish up... + */ + if (fvp->fv_state == CVS_ACTIVE) + fup->fu_open_vcc--; + + DEVICE_UNLOCK((Cmn_unit *)fup); + + return (err); +} + |