diff options
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); +} + |