diff options
Diffstat (limited to 'sys')
132 files changed, 73729 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); +} + diff --git a/sys/netatm/atm.h b/sys/netatm/atm.h new file mode 100644 index 0000000..46dfb86 --- /dev/null +++ b/sys/netatm/atm.h @@ -0,0 +1,651 @@ +/* + * + * =================================== + * 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: atm.h,v 1.8 1998/07/30 22:30:43 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM address family definitions + * + */ + +#ifndef _NETATM_ATM_H +#define _NETATM_ATM_H + + +/* + * The definitions in this file are intended to conform to the + * specifications defined in: + * + * The Open Group, Networking Services (XNS) Issue 5 + * + * ATM Transport Protocol Information for Sockets + * + * which is Copyright (c) 1997, The Open Group. + * + * All extensions contained in this file to the base specification + * are denoted with a comment string of "XNS_EXT". + */ + +/* + * ATM socket protocols + */ +#define ATM_PROTO_AAL5 0x5301 /* AAL type 5 protocol */ +#define ATM_PROTO_SSCOP 0x5302 /* SSCOP protocol */ + + +/* + * ATM address defintions + */ +/* + * General format of an ATM address + */ +#define ATM_ADDR_LEN 20 /* Size of address field (XNS_EXT) */ + +struct t_atm_addr { + int8_t address_format; /* Address format (see below) */ + u_int8_t address_length; /* Length of address field */ + u_int8_t address[ATM_ADDR_LEN]; /* Address field */ +}; +typedef struct t_atm_addr Atm_addr; /* XNS_EXT */ + +/* + * ATM address formats + */ +#define T_ATM_ABSENT (-1) /* No address present */ +#define T_ATM_ENDSYS_ADDR 1 /* ATM Endsystem */ +#define T_ATM_NSAP_ADDR 1 /* NSAP */ +#define T_ATM_E164_ADDR 2 /* E.164 */ +#define T_ATM_SPANS_ADDR 3 /* FORE SPANS (XNS_EXT) */ +#define T_ATM_PVC_ADDR 4 /* PVC (VPI,VCI) (XNS_EXT) */ + +/* + * ATM Endsystem / NSAP address format + */ +struct atm_addr_nsap { /* XNS_EXT */ + u_char aan_afi; /* Authority and Format Identifier */ + /* (see below) */ + u_char aan_afspec[12]; /* AFI specific fields */ + u_char aan_esi[6]; /* End System Identifier */ + u_char aan_sel; /* Selector */ +}; +typedef struct atm_addr_nsap Atm_addr_nsap; + +/* + * AFI codes + */ +#define AFI_DCC 0x39 /* DCC ATM Format (XNS_EXT) */ +#define AFI_ICD 0x47 /* ICD ATM Format (XNS_EXT) */ +#define AFI_E164 0x45 /* E.164 ATM Format (XNS_EXT) */ + +/* + * E.164 address format + */ +struct atm_addr_e164 { /* XNS_EXT */ + u_char aae_addr[15]; /* E.164 address */ +}; +typedef struct atm_addr_e164 Atm_addr_e164; + +/* + * SPANS address format + */ +struct atm_addr_spans { /* XNS_EXT */ + u_char aas_addr[8]; /* See SPANS code for specific fields */ +}; +typedef struct atm_addr_spans Atm_addr_spans; + +/* + * PVC address format + */ +struct atm_addr_pvc { /* XNS_EXT */ + u_int8_t aap_vpi[2]; /* VPI */ + u_int8_t aap_vci[2]; /* VCI */ +}; +typedef struct atm_addr_pvc Atm_addr_pvc; + +#define ATM_PVC_GET_VPI(addr) /* XNS_EXT */ \ + ((u_int16_t)(((addr)->aap_vpi[0] << 8) | (addr)->aap_vpi[1])) +#define ATM_PVC_GET_VCI(addr) /* XNS_EXT */ \ + ((u_int16_t)(((addr)->aap_vci[0] << 8) | (addr)->aap_vci[1])) +#define ATM_PVC_SET_VPI(addr,vpi) { /* XNS_EXT */ \ + (addr)->aap_vpi[0] = ((vpi) >> 8) & 0xff; \ + (addr)->aap_vpi[1] = (vpi) & 0xff; \ +} +#define ATM_PVC_SET_VCI(addr,vci) { /* XNS_EXT */ \ + (addr)->aap_vci[0] = ((vci) >> 8) & 0xff; \ + (addr)->aap_vci[1] = (vci) & 0xff; \ +} + + +/* + * ATM service access point (SAP) + * + * A SAP address consists of SAP Vector Elements (SVE). Each SVE consists + * of the following fields: + * o tag - defines the interpretation of the SVE; + * o length - the length of the SVE value field; + * o value - the value associated with the SVE; + * + * All of the possible SAP field values are either defined below + * or in the corresponding option value definitions. + */ + +/* + * ATM Address and Selector SVE + */ +struct t_atm_sap_addr { + int8_t SVE_tag_addr; /* SVE tag (address) */ + int8_t SVE_tag_selector; /* SVE tag (selector) */ + /* Address/selector value */ + int8_t address_format; /* Address format */ + u_int8_t address_length; /* Length of address field */ + u_int8_t address[ATM_ADDR_LEN]; /* Address field */ +}; + +/* + * B-LLI Layer 2 SVE + */ +struct t_atm_sap_layer2 { + int8_t SVE_tag; /* SVE tag */ + u_int8_t ID_type; /* Layer 2 protocol discriminator */ + union { /* Layer 2 protocol */ + u_int8_t simple_ID; /* ITU */ + u_int8_t user_defined_ID;/* User-defined */ + } ID; +}; + +/* + * B-LLI Layer 3 SVE + */ +struct t_atm_sap_layer3 { + int8_t SVE_tag; /* SVE tag */ + u_int8_t ID_type; /* Layer 3 protocol discriminator */ + union { /* Layer 3 protocol */ + u_int8_t simple_ID; /* ITU */ + u_int8_t IPI_ID; /* ISO IPI */ + struct { /* IEEE 802.1 SNAP ID */ + u_int8_t OUI[3]; + u_int8_t PID[2]; + } SNAP_ID; + u_int8_t user_defined_ID;/* User-defined */ + } ID; +}; + +/* + * B_HLI SVE + */ +struct t_atm_sap_appl { + int8_t SVE_tag; /* SVE tag */ + u_int8_t ID_type; /* High Layer type discriminator */ + union { /* High Layer type */ + u_int8_t ISO_ID[8]; /* ISO */ + struct { /* Vendor-specific */ + u_int8_t OUI[3]; + u_int8_t app_ID[4]; + } vendor_ID; + u_int8_t user_defined_ID[8];/* User-defined */ + } ID; +}; + +/* + * ATM SAP (protocol) address structure + */ +struct t_atm_sap { + struct t_atm_sap_addr t_atm_sap_addr; + struct t_atm_sap_layer2 t_atm_sap_layer2; + struct t_atm_sap_layer3 t_atm_sap_layer3; + struct t_atm_sap_appl t_atm_sap_appl; +}; + +/* + * SVE Tag values + */ +#define T_ATM_ABSENT (-1) /* Value field invalid; match none */ +#define T_ATM_PRESENT (-2) /* Value field valid; match value */ +#define T_ATM_ANY (-3) /* Value field invalid; match any */ + + +/* + * ATM socket address + */ +struct sockaddr_atm { /* XNS_EXT */ +#if (defined(BSD) && (BSD >= 199103)) + u_char satm_len; /* Length of socket structure */ + u_char satm_family; /* Address family */ +#else + u_short satm_family; /* Address family */ +#endif + struct t_atm_sap satm_addr; /* Protocol address */ +}; + + +/* + * ATM socket options for use with [gs]etsockopt() + */ +#define T_ATM_SIGNALING 0x5301 /* Option level */ + +#define T_ATM_AAL5 1 /* ATM adaptation layer 5 */ +#define T_ATM_TRAFFIC 2 /* ATM traffic descriptor */ +#define T_ATM_BEARER_CAP 3 /* ATM service capabilities */ +#define T_ATM_BHLI 4 /* Higher-layer protocol */ +#define T_ATM_BLLI 5 /* Lower-layer protocol */ +#define T_ATM_DEST_ADDR 6 /* Call responder's address */ +#define T_ATM_DEST_SUB 7 /* Call responder's subaddress */ +#define T_ATM_ORIG_ADDR 8 /* Call initiator's address */ +#define T_ATM_ORIG_SUB 9 /* Call initiator's subaddress */ +#define T_ATM_CALLER_ID 10 /* Caller's ID attributes */ +#define T_ATM_CAUSE 11 /* Cause of disconection */ +#define T_ATM_QOS 12 /* Quality of service */ +#define T_ATM_TRANSIT 13 /* Choice of public carrier */ +#define T_ATM_ADD_LEAF 14 /* Add leaf to connection */ +#define T_ATM_DROP_LEAF 15 /* Remove leaf from connection */ +#define T_ATM_LEAF_IND 16 /* Indication of leaf status */ +#define T_ATM_NET_INTF 17 /* Network interface XNS_EXT */ +#define T_ATM_LLC 18 /* LLC multiplexing XNS_EXT */ +#define T_ATM_APP_NAME 19 /* Application name XNS_EXT */ + + +/* + * Common socket option values + * + * See API specification for individual option applicability/meaning + */ +#define T_ATM_ABSENT (-1) /* No option value present */ +#define T_ATM_NULL 0 /* Option value is null */ +#define T_NO 0 /* Option is not requested */ +#define T_YES 1 /* Option is requested */ + + +/* + * T_ATM_AAL5 option value structure + */ +struct t_atm_aal5 { + int32_t forward_max_SDU_size; + int32_t backward_max_SDU_size; + int32_t SSCS_type; +}; + +/* + * T_ATM_AAL5 option values + */ + /* SSCS_type */ +#define T_ATM_SSCS_SSCOP_REL 1 /* SSCOP assured operation */ +#define T_ATM_SSCS_SSCOP_UNREL 2 /* SSCOP non-assured operation */ +#define T_ATM_SSCS_FR 4 /* Frame relay */ + + +/* + * T_ATM_TRAFFIC option value structure + */ +struct t_atm_traffic_substruct { + int32_t PCR_high_priority; + int32_t PCR_all_traffic; + int32_t SCR_high_priority; + int32_t SCR_all_traffic; + int32_t MBS_high_priority; + int32_t MBS_all_traffic; + int32_t tagging; +}; + +struct t_atm_traffic { + struct t_atm_traffic_substruct forward; + struct t_atm_traffic_substruct backward; + u_int8_t best_effort; +}; + + +/* + * T_ATM_BEARER_CAP option value structure + */ +struct t_atm_bearer { + u_int8_t bearer_class; + u_int8_t traffic_type; + u_int8_t timing_requirements; + u_int8_t clipping_susceptibility; + u_int8_t connection_configuration; +}; + +/* + * T_ATM_BEARER_CAP option values + */ + /* bearer_class */ +#define T_ATM_CLASS_A 0x01 /* Bearer class A */ +#define T_ATM_CLASS_C 0x03 /* Bearer class C */ +#define T_ATM_CLASS_X 0x10 /* Bearer class X */ + + /* traffic_type */ +#define T_ATM_CBR 0x01 /* Constant bit rate */ +#define T_ATM_VBR 0x02 /* Variable bit rate */ + + /* timing_requirements */ +#define T_ATM_END_TO_END 0x01 /* End-to-end timing required */ +#define T_ATM_NO_END_TO_END 0x02 /* End-to-end timing not required */ + + /* connection_configuration */ +#define T_ATM_1_TO_1 0x00 /* Point-to-point connection */ +#define T_ATM_1_TO_MANY 0x01 /* Point-to-multipoint connection */ + + +/* + * T_ATM_BHLI option value structure + */ +struct t_atm_bhli { + int32_t ID_type; + union { + u_int8_t ISO_ID[8]; + struct { + u_int8_t OUI[3]; + u_int8_t app_ID[4]; + } vendor_ID; + u_int8_t user_defined_ID[8]; + } ID; +}; + +/* + * T_ATM_BHLI option values + */ + /* ID_type */ +#define T_ATM_ISO_APP_ID 0 /* ISO codepoint */ +#define T_ATM_USER_APP_ID 1 /* User-specific codepoint */ +#define T_ATM_VENDOR_APP_ID 3 /* Vendor-specific codepoint */ + +/* + * T_ATM_BLLI option value structure + */ +struct t_atm_blli { + struct { + int8_t ID_type; + union { + u_int8_t simple_ID; + u_int8_t user_defined_ID; + } ID; + int8_t mode; + int8_t window_size; + } layer_2_protocol; + struct { + int8_t ID_type; + union { + u_int8_t simple_ID; + int32_t IPI_ID; + struct { + u_int8_t OUI[3]; + u_int8_t PID[2]; + } SNAP_ID; + u_int8_t user_defined_ID; + } ID; + int8_t mode; + int8_t packet_size; + int8_t window_size; + } layer_3_protocol; +}; + + +/* + * T_ATM_BLLI option values + */ + /* layer_[23]_protocol.ID_type */ +#define T_ATM_SIMPLE_ID 1 /* ID via ITU encoding */ +#define T_ATM_IPI_ID 2 /* ID via ISO/IEC TR 9577 */ +#define T_ATM_SNAP_ID 3 /* ID via SNAP */ +#define T_ATM_USER_ID 4 /* ID via user codepoints */ + + /* layer_[23]_protocol.mode */ +#define T_ATM_BLLI_NORMAL_MODE 1 +#define T_ATM_BLLI_EXTENDED_MODE 2 + + /* layer_2_protocol.simple_ID */ +#define T_ATM_BLLI2_I1745 1 /* I.1745 */ +#define T_ATM_BLLI2_Q921 2 /* Q.921 */ +#define T_ATM_BLLI2_X25_LINK 6 /* X.25, link layer */ +#define T_ATM_BLLI2_X25_MLINK 7 /* X.25, multilink */ +#define T_ATM_BLLI2_LAPB 8 /* Extended LAPB */ +#define T_ATM_BLLI2_HDLC_ARM 9 /* I.4335, ARM */ +#define T_ATM_BLLI2_HDLC_NRM 10 /* I.4335, NRM */ +#define T_ATM_BLLI2_HDLC_ABM 11 /* I.4335, ABM */ +#define T_ATM_BLLI2_I8802 12 /* I.8802 */ +#define T_ATM_BLLI2_X75 13 /* X.75 */ +#define T_ATM_BLLI2_Q922 14 /* Q.922 */ +#define T_ATM_BLLI2_I7776 17 /* I.7776 */ + + /* layer_3_protocol.simple_ID */ +#define T_ATM_BLLI3_X25 6 /* X.25 */ +#define T_ATM_BLLI3_I8208 7 /* I.8208 */ +#define T_ATM_BLLI3_X223 8 /* X.223 */ +#define T_ATM_BLLI3_I8473 9 /* I.8473 */ +#define T_ATM_BLLI3_T70 10 /* T.70 */ +#define T_ATM_BLLI3_I9577 11 /* I.9577 */ + + /* layer_3_protocol.packet_size */ +#define T_ATM_PACKET_SIZE_16 4 +#define T_ATM_PACKET_SIZE_32 5 +#define T_ATM_PACKET_SIZE_64 6 +#define T_ATM_PACKET_SIZE_128 7 +#define T_ATM_PACKET_SIZE_256 8 +#define T_ATM_PACKET_SIZE_512 9 +#define T_ATM_PACKET_SIZE_1024 10 +#define T_ATM_PACKET_SIZE_2048 11 +#define T_ATM_PACKET_SIZE_4096 12 + + +/* + * T_ATM_CALLER_ID option value structure + */ +struct t_atm_caller_id { + int8_t presentation; + u_int8_t screening; +}; + +/* + * T_ATM_CALLER_ID option values + */ + /* presentation */ +#define T_ATM_PRES_ALLOWED 0 +#define T_ATM_PRES_RESTRICTED 1 +#define T_ATM_PRES_UNAVAILABLE 2 + /* screening */ +#define T_ATM_USER_ID_NOT_SCREENED 0 +#define T_ATM_USER_ID_PASSED_SCREEN 1 +#define T_ATM_USER_ID_FAILED_SCREEN 2 +#define T_ATM_NETWORK_PROVIDED_ID 3 + + +/* + * T_ATM_CAUSE option value structure + */ +struct t_atm_cause { + int8_t coding_standard; + u_int8_t location; + u_int8_t cause_value; + u_int8_t diagnostics[4]; +}; + +/* + * T_ATM_CAUSE option values + */ + /* coding_standard */ +#define T_ATM_ITU_CODING 0 +#define T_ATM_NETWORK_CODING 3 + + /* location */ +#define T_ATM_LOC_USER 0 +#define T_ATM_LOC_LOCAL_PRIVATE_NET 1 +#define T_ATM_LOC_LOCAL_PUBLIC_NET 2 +#define T_ATM_LOC_TRANSIT_NET 3 +#define T_ATM_LOC_REMOTE_PUBLIC_NET 4 +#define T_ATM_LOC_REMOTE_PRIVATE_NET 5 +#define T_ATM_LOC_INTERNATIONAL_NET 7 +#define T_ATM_LOC_BEYOND_INTERWORKING 10 + + /* cause_value */ +#define T_ATM_CAUSE_UNALLOCATED_NUMBER 1 +#define T_ATM_CAUSE_NO_ROUTE_TO_TRANSIT_NETWORK 2 +#define T_ATM_CAUSE_NO_ROUTE_TO_DESTINATION 3 +#define T_ATM_CAUSE_NORMAL_CALL_CLEARING 16 +#define T_ATM_CAUSE_USER_BUSY 17 +#define T_ATM_CAUSE_NO_USER_RESPONDING 18 +#define T_ATM_CAUSE_CALL_REJECTED 21 +#define T_ATM_CAUSE_NUMBER_CHANGED 22 +#define T_ATM_CAUSE_ALL_CALLS_WITHOUT_CALLER_ID_REJECTED 23 +#define T_ATM_CAUSE_DESTINATION_OUT_OF_ORDER 27 +#define T_ATM_CAUSE_INVALID_NUMBER_FORMAT 28 +#define T_ATM_CAUSE_RESPONSE_TO_STATUS_ENQUIRY 30 +#define T_ATM_CAUSE_UNSPECIFIED_NORMAL 31 +#define T_ATM_CAUSE_REQUESTED_VPCI_VCI_NOT_AVAILABLE 35 +#define T_ATM_CAUSE_VPCI_VCI_ASSIGNMENT_FAILURE 36 +#define T_ATM_CAUSE_USER_CELL_RATE_NOT_AVAILABLE 37 +#define T_ATM_CAUSE_NETWORK_OUT_OF_ORDER 38 +#define T_ATM_CAUSE_TEMPORARY_FAILURE 41 +#define T_ATM_CAUSE_ACCESS_INFO_DISCARDED 43 +#define T_ATM_CAUSE_NO_VPCI_VCI_AVAILABLE 45 +#define T_ATM_CAUSE_UNSPECIFIED_RESOURCE_UNAVAILABLE 47 +#define T_ATM_CAUSE_QUALITY_OF_SERVICE_UNAVAILABLE 49 +#define T_ATM_CAUSE_BEARER_CAPABILITY_NOT_AUTHORIZED 57 +#define T_ATM_CAUSE_BEARER_CAPABILITY_UNAVAILABLE 58 +#define T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE 63 +#define T_ATM_CAUSE_BEARER_CAPABILITY_NOT_IMPLEMENTED 65 +#define T_ATM_CAUSE_INVALID_TRAFFIC_PARAMETERS 73 +#define T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED 78 +#define T_ATM_CAUSE_INVALID_CALL_REFERENCE_VALUE 81 +#define T_ATM_CAUSE_IDENTIFIED_CHANNEL_DOES_NOT_EXIST 82 +#define T_ATM_CAUSE_INCOMPATIBLE_DESTINATION 88 +#define T_ATM_CAUSE_INVALID_ENDPOINT_REFERENCE 89 +#define T_ATM_CAUSE_INVALID_TRANSIT_NETWORK_SELECTION 91 +#define T_ATM_CAUSE_TOO_MANY_PENDING_ADD_PARTY_REQUESTS 92 +#define T_ATM_CAUSE_MANDITORY_INFO_ELEMENT_MISSING 96 +#define T_ATM_CAUSE_MESSAGE_TYPE_NOT_IMPLEMENTED 97 +#define T_ATM_CAUSE_INFO_ELEMENT_NOT_IMPLEMENTED 99 +#define T_ATM_CAUSE_INVALID_INFO_ELEMENT_CONTENTS 100 +#define T_ATM_CAUSE_MESSAGE_INCOMPATIBLE_WITH_CALL_STATE 101 +#define T_ATM_CAUSE_RECOVERY_ON_TIMER_EXPIRY 102 +#define T_ATM_CAUSE_INCORRECT_MESSAGE_LENGTH 104 +#define T_ATM_CAUSE_UNSPECIFIED_PROTOCOL_ERROR 111 + + +/* + * T_ATM_QOS option value structure + */ +struct t_atm_qos_substruct { + int32_t qos_class; +}; + +struct t_atm_qos { + int8_t coding_standard; + struct t_atm_qos_substruct forward; + struct t_atm_qos_substruct backward; +}; + +/* + * T_ATM_QOS option values + */ + /* qos_class */ +#define T_ATM_QOS_CLASS_0 0 +#define T_ATM_QOS_CLASS_1 1 +#define T_ATM_QOS_CLASS_2 2 +#define T_ATM_QOS_CLASS_3 3 +#define T_ATM_QOS_CLASS_4 4 + + +/* + * T_ATM_TRANSIT structure + */ +#define T_ATM_MAX_NET_ID 4 /* XNS_EXT */ +struct t_atm_transit { + u_int8_t length; + u_int8_t network_id[T_ATM_MAX_NET_ID]; +}; + + +/* + * T_ATM_ADD_LEAF option value structure + */ +struct t_atm_add_leaf { + int32_t leaf_ID; + struct t_atm_addr leaf_address; +}; + + +/* + * T_ATM_DROP_LEAF option value structure + */ +struct t_atm_drop_leaf { + int32_t leaf_ID; + int32_t reason; +}; + +/* + * T_ATM_LEAF_IND option value structure + */ +struct t_atm_leaf_ind { + int32_t status; + int32_t leaf_ID; + int32_t reason; +}; + +/* + * T_ATM_LEAF_IND option values + */ + /* status */ +#define T_LEAF_NOCHANGE 0 +#define T_LEAF_CONNECTED 1 +#define T_LEAF_DISCONNECTED 2 + +/* + * T_ATM_NET_INTF option value structure (XNS_EXT) + */ +struct t_atm_net_intf { /* XNS_EXT */ + char net_intf[IFNAMSIZ]; +}; + +/* + * T_ATM_LLC option value structure (XNS_EXT) + */ +#define T_ATM_LLC_MIN_LEN 3 +#define T_ATM_LLC_MAX_LEN 8 + +struct t_atm_llc { /* XNS_EXT */ + u_int8_t flags; /* LLC flags (see below) */ + u_int8_t llc_len; /* Length of LLC information */ + u_int8_t llc_info[T_ATM_LLC_MAX_LEN]; /* LLC information */ +}; + +/* + * T_ATM_LLC option values + */ + /* flags */ +#define T_ATM_LLC_SHARING 0x01 /* LLC sharing allowed */ + +/* + * T_ATM_APP_NAME option value structure (XNS_EXT) + */ +#define T_ATM_APP_NAME_LEN 8 +struct t_atm_app_name { /* XNS_EXT */ + char app_name[T_ATM_APP_NAME_LEN]; +}; + +#endif /* _NETATM_ATM_H */ diff --git a/sys/netatm/atm_aal5.c b/sys/netatm/atm_aal5.c new file mode 100644 index 0000000..c0e8fe4 --- /dev/null +++ b/sys/netatm/atm_aal5.c @@ -0,0 +1,905 @@ +/* + * + * =================================== + * 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: atm_aal5.c,v 1.4 1998/07/30 22:30:46 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM AAL5 socket protocol processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_aal5.c,v 1.4 1998/07/30 22:30:46 mks Exp $"; +#endif + +#include <netatm/kern_include.h> +#include <sys/stat.h> + + +/* + * Global variables + */ +u_long atm_aal5_sendspace = 64 * 1024; /* XXX */ +u_long atm_aal5_recvspace = 64 * 1024; /* XXX */ + + +/* + * Local functions + */ +static int atm_aal5_attach __P((struct socket *, int, struct proc *)); +static int atm_aal5_detach __P((struct socket *)); +static int atm_aal5_bind __P((struct socket *, struct sockaddr *, + struct proc *)); +static int atm_aal5_listen __P((struct socket *, struct proc *)); +static int atm_aal5_connect __P((struct socket *, struct sockaddr *, + struct proc *)); +static int atm_aal5_accept __P((struct socket *, struct sockaddr **)); +static int atm_aal5_disconnect __P((struct socket *)); +static int atm_aal5_shutdown __P((struct socket *)); +static int atm_aal5_send __P((struct socket *, int, KBuffer *, + struct sockaddr *, KBuffer *, struct proc *)); +static int atm_aal5_abort __P((struct socket *)); +static int atm_aal5_control __P((struct socket *, u_long, caddr_t, + struct ifnet *, struct proc *)); +static int atm_aal5_sense __P((struct socket *, struct stat *)); +static int atm_aal5_sockaddr __P((struct socket *, struct sockaddr **)); +static int atm_aal5_peeraddr __P((struct socket *, struct sockaddr **)); +static int atm_aal5_incoming __P((void *, Atm_connection *, + Atm_attributes *, void **)); +static void atm_aal5_cpcs_data __P((void *, KBuffer *)); +static caddr_t atm_aal5_getname __P((void *)); + + +#if (defined(__FreeBSD__) && (BSD >= 199506)) +/* + * New-style socket request routines + */ +struct pr_usrreqs atm_aal5_usrreqs = { + atm_aal5_abort, /* pru_abort */ + atm_aal5_accept, /* pru_accept */ + atm_aal5_attach, /* pru_attach */ + atm_aal5_bind, /* pru_bind */ + atm_aal5_connect, /* pru_connect */ + pru_connect2_notsupp, /* pru_connect2 */ + atm_aal5_control, /* pru_control */ + atm_aal5_detach, /* pru_detach */ + atm_aal5_disconnect, /* pru_disconnect */ + atm_aal5_listen, /* pru_listen */ + atm_aal5_peeraddr, /* pru_peeraddr */ + pru_rcvd_notsupp, /* pru_rcvd */ + pru_rcvoob_notsupp, /* pru_rcvoob */ + atm_aal5_send, /* pru_send */ + atm_aal5_sense, /* pru_sense */ + atm_aal5_shutdown, /* pru_shutdown */ + atm_aal5_sockaddr, /* pru_sockaddr */ + sosend, /* pru_sosend */ + soreceive, /* pru_soreceive */ + sopoll /* pru_sopoll */ +}; +#endif + + +/* + * Local variables + */ +static Atm_endpoint atm_aal5_endpt = { + NULL, + ENDPT_SOCK_AAL5, + NULL, + atm_aal5_getname, + atm_sock_connected, + atm_sock_cleared, + atm_aal5_incoming, + NULL, + NULL, + NULL, + atm_aal5_cpcs_data, + NULL, + NULL, + NULL, + NULL +}; + +static Atm_attributes atm_aal5_defattr = { + NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL5 + }, + { /* traffic */ + T_ATM_ABSENT, + }, + { /* bearer */ + T_ATM_ABSENT, + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_ABSENT, + T_ATM_ABSENT, + }, + { /* llc */ + T_ATM_ABSENT, + }, + { /* called */ + T_ATM_ABSENT, + { + T_ATM_ABSENT, + 0 + }, + { + T_ATM_ABSENT, + 0 + } + }, + { /* calling */ + T_ATM_ABSENT + }, + { /* qos */ + T_ATM_ABSENT, + }, + { /* transit */ + T_ATM_ABSENT + }, + { /* cause */ + T_ATM_ABSENT + } +}; + + +/* + * Handy common code macros + */ +#ifdef DIAGNOSTIC +#define ATM_INTRO(f) \ + int s, err = 0; \ + s = splnet(); \ + ATM_DEBUG2("aal5 socket %s (0x%x)\n", f, (int)so); \ + /* \ + * Stack queue should have been drained \ + */ \ + if (atm_stackq_head != NULL) \ + panic("atm_aal5: stack queue not empty"); \ + ; +#else +#define ATM_INTRO(f) \ + int s, err = 0; \ + s = splnet(); \ + ; +#endif + +#define ATM_OUTRO() \ +out: \ + /* \ + * Drain any deferred calls \ + */ \ + STACK_DRAIN(); \ + (void) splx(s); \ + return (err); \ + ; + +#define ATM_RETERR(errno) { \ + err = errno; \ + goto out; \ +} + + +/* + * Attach protocol to socket + * + * Arguments: + * so pointer to socket + * proto protocol identifier + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_attach(so, proto, p) + struct socket *so; + int proto; + struct proc *p; +{ + Atm_pcb *atp; + + ATM_INTRO("attach"); + + /* + * Do general attach stuff + */ + err = atm_sock_attach(so, atm_aal5_sendspace, atm_aal5_recvspace); + if (err) + goto out; + + /* + * Finish up any protocol specific stuff + */ + atp = sotoatmpcb(so); + atp->atp_type = ATPT_AAL5; + + /* + * Set default connection attributes + */ + atp->atp_attr = atm_aal5_defattr; + strncpy(atp->atp_name, "(AAL5)", T_ATM_APP_NAME_LEN); + + ATM_OUTRO(); +} + + +/* + * Detach protocol from socket + * + * Arguments: + * so pointer to socket + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_detach(so) + struct socket *so; +{ + ATM_INTRO("detach"); + + err = atm_sock_detach(so); + + ATM_OUTRO(); +} + + +/* + * Bind address to socket + * + * Arguments: + * so pointer to socket + * addr pointer to protocol address + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_bind(so, addr, p) + struct socket *so; + struct sockaddr *addr; + struct proc *p; +{ + ATM_INTRO("bind"); + + err = atm_sock_bind(so, addr); + + ATM_OUTRO(); +} + + +/* + * Listen for incoming connections + * + * Arguments: + * so pointer to socket + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_listen(so, p) + struct socket *so; + struct proc *p; +{ + ATM_INTRO("listen"); + + err = atm_sock_listen(so, &atm_aal5_endpt); + + ATM_OUTRO(); +} + + +/* + * Connect socket to peer + * + * Arguments: + * so pointer to socket + * addr pointer to protocol address + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_connect(so, addr, p) + struct socket *so; + struct sockaddr *addr; + struct proc *p; +{ + Atm_pcb *atp; + + ATM_INTRO("connect"); + + atp = sotoatmpcb(so); + + /* + * Resize send socket buffer to maximum sdu size + */ + if (atp->atp_attr.aal.tag == T_ATM_PRESENT) { + long size; + + size = atp->atp_attr.aal.v.aal5.forward_max_SDU_size; + if (size != T_ATM_ABSENT) + (void) sbreserve(&so->so_snd, size); + } + + /* + * Now get the socket connected + */ + err = atm_sock_connect(so, addr, &atm_aal5_endpt); + + ATM_OUTRO(); +} + + +/* + * Accept pending connection + * + * Arguments: + * so pointer to socket + * addr pointer to pointer to contain protocol address + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_accept(so, addr) + struct socket *so; + struct sockaddr **addr; +{ + ATM_INTRO("accept"); + + /* + * Everything is pretty much done already, we just need to + * return the caller's address to the user. + */ + err = atm_sock_peeraddr(so, addr); + + ATM_OUTRO(); +} + + +/* + * Disconnect connected socket + * + * Arguments: + * so pointer to socket + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_disconnect(so) + struct socket *so; +{ + ATM_INTRO("disconnect"); + + err = atm_sock_disconnect(so); + + ATM_OUTRO(); +} + + +/* + * Shut down socket data transmission + * + * Arguments: + * so pointer to socket + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_shutdown(so) + struct socket *so; +{ + ATM_INTRO("shutdown"); + + socantsendmore(so); + + ATM_OUTRO(); +} + + +/* + * Send user data + * + * Arguments: + * so pointer to socket + * flags send data flags + * m pointer to buffer containing user data + * addr pointer to protocol address + * control pointer to buffer containing protocol control data + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_send(so, flags, m, addr, control, p) + struct socket *so; + int flags; + KBuffer *m; + struct sockaddr *addr; + KBuffer *control; + struct proc *p; +{ + Atm_pcb *atp; + + ATM_INTRO("send"); + + /* + * We don't support any control functions + */ + if (control) { + int clen; + + clen = KB_LEN(control); + KB_FREEALL(control); + if (clen) { + KB_FREEALL(m); + ATM_RETERR(EINVAL); + } + } + + /* + * We also don't support any flags or send-level addressing + */ + if (flags || addr) { + KB_FREEALL(m); + ATM_RETERR(EINVAL); + } + + /* + * All we've got left is the data, so push it out + */ + atp = sotoatmpcb(so); + err = atm_cm_cpcs_data(atp->atp_conn, m); + if (err) { + /* + * Output problem, drop packet + */ + atm_sock_stat.as_outdrop[atp->atp_type]++; + KB_FREEALL(m); + } + + ATM_OUTRO(); +} + + +/* + * Abnormally terminate service + * + * Arguments: + * so pointer to socket + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_abort(so) + struct socket *so; +{ + ATM_INTRO("abort"); + + so->so_error = ECONNABORTED; + err = atm_sock_detach(so); + + ATM_OUTRO(); +} + + +/* + * Do control operation - ioctl system call + * + * Arguments: + * so pointer to socket + * cmd ioctl code + * data pointer to code specific parameter data area + * ifp pointer to ifnet structure if it's an interface ioctl + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_control(so, cmd, data, ifp, p) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; + struct proc *p; +{ + ATM_INTRO("control"); + + switch (cmd) { + + default: + err = EOPNOTSUPP; + } + + ATM_OUTRO(); +} + +/* + * Sense socket status - fstat system call + * + * Arguments: + * so pointer to socket + * st pointer to file status structure + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_sense(so, st) + struct socket *so; + struct stat *st; +{ + ATM_INTRO("sense"); + + /* + * Just return the max sdu size for the connection + */ + st->st_blksize = so->so_snd.sb_hiwat; + + ATM_OUTRO(); +} + + +/* + * Retrieve local socket address + * + * Arguments: + * so pointer to socket + * addr pointer to pointer to contain protocol address + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_sockaddr(so, addr) + struct socket *so; + struct sockaddr **addr; +{ + ATM_INTRO("sockaddr"); + + err = atm_sock_sockaddr(so, addr); + + ATM_OUTRO(); +} + + +/* + * Retrieve peer socket address + * + * Arguments: + * so pointer to socket + * addr pointer to pointer to contain protocol address + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_aal5_peeraddr(so, addr) + struct socket *so; + struct sockaddr **addr; +{ + ATM_INTRO("peeraddr"); + + err = atm_sock_peeraddr(so, addr); + + ATM_OUTRO(); +} + + +/* + * Process Incoming Calls + * + * This function will receive control when an incoming call has been matched + * to one of our registered listen parameter blocks. Assuming the call passes + * acceptance criteria and all required resources are available, we will + * create a new protocol control block and socket association. We must + * then await notification of the final SVC setup results. If any + * problems are encountered, we will just tell the connection manager to + * reject the call. + * + * Called at splnet. + * + * Arguments: + * tok owner's matched listening token + * cop pointer to incoming call's connection block + * ap pointer to incoming call's attributes + * tokp pointer to location to store our connection token + * + * Returns: + * 0 call is accepted + * errno call rejected - reason indicated + * + */ +static int +atm_aal5_incoming(tok, cop, ap, tokp) + void *tok; + Atm_connection *cop; + Atm_attributes *ap; + void **tokp; +{ + Atm_pcb *atp = tok; + struct socket *so; + int err = 0; + + /* + * Allocate a new socket and pcb for this connection. + * + * Note that our attach function will be called via sonewconn + * and it will allocate and setup most of the pcb. + */ + atm_sock_stat.as_inconn[atp->atp_type]++; +#if (defined(BSD) && (BSD >= 199103)) + so = sonewconn(atp->atp_socket, 0); +#else + so = sonewconn(atp->atp_socket); +#endif + + if (so) { + /* + * Finish pcb setup and pass pcb back to CM + */ + atp = sotoatmpcb(so); + atp->atp_conn = cop; + *tokp = atp; + } else { + err = ECONNABORTED; + atm_sock_stat.as_connfail[atp->atp_type]++; + } + + return (err); +} + + +/* + * Process Socket VCC Input Data + * + * Arguments: + * tok owner's connection token (atm_pcb) + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +atm_aal5_cpcs_data(tok, m) + void *tok; + KBuffer *m; +{ + Atm_pcb *atp = tok; + struct socket *so; + int len; + + so = atp->atp_socket; + + KB_PLENGET(m, len); + + /* + * Ensure that the socket is able to receive data and + * that there's room in the socket buffer + */ + if (((so->so_state & SS_ISCONNECTED) == 0) || + (so->so_state & SS_CANTRCVMORE) || + (len > sbspace(&so->so_rcv))) { + atm_sock_stat.as_indrop[atp->atp_type]++; + KB_FREEALL(m); + return; + } + + /* + * Queue the data and notify the user + */ + sbappendrecord(&so->so_rcv, m); + sorwakeup(so); + + return; +} + + +/* + * Process getsockopt/setsockopt system calls + * + * Arguments: + * so pointer to socket + * sopt pointer to socket option info + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_aal5_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + Atm_pcb *atp; + + ATM_INTRO("ctloutput"); + + /* + * Make sure this is for us + */ + if (sopt->sopt_level != T_ATM_SIGNALING) { + ATM_RETERR(EINVAL); + } + atp = sotoatmpcb(so); + if (atp == NULL) { + ATM_RETERR(ENOTCONN); + } + + switch (sopt->sopt_dir) { + + case SOPT_SET: + /* + * setsockopt() + */ + + /* + * Validate socket state + */ + switch (sopt->sopt_name) { + + case T_ATM_ADD_LEAF: + case T_ATM_DROP_LEAF: + if ((so->so_state & SS_ISCONNECTED) == 0) { + ATM_RETERR(ENOTCONN); + } + break; + + case T_ATM_CAUSE: + break; + + default: + if (so->so_state & SS_ISCONNECTED) { + ATM_RETERR(EISCONN); + } + break; + } + + /* + * Validate and save user-supplied option data + */ + err = atm_sock_setopt(so, sopt, atp); + + break; + + case SOPT_GET: + /* + * getsockopt() + */ + + /* + * Return option data + */ + err = atm_sock_getopt(so, sopt, atp); + + break; + } + + ATM_OUTRO(); +} + + +/* + * Initialize AAL5 Sockets + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +atm_aal5_init() +{ + /* + * Register our endpoint + */ + if (atm_endpoint_register(&atm_aal5_endpt)) + panic("atm_aal5_init: register"); + + /* + * Set default connection attributes + */ + atm_aal5_defattr.aal.v.aal5.forward_max_SDU_size = T_ATM_ABSENT; + atm_aal5_defattr.aal.v.aal5.backward_max_SDU_size = T_ATM_ABSENT; + atm_aal5_defattr.aal.v.aal5.SSCS_type = T_ATM_NULL; +} + + +/* + * Get Connection's Application/Owner Name + * + * Arguments: + * tok owner's connection token (atm_pcb) + * + * Returns: + * addr pointer to string containing our name + * + */ +static caddr_t +atm_aal5_getname(tok) + void *tok; +{ + Atm_pcb *atp = tok; + + return (atp->atp_name); +} + diff --git a/sys/netatm/atm_cm.c b/sys/netatm/atm_cm.c new file mode 100644 index 0000000..55612e3 --- /dev/null +++ b/sys/netatm/atm_cm.c @@ -0,0 +1,3464 @@ +/* + * + * =================================== + * 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: atm_cm.c,v 1.8 1998/08/06 18:10:42 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Connection Manager + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_cm.c,v 1.8 1998/08/06 18:10:42 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Global variables + */ +struct atm_cm_stat atm_cm_stat = {0}; + +/* + * Local functions + */ +static void atm_cm_cpcs_upper __P((int, void *, int, int)); +static void atm_cm_saal_upper __P((int, void *, int, int)); +static void atm_cm_sscop_upper __P((int, void *, int, int)); +static Atm_connvc * atm_cm_share_llc __P((Atm_attributes *)); +static void atm_cm_closeconn __P((Atm_connection *, + struct t_atm_cause *)); +static void atm_cm_closevc __P((Atm_connvc *)); +static void atm_cm_timeout __P((struct atm_time *)); +static KTimeout_ret atm_cm_procinq __P((void *)); +static void atm_cm_incall __P((Atm_connvc *)); +static int atm_cm_accept __P((Atm_connvc *, Atm_connection *)); + +/* + * Local variables + */ +static Queue_t atm_connection_queue = {NULL}; +static Queue_t atm_incoming_queue = {NULL}; +static int atm_incoming_qlen = 0; +static Atm_connection *atm_listen_queue = NULL; +static struct attr_cause atm_cause_tmpl = + {T_ATM_PRESENT, {T_ATM_ITU_CODING, T_ATM_LOC_USER, 0, {0, 0, 0, 0}}}; + +/* + * Stack commands, indexed by API + */ +static struct { + int init; + int term; +} atm_stackcmds[] = { + {CPCS_INIT, CPCS_TERM}, /* CMAPI_CPCS */ + {SSCF_UNI_INIT, SSCF_UNI_TERM}, /* CMAPI_SAAL */ + {SSCOP_INIT, SSCOP_TERM}, /* CMAPI_SSCOP */ +}; + + +static struct sp_info atm_connection_pool = { + "atm connection pool", /* si_name */ + sizeof(Atm_connection), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; +static struct sp_info atm_connvc_pool = { + "atm connection vcc pool", /* si_name */ + sizeof(Atm_connvc), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + + +/* + * Initiate Outgoing ATM Call + * + * Called by an endpoint service to create a new Connection Manager API + * instance and to initiate an outbound ATM connection. The endpoint + * provided token will be used in all further CM -> endpoint function + * calls, and the returned connection block pointer must be used in all + * subsequent endpoint -> CM function calls. + * + * If the return indicates that the connection setup has been immediately + * successful (typically only for PVCs and shared SVCs), then the connection + * is ready for data transmission. + * + * If the return indicates that the connection setup is still in progress, + * then the endpoint must wait for notification from the Connection Manager + * indicating the final status of the call setup. If the call setup completes + * successfully, then a "call connected" notification will be sent to the + * endpoint by the Connection Manager. If the call setup fails, then the + * endpoint will receive a "call cleared" notification. + * + * All connection instances must be freed with an atm_cm_release() call. + * + * Arguments: + * epp pointer to endpoint definition structure + * token endpoint's connection instance token + * ap pointer to requested connection attributes + * copp pointer to location to return allocated connection block + * + * Returns: + * 0 connection has been successfully established + * EINPROGRESS connection establishment is in progress + * errno connection failed - reason indicated + * + */ +int +atm_cm_connect(epp, token, ap, copp) + Atm_endpoint *epp; + void *token; + Atm_attributes *ap; + Atm_connection **copp; +{ + Atm_connection *cop; + Atm_connvc *cvp; + struct atm_pif *pip; + struct sigmgr *smp; + struct stack_list sl; + void (*upf)__P((int, void *, int, int)); + int s, sli, err, err2; + + *copp = NULL; + cvp = NULL; + + /* + * Get a connection block + */ + cop = (Atm_connection *)atm_allocate(&atm_connection_pool); + if (cop == NULL) + return (ENOMEM); + + /* + * Initialize connection info + */ + cop->co_endpt = epp; + cop->co_toku = token; + + /* + * Initialize stack list index + */ + sli = 0; + + /* + * Validate and extract useful attribute information + */ + + /* + * Must specify a network interface (validated below) + */ + if (ap->nif == NULL) { + err = EINVAL; + goto done; + } + + /* + * Check out Data API + */ + switch (ap->api) { + + case CMAPI_CPCS: + upf = atm_cm_cpcs_upper; + break; + + case CMAPI_SAAL: + sl.sl_sap[sli++] = SAP_SSCF_UNI; + sl.sl_sap[sli++] = SAP_SSCOP; + upf = atm_cm_saal_upper; + break; + + case CMAPI_SSCOP: + sl.sl_sap[sli++] = SAP_SSCOP; + upf = atm_cm_sscop_upper; + break; + + default: + err = EINVAL; + goto done; + } + + /* + * AAL Attributes + */ + if (ap->aal.tag != T_ATM_PRESENT) { + err = EINVAL; + goto done; + } + + switch (ap->aal.type) { + + case ATM_AAL5: + sl.sl_sap[sli++] = SAP_CPCS_AAL5; + sl.sl_sap[sli++] = SAP_SAR_AAL5; + sl.sl_sap[sli++] = SAP_ATM; + break; + + case ATM_AAL3_4: + sl.sl_sap[sli++] = SAP_CPCS_AAL3_4; + sl.sl_sap[sli++] = SAP_SAR_AAL3_4; + sl.sl_sap[sli++] = SAP_ATM; + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Broadband Bearer Attributes + */ + if (ap->bearer.tag != T_ATM_PRESENT) { + err = EINVAL; + goto done; + } + + switch (ap->bearer.v.connection_configuration) { + + case T_ATM_1_TO_1: + cop->co_flags |= COF_P2P; + break; + + case T_ATM_1_TO_MANY: + /* Not supported */ + cop->co_flags |= COF_P2MP; + err = EINVAL; + goto done; + + default: + err = EINVAL; + goto done; + } + + /* + * Logical Link Control Attributes + */ + if (ap->llc.tag == T_ATM_PRESENT) { + if ((ap->blli.tag_l2 != T_ATM_PRESENT) || + (ap->blli.v.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) || + (ap->blli.v.layer_2_protocol.ID.simple_ID != + T_ATM_BLLI2_I8802) || + (ap->llc.v.llc_len < T_ATM_LLC_MIN_LEN) || + (ap->llc.v.llc_len > T_ATM_LLC_MAX_LEN)) { + err = EINVAL; + goto done; + } + cop->co_mpx = ATM_ENC_LLC; + cop->co_llc = ap->llc; + } else + cop->co_mpx = ATM_ENC_NULL; + + /* + * Called Party Attributes + */ + if (ap->called.tag != T_ATM_PRESENT) { + err = EINVAL; + goto done; + } + + if ((ap->called.addr.address_format == T_ATM_ABSENT) || + (ap->called.addr.address_length == 0)) { + err = EINVAL; + goto done; + } + + /* + * Calling Party Attributes + */ + if (ap->calling.tag != T_ATM_ABSENT) { + err = EINVAL; + goto done; + } + + /* + * Quality of Service Attributes + */ + if (ap->qos.tag != T_ATM_PRESENT) { + err = EINVAL; + goto done; + } + + /* + * Terminate stack list + */ + sl.sl_sap[sli] = 0; + + s = splnet(); + + /* + * Let multiplexors decide whether we need a new VCC + */ + switch (cop->co_mpx) { + + case ATM_ENC_NULL: + /* + * All of these connections require a new VCC + */ + break; + + case ATM_ENC_LLC: + /* + * See if we can share an existing LLC connection + */ + cvp = atm_cm_share_llc(ap); + if (cvp == NULL) + break; + + /* + * We've got a connection to share + */ + cop->co_connvc = cvp; + if (cvp->cvc_state == CVCS_ACTIVE) { + cop->co_state = COS_ACTIVE; + err = 0; + } else { + cop->co_state = COS_OUTCONN; + err = EINPROGRESS; + } + LINK2TAIL(cop, Atm_connection, cvp->cvc_conn->co_mxh, co_next); + cop->co_mxh = cvp->cvc_conn->co_mxh; + *copp = cop; + + (void) splx(s); + return (err); + + default: + panic("atm_cm_connect: unknown mpx"); + } + + /* + * If we get here, it means we need to create + * a new VCC for this connection + */ + + /* + * Validate that network interface is registered and that + * a signalling manager is attached + */ + for (pip = atm_interface_head; pip != NULL; pip = pip->pif_next) { + struct atm_nif *nip; + for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) { + if (nip == ap->nif) + break; + } + if (nip) + break; + } + if (pip == NULL) { + err = ENXIO; + goto donex; + } + + if ((smp = pip->pif_sigmgr) == NULL) { + err = ENXIO; + goto donex; + } + + /* + * Get a connection VCC block + */ + cvp = (Atm_connvc *)atm_allocate(&atm_connvc_pool); + if (cvp == NULL) { + err = ENOMEM; + goto donex; + } + + /* + * Save VCC attributes + */ + cvp->cvc_attr = *ap; + cvp->cvc_flags |= CVCF_CALLER; + + /* + * Link the control blocks + */ + cop->co_connvc = cvp; + cvp->cvc_conn = cop; + cvp->cvc_sigmgr = smp; + + /* + * Create a service stack + */ + err = atm_create_stack(cvp, &sl, upf); + if (err) { + cvp->cvc_state = CVCS_CLEAR; + atm_cm_closevc(cvp); + goto donex; + } + + /* + * Let the signalling manager handle the VCC creation + */ + cvp->cvc_state = CVCS_SETUP; + switch ((*smp->sm_setup)(cvp, &err)) { + + case CALL_CONNECTED: + /* + * Connection is fully setup - initialize the stack + */ + cvp->cvc_state = CVCS_INIT; + STACK_CALL(atm_stackcmds[ap->api].init, cvp->cvc_lower, + cvp->cvc_tokl, cvp, ap->api_init, 0, err2); + if (err2) + panic("atm_cm_connect: init"); + + if (cvp->cvc_flags & CVCF_ABORTING) { + /* + * Someone on the stack bailed out...schedule the + * VCC and stack termination + */ + atm_cm_closevc(cvp); + err = EFAULT; + } else { + /* + * Everything looks fine from here + */ + cvp->cvc_state = CVCS_ACTIVE; + cop->co_state = COS_ACTIVE; + } + break; + + case CALL_FAILED: + /* + * Terminate stack and clean up before we leave + */ + cvp->cvc_state = CVCS_CLEAR; + atm_cm_closevc(cvp); + break; + + case CALL_PROCEEDING: + /* + * We'll just wait for final call status + */ + cop->co_state = COS_OUTCONN; + err = EINPROGRESS; + break; + + default: + panic("atm_cm_connect: setup"); + } + +donex: + (void) splx(s); + +done: + if (err && err != EINPROGRESS) { + /* + * Undo any partial setup stuff + */ + if (cop) + atm_free((caddr_t)cop); + } else { + /* + * Finish connection setup + */ + s = splnet(); + cvp->cvc_flags |= CVCF_CONNQ; + ENQUEUE(cvp, Atm_connvc, cvc_q, atm_connection_queue); + LINK2TAIL(cop, Atm_connection, cop->co_mxh, co_next); + (void) splx(s); + *copp = cop; + } + return (err); +} + + +/* + * Listen for Incoming ATM Calls + * + * Called by an endpoint service in order to indicate its willingness to + * accept certain incoming calls. The types of calls which the endpoint + * is prepared to accept are specified in the Atm_attributes parameter. + * + * For each call which meets the criteria specified by the endpoint, the + * endpoint service will receive an incoming call notification via the + * endpoint's ep_incoming() function. + * + * To cancel the listening connection, the endpoint user should invoke + * atm_cm_release(). + * + * Arguments: + * epp pointer to endpoint definition structure + * token endpoint's listen instance token + * ap pointer to listening connection attributes + * copp pointer to location to return allocated connection block + * + * Returns: + * 0 listening connection installed + * errno listen failed - reason indicated + * + */ +int +atm_cm_listen(epp, token, ap, copp) + Atm_endpoint *epp; + void *token; + Atm_attributes *ap; + Atm_connection **copp; +{ + Atm_connection *cop; + int s, err = 0; + + *copp = NULL; + + /* + * Get a connection block + */ + cop = (Atm_connection *)atm_allocate(&atm_connection_pool); + if (cop == NULL) + return (ENOMEM); + + /* + * Initialize connection info + */ + cop->co_endpt = epp; + cop->co_toku = token; + cop->co_mxh = cop; + + /* + * Validate and extract useful attribute information + */ + + /* + * Check out Data API + */ + switch (ap->api) { + + case CMAPI_CPCS: + case CMAPI_SAAL: + case CMAPI_SSCOP: + break; + + default: + err = EINVAL; + goto done; + } + + /* + * AAL Attributes + */ + switch (ap->aal.tag) { + + case T_ATM_PRESENT: + + switch (ap->aal.type) { + + case ATM_AAL5: + case ATM_AAL3_4: + break; + + default: + err = EINVAL; + goto done; + } + break; + + case T_ATM_ABSENT: + case T_ATM_ANY: + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Broadband High Layer Information Attributes + */ + switch (ap->bhli.tag) { + + case T_ATM_PRESENT: + case T_ATM_ABSENT: + case T_ATM_ANY: + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Broadband Low Layer Information Attributes + */ + switch (ap->blli.tag_l2) { + + case T_ATM_PRESENT: + case T_ATM_ABSENT: + case T_ATM_ANY: + break; + + default: + err = EINVAL; + goto done; + } + + switch (ap->blli.tag_l3) { + + case T_ATM_PRESENT: + case T_ATM_ABSENT: + case T_ATM_ANY: + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Logical Link Control Attributes + */ + switch (ap->llc.tag) { + + case T_ATM_PRESENT: + if ((ap->blli.tag_l2 != T_ATM_PRESENT) || + (ap->blli.v.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) || + (ap->blli.v.layer_2_protocol.ID.simple_ID != + T_ATM_BLLI2_I8802) || + (ap->llc.v.llc_len < T_ATM_LLC_MIN_LEN) || + (ap->llc.v.llc_len > T_ATM_LLC_MAX_LEN)) { + err = EINVAL; + goto done; + } + cop->co_mpx = ATM_ENC_LLC; + cop->co_llc = ap->llc; + break; + + case T_ATM_ABSENT: + case T_ATM_ANY: + cop->co_mpx = ATM_ENC_NULL; + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Called Party Attributes + */ + switch (ap->called.tag) { + + case T_ATM_PRESENT: + switch (ap->called.addr.address_format) { + + case T_ATM_ABSENT: + ap->called.tag = T_ATM_ABSENT; + break; + + case T_ATM_PVC_ADDR: + err = EINVAL; + goto done; + } + break; + + case T_ATM_ABSENT: + case T_ATM_ANY: + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Get an attribute block and save listening attributes + */ + cop->co_lattr = (Atm_attributes *)atm_allocate(&atm_attributes_pool); + if (cop->co_lattr == NULL) { + err = ENOMEM; + goto done; + } + *cop->co_lattr = *ap; + + /* + * Now try to register the listening connection + */ + s = splnet(); + if (atm_cm_match(cop->co_lattr, NULL) != NULL) { + /* + * Can't have matching listeners + */ + err = EADDRINUSE; + goto donex; + } + cop->co_state = COS_LISTEN; + LINK2TAIL(cop, Atm_connection, atm_listen_queue, co_next); + +donex: + (void) splx(s); + +done: + if (err) { + /* + * Undo any partial setup stuff + */ + if (cop) { + if (cop->co_lattr) + atm_free((caddr_t)cop->co_lattr); + atm_free((caddr_t)cop); + } + } else { + /* + * Finish connection setup + */ + *copp = cop; + } + return (err); +} + + +/* + * Add to LLC Connection + * + * Called by an endpoint service to create a new Connection Manager API + * instance to be associated with an LLC-multiplexed connection instance + * which has been previously created. The endpoint provided token will + * be used in all further CM -> endpoint function calls, and the returned + * connection block pointer must be used in all subsequent endpoint -> CM + * function calls. + * + * If the return indicates that the connection setup has been immediately + * successful, then the connection is ready for data transmission. + * + * If the return indicates that the connection setup is still in progress, + * then the endpoint must wait for notification from the Connection Manager + * indicating the final status of the call setup. If the call setup completes + * successfully, then a "call connected" notification will be sent to the + * endpoint by the Connection Manager. If the call setup fails, then the + * endpoint will receive a "call cleared" notification. + * + * All connection instances must be freed with an atm_cm_release() call. + * + * Arguments: + * epp pointer to endpoint definition structure + * token endpoint's connection instance token + * llc pointer to llc attributes for new connection + * ecop pointer to existing connection block + * copp pointer to location to return allocated connection block + * + * Returns: + * 0 connection has been successfully established + * EINPROGRESS connection establishment is in progress + * errno addllc failed - reason indicated + * + */ +int +atm_cm_addllc(epp, token, llc, ecop, copp) + Atm_endpoint *epp; + void *token; + struct attr_llc *llc; + Atm_connection *ecop; + Atm_connection **copp; +{ + Atm_connection *cop, *cop2; + Atm_connvc *cvp; + int s, err; + + *copp = NULL; + + /* + * Check out requested LLC attributes + */ + if ((llc->tag != T_ATM_PRESENT) || + ((llc->v.flags & T_ATM_LLC_SHARING) == 0) || + (llc->v.llc_len < T_ATM_LLC_MIN_LEN) || + (llc->v.llc_len > T_ATM_LLC_MAX_LEN)) + return (EINVAL); + + /* + * Get a connection block + */ + cop = (Atm_connection *)atm_allocate(&atm_connection_pool); + if (cop == NULL) + return (ENOMEM); + + /* + * Initialize connection info + */ + cop->co_endpt = epp; + cop->co_toku = token; + cop->co_llc = *llc; + + s = splnet(); + + /* + * Ensure that supplied connection is really valid + */ + cop2 = NULL; + for (cvp = Q_HEAD(atm_connection_queue, Atm_connvc); cvp; + cvp = Q_NEXT(cvp, Atm_connvc, cvc_q)) { + for (cop2 = cvp->cvc_conn; cop2; cop2 = cop2->co_next) { + if (ecop == cop2) + break; + } + if (cop2) + break; + } + if (cop2 == NULL) { + err = ENOENT; + goto done; + } + + switch (ecop->co_state) { + + case COS_OUTCONN: + case COS_INACCEPT: + err = EINPROGRESS; + break; + + case COS_ACTIVE: + err = 0; + break; + + default: + err = EINVAL; + goto done; + } + + /* + * Connection must be LLC multiplexed and shared + */ + if ((ecop->co_mpx != ATM_ENC_LLC) || + ((ecop->co_llc.v.flags & T_ATM_LLC_SHARING) == 0)) { + err = EINVAL; + goto done; + } + + /* + * This new LLC header must be unique for this VCC + */ + cop2 = ecop->co_mxh; + while (cop2) { + int i = MIN(llc->v.llc_len, cop2->co_llc.v.llc_len); + + if (KM_CMP(llc->v.llc_info, cop2->co_llc.v.llc_info, i) == 0) { + err = EINVAL; + goto done; + } + + cop2 = cop2->co_next; + } + + /* + * Everything seems to check out + */ + cop->co_flags = ecop->co_flags; + cop->co_state = ecop->co_state; + cop->co_mpx = ecop->co_mpx; + cop->co_connvc = ecop->co_connvc; + + LINK2TAIL(cop, Atm_connection, ecop->co_mxh, co_next); + cop->co_mxh = ecop->co_mxh; + +done: + (void) splx(s); + + if (err && err != EINPROGRESS) { + /* + * Undo any partial setup stuff + */ + if (cop) + atm_free((caddr_t)cop); + } else { + /* + * Pass new connection back to caller + */ + *copp = cop; + } + return (err); +} + + +/* + * XXX + * + * Arguments: + * cop pointer to connection block + * id identifier for party to be added + * addr address of party to be added + * + * Returns: + * 0 addparty successful + * errno addparty failed - reason indicated + * + */ +int +atm_cm_addparty(cop, id, addr) + Atm_connection *cop; + int id; + struct t_atm_sap *addr; +{ + return (0); +} + + +/* + * XXX + * + * Arguments: + * cop pointer to connection block + * id identifier for party to be added + * cause pointer to cause of drop + * + * Returns: + * 0 dropparty successful + * errno dropparty failed - reason indicated + * + */ +int +atm_cm_dropparty(cop, id, cause) + Atm_connection *cop; + int id; + struct t_atm_cause *cause; +{ + return (0); +} + + +/* + * Release Connection Resources + * + * Called by the endpoint service in order to terminate an ATM connection + * and to release all system resources for the connection. This function + * must be called for every allocated connection instance and must only + * be called by the connection's owner. + * + * Arguments: + * cop pointer to connection block + * cause pointer to cause of release + * + * Returns: + * 0 release successful + * errno release failed - reason indicated + * + */ +int +atm_cm_release(cop, cause) + Atm_connection *cop; + struct t_atm_cause *cause; +{ + Atm_connvc *cvp; + int s; + + s = splnet(); + + /* + * First, a quick state validation check + */ + switch (cop->co_state) { + + case COS_OUTCONN: + case COS_LISTEN: + case COS_INACCEPT: + case COS_ACTIVE: + case COS_CLEAR: + /* + * Break link to user + */ + cop->co_toku = NULL; + break; + + case COS_INCONN: + (void) splx(s); + return (EFAULT); + + default: + panic("atm_cm_release: bogus conn state"); + } + + /* + * Check out the VCC state too + */ + if (cvp = cop->co_connvc) { + + switch (cvp->cvc_state) { + + case CVCS_SETUP: + case CVCS_INIT: + case CVCS_ACCEPT: + case CVCS_ACTIVE: + break; + + case CVCS_INCOMING: + (void) splx(s); + return (EFAULT); + + case CVCS_CLEAR: + (void) splx(s); + return (EALREADY); + + default: + panic("atm_cm_release: bogus connvc state"); + } + + /* + * If we're the only connection, terminate the VCC + */ + if ((cop->co_mxh == cop) && (cop->co_next == NULL)) { + cvp->cvc_attr.cause.tag = T_ATM_PRESENT; + cvp->cvc_attr.cause.v = *cause; + atm_cm_closevc(cvp); + } + } + + /* + * Now get rid of the connection + */ + atm_cm_closeconn(cop, cause); + + return (0); +} + + +/* + * Abort an ATM Connection VCC + * + * This function allows any non-owner kernel entity to request an + * immediate termination of an ATM VCC. This will normally be called + * when encountering a catastrophic error condition that cannot be + * resolved via the available stack protocols. The connection manager + * will schedule the connection's termination, including notifying the + * connection owner of the termination. + * + * This function should only be called by a stack entity instance. After + * calling the function, the caller should set a protocol state which just + * waits for a <sap>_TERM stack command to be delivered. + * + * Arguments: + * cvp pointer to connection VCC block + * cause pointer to cause of abort + * + * Returns: + * 0 abort successful + * errno abort failed - reason indicated + * + */ +int +atm_cm_abort(cvp, cause) + Atm_connvc *cvp; + struct t_atm_cause *cause; +{ + ATM_DEBUG2("atm_cm_abort: cvp=0x%x cause=%d\n", + (int)cvp, cause->cause_value); + + /* + * Note that we're aborting + */ + cvp->cvc_flags |= CVCF_ABORTING; + + switch (cvp->cvc_state) { + + case CVCS_INIT: + /* + * In-line code will handle this + */ + cvp->cvc_attr.cause.tag = T_ATM_PRESENT; + cvp->cvc_attr.cause.v = *cause; + break; + + case CVCS_SETUP: + case CVCS_ACCEPT: + case CVCS_ACTIVE: + /* + * Schedule connection termination, since we want + * to avoid any sequencing interactions + */ + cvp->cvc_attr.cause.tag = T_ATM_PRESENT; + cvp->cvc_attr.cause.v = *cause; + CVC_TIMER(cvp, 0); + break; + + case CVCS_REJECT: + case CVCS_RELEASE: + case CVCS_CLEAR: + case CVCS_TERM: + /* + * Ignore abort, as we're already terminating + */ + break; + + default: + log(LOG_ERR, + "atm_cm_abort: invalid state: cvp=0x%x, state=%d\n", + (int)cvp, cvp->cvc_state); + } + return (0); +} + + +/* + * Incoming ATM Call Received + * + * Called by a signalling manager to indicate that a new call request has + * been received. This function will allocate and initialize the connection + * manager control blocks and queue this call request. The call request + * processing function, atm_cm_procinq(), will be scheduled to perform the + * call processing. + * + * Arguments: + * vcp pointer to incoming call's VCC control block + * ap pointer to incoming call's attributes + * + * Returns: + * 0 call queuing successful + * errno call queuing failed - reason indicated + * + */ +int +atm_cm_incoming(vcp, ap) + struct vccb *vcp; + Atm_attributes *ap; +{ + Atm_connvc *cvp; + int s, err; + + + /* + * Do some minimal attribute validation + */ + + /* + * Must specify a network interface + */ + if (ap->nif == NULL) + return (EINVAL); + + /* + * AAL Attributes + */ + if ((ap->aal.tag != T_ATM_PRESENT) || + ((ap->aal.type != ATM_AAL5) && + (ap->aal.type != ATM_AAL3_4))) + return (EINVAL); + + /* + * Traffic Descriptor Attributes + */ + if ((ap->traffic.tag != T_ATM_PRESENT) && + (ap->traffic.tag != T_ATM_ABSENT)) + return (EINVAL); + + /* + * Broadband Bearer Attributes + */ + if ((ap->bearer.tag != T_ATM_PRESENT) || + ((ap->bearer.v.connection_configuration != T_ATM_1_TO_1) && + (ap->bearer.v.connection_configuration != T_ATM_1_TO_MANY))) + return (EINVAL); + + /* + * Broadband High Layer Attributes + */ + if ((ap->bhli.tag != T_ATM_PRESENT) && + (ap->bhli.tag != T_ATM_ABSENT)) + return (EINVAL); + + /* + * Broadband Low Layer Attributes + */ + if ((ap->blli.tag_l2 != T_ATM_PRESENT) && + (ap->blli.tag_l2 != T_ATM_ABSENT)) + return (EINVAL); + if ((ap->blli.tag_l3 != T_ATM_PRESENT) && + (ap->blli.tag_l3 != T_ATM_ABSENT)) + return (EINVAL); + + /* + * Logical Link Control Attributes + */ + if (ap->llc.tag == T_ATM_PRESENT) + return (EINVAL); + ap->llc.tag = T_ATM_ANY; + + /* + * Called Party Attributes + */ + if ((ap->called.tag != T_ATM_PRESENT) || + (ap->called.addr.address_format == T_ATM_ABSENT)) + return (EINVAL); + if (ap->called.tag == T_ATM_ABSENT) { + ap->called.addr.address_format = T_ATM_ABSENT; + ap->called.addr.address_length = 0; + ap->called.subaddr.address_format = T_ATM_ABSENT; + ap->called.subaddr.address_length = 0; + } + + /* + * Calling Party Attributes + */ + if ((ap->calling.tag != T_ATM_PRESENT) && + (ap->calling.tag != T_ATM_ABSENT)) + return (EINVAL); + if (ap->calling.tag == T_ATM_ABSENT) { + ap->calling.addr.address_format = T_ATM_ABSENT; + ap->calling.addr.address_length = 0; + ap->calling.subaddr.address_format = T_ATM_ABSENT; + ap->calling.subaddr.address_length = 0; + } + + /* + * Quality of Service Attributes + */ + if (ap->qos.tag != T_ATM_PRESENT) + return (EINVAL); + + /* + * Transit Network Attributes + */ + if ((ap->transit.tag != T_ATM_PRESENT) && + (ap->transit.tag != T_ATM_ABSENT)) + return (EINVAL); + + /* + * Cause Attributes + */ + if ((ap->cause.tag != T_ATM_PRESENT) && + (ap->cause.tag != T_ATM_ABSENT)) + return (EINVAL); + + /* + * Get a connection VCC block + */ + cvp = (Atm_connvc *)atm_allocate(&atm_connvc_pool); + if (cvp == NULL) { + err = ENOMEM; + goto fail; + } + + /* + * Initialize the control block + */ + cvp->cvc_vcc = vcp; + cvp->cvc_sigmgr = vcp->vc_pif->pif_sigmgr; + cvp->cvc_attr = *ap; + cvp->cvc_state = CVCS_INCOMING; + + /* + * Control queue length + */ + s = splnet(); + if (atm_incoming_qlen >= ATM_CALLQ_MAX) { + (void) splx(s); + err = EBUSY; + goto fail; + } + + /* + * Queue request and schedule call processing function + */ + cvp->cvc_flags |= CVCF_INCOMQ; + ENQUEUE(cvp, Atm_connvc, cvc_q, atm_incoming_queue); + if (atm_incoming_qlen++ == 0) { + timeout(atm_cm_procinq, (void *)0, 0); + } + + /* + * Link for signalling manager + */ + vcp->vc_connvc = cvp; + + (void) splx(s); + + return (0); + +fail: + /* + * Free any resources + */ + if (cvp) + atm_free((caddr_t)cvp); + return (err); +} + + +/* + * VCC Connected Notification + * + * This function is called by a signalling manager as notification that a + * VCC call setup has been successful. + * + * Arguments: + * cvp pointer to connection VCC block + * + * Returns: + * none + * + */ +void +atm_cm_connected(cvp) + Atm_connvc *cvp; +{ + Atm_connection *cop, *cop2; + KBuffer *m; + int s, err; + + s = splnet(); + + /* + * Validate connection vcc + */ + switch (cvp->cvc_state) { + + case CVCS_SETUP: + /* + * Initialize the stack + */ + cvp->cvc_state = CVCS_INIT; + STACK_CALL(atm_stackcmds[cvp->cvc_attr.api].init, + cvp->cvc_lower, cvp->cvc_tokl, + cvp, cvp->cvc_attr.api_init, 0, err); + if (err) + panic("atm_cm_connected: init"); + + if (cvp->cvc_flags & CVCF_ABORTING) { + /* + * Someone on the stack bailed out...notify all of the + * connections and schedule the VCC termination + */ + cop = cvp->cvc_conn; + while (cop) { + cop2 = cop->co_next; + atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); + cop = cop2; + } + atm_cm_closevc(cvp); + (void) splx(s); + return; + } + break; + + case CVCS_ACCEPT: + /* + * Stack already initialized + */ + break; + + default: + panic("atm_cm_connected: connvc state"); + } + + /* + * VCC is ready for action + */ + cvp->cvc_state = CVCS_ACTIVE; + + /* + * Notify all connections that the call has completed + */ + cop = cvp->cvc_conn; + while (cop) { + cop2 = cop->co_next; + + switch (cop->co_state) { + + case COS_OUTCONN: + case COS_INACCEPT: + cop->co_state = COS_ACTIVE; + (*cop->co_endpt->ep_connected)(cop->co_toku); + break; + + case COS_ACTIVE: + /* + * May get here if an ep_connected() call (from + * above) results in an atm_cm_addllc() call for + * the just connected connection. + */ + break; + + default: + panic("atm_cm_connected: connection state"); + } + + cop = cop2; + } + + (void) splx(s); + + /* + * Input any queued packets + */ + while (m = cvp->cvc_rcvq) { + cvp->cvc_rcvq = KB_QNEXT(m); + cvp->cvc_rcvqlen--; + KB_QNEXT(m) = NULL; + + /* + * Currently only supported for CPCS API + */ + atm_cm_cpcs_upper(CPCS_UNITDATA_SIG, cvp, (int)m, 0); + } + + return; +} + + +/* + * VCC Cleared Notification + * + * This function is called by a signalling manager as notification that a + * VCC call has been cleared. The cause information describing the reason + * for the call clearing will be contained in the connection VCC attributes. + * + * Arguments: + * cvp pointer to connection VCC block + * + * Returns: + * none + * + */ +void +atm_cm_cleared(cvp) + Atm_connvc *cvp; +{ + Atm_connection *cop, *cop2; + int s; + +#ifdef DIAGNOSTIC + if ((cvp->cvc_state == CVCS_FREE) || + (cvp->cvc_state >= CVCS_CLEAR)) + panic("atm_cm_cleared"); +#endif + + cvp->cvc_state = CVCS_CLEAR; + + s = splnet(); + + /* + * Terminate all connections + */ + cop = cvp->cvc_conn; + while (cop) { + cop2 = cop->co_next; + atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); + cop = cop2; + } + + /* + * Clean up connection VCC + */ + atm_cm_closevc(cvp); + + (void) splx(s); + + return; +} + + +/* + * Process Incoming Call Queue + * + * This function is scheduled by atm_cm_incoming() in order to process + * all the entries on the incoming call queue. + * + * Arguments: + * arg argument passed on timeout() call + * + * Returns: + * none + * + */ +static KTimeout_ret +atm_cm_procinq(arg) + void *arg; +{ + Atm_connvc *cvp; + int cnt = 0, s; + + /* + * Only process incoming calls up to our quota + */ + while (cnt++ < ATM_CALLQ_MAX) { + + s = splnet(); + + /* + * Get next awaiting call + */ + cvp = Q_HEAD(atm_incoming_queue, Atm_connvc); + if (cvp == NULL) { + (void) splx(s); + break; + } + DEQUEUE(cvp, Atm_connvc, cvc_q, atm_incoming_queue); + atm_incoming_qlen--; + cvp->cvc_flags &= ~CVCF_INCOMQ; + + /* + * Handle the call + */ + atm_cm_incall(cvp); + + (void) splx(s); + } + + /* + * If we've expended our quota, reschedule ourselves + */ + if (cnt >= ATM_CALLQ_MAX) + timeout(atm_cm_procinq, (void *)0, 0); +} + + +/* + * Process Incoming Call + * + * This function will search through the listening queue and try to find + * matching endpoint(s) for the incoming call. If we find any, we will + * notify the endpoint service(s) of the incoming call and will then + * notify the signalling manager to progress the call to an active status. + * + * If there are no listeners for the call, the signalling manager will be + * notified of a call rejection. + * + * Called at splnet. + * + * Arguments: + * cvp pointer to connection VCC for incoming call + * + * Returns: + * none + * + */ +static void +atm_cm_incall(cvp) + Atm_connvc *cvp; +{ + Atm_connection *cop, *lcop, *hcop; + Atm_attributes attr; + int err; + + hcop = NULL; + lcop = NULL; + cop = NULL; + attr = cvp->cvc_attr; + + /* + * Look for matching listeners + */ + while (lcop = atm_cm_match(&attr, lcop)) { + + if (cop == NULL) { + /* + * Need a new connection block + */ + cop = (Atm_connection *) + atm_allocate(&atm_connection_pool); + if (cop == NULL) { + cvp->cvc_attr.cause = atm_cause_tmpl; + cvp->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_TEMPORARY_FAILURE; + goto fail; + } + } + + /* + * Initialize connection from listener and incoming call + */ + cop->co_mxh = NULL; + cop->co_state = COS_INCONN; + cop->co_mpx = lcop->co_mpx; + cop->co_endpt = lcop->co_endpt; + cop->co_llc = lcop->co_llc; + + switch (attr.bearer.v.connection_configuration) { + + case T_ATM_1_TO_1: + cop->co_flags |= COF_P2P; + break; + + case T_ATM_1_TO_MANY: + /* Not supported */ + cop->co_flags |= COF_P2MP; + cvp->cvc_attr.cause = atm_cause_tmpl; + cvp->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_BEARER_CAPABILITY_NOT_IMPLEMENTED; + goto fail; + } + + /* + * Notify endpoint of incoming call + */ + err = (*cop->co_endpt->ep_incoming) + (lcop->co_toku, cop, &cvp->cvc_attr, &cop->co_toku); + + if (err == 0) { + + /* + * Endpoint has accepted the call + * + * Setup call attributes + */ + if (hcop == NULL) { + cvp->cvc_attr.api = lcop->co_lattr->api; + cvp->cvc_attr.api_init = + lcop->co_lattr->api_init; + cvp->cvc_attr.llc = lcop->co_lattr->llc; + } + cvp->cvc_attr.headin = MAX(cvp->cvc_attr.headin, + lcop->co_lattr->headin); + + /* + * Setup connection info and queueing + */ + cop->co_state = COS_INACCEPT; + cop->co_connvc = cvp; + LINK2TAIL(cop, Atm_connection, hcop, co_next); + cop->co_mxh = hcop; + + /* + * Need a new connection block next time around + */ + cop = NULL; + + } else { + /* + * Endpoint refuses call + */ + goto fail; + } + } + + /* + * We're done looking for listeners + */ + if (hcop) { + /* + * Someone actually wants the call, so notify + * the signalling manager to continue + */ + cvp->cvc_flags |= CVCF_CONNQ; + ENQUEUE(cvp, Atm_connvc, cvc_q, atm_connection_queue); + if (atm_cm_accept(cvp, hcop)) + goto fail; + + } else { + /* + * Nobody around to take the call + */ + cvp->cvc_attr.cause = atm_cause_tmpl; + cvp->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_INCOMPATIBLE_DESTINATION; + goto fail; + } + + /* + * Clean up loose ends + */ + if (cop) + atm_free((caddr_t)cop); + + /* + * Call has been accepted + */ + return; + +fail: + /* + * Call failed - notify any endpoints of the call failure + */ + + /* + * Clean up loose ends + */ + if (cop) + atm_free((caddr_t)cop); + + if (cvp->cvc_attr.cause.tag != T_ATM_PRESENT) { + cvp->cvc_attr.cause = atm_cause_tmpl; + cvp->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_UNSPECIFIED_NORMAL; + } + cop = hcop; + while (cop) { + Atm_connection *cop2 = cop->co_next; + atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); + cop = cop2; + } + + /* + * Tell the signalling manager to reject the call + */ + atm_cm_closevc(cvp); + + return; +} + + +/* + * Accept an Incoming ATM Call + * + * Some endpoint service(s) wants to accept an incoming call, so the + * signalling manager will be notified to attempt to progress the call + * to an active status. + * + * If the signalling manager indicates that connection activation has + * been immediately successful, then all of the endpoints will be notified + * that the connection is ready for data transmission. + * + * If the return indicates that connection activation is still in progress, + * then the endpoints must wait for notification from the Connection Manager + * indicating the final status of the call setup. If the call setup completes + * successfully, then a "call connected" notification will be sent to the + * endpoints by the Connection Manager. If the call setup fails, then the + * endpoints will receive a "call cleared" notification. + * + * Called at splnet. + * + * Arguments: + * cvp pointer to connection VCC for incoming call + * cop pointer to head of accepted connections + * + * Returns: + * 0 connection has been successfully activated + * errno accept failed - reason indicated + * + */ +static int +atm_cm_accept(cvp, cop) + Atm_connvc *cvp; + Atm_connection *cop; +{ + struct stack_list sl; + void (*upf)__P((int, void *, int, int)); + int sli, err, err2; + + + /* + * Link vcc to connections + */ + cvp->cvc_conn = cop; + + /* + * Initialize stack list index + */ + sli = 0; + + /* + * Check out Data API + */ + switch (cvp->cvc_attr.api) { + + case CMAPI_CPCS: + upf = atm_cm_cpcs_upper; + break; + + case CMAPI_SAAL: + sl.sl_sap[sli++] = SAP_SSCF_UNI; + sl.sl_sap[sli++] = SAP_SSCOP; + upf = atm_cm_saal_upper; + break; + + case CMAPI_SSCOP: + sl.sl_sap[sli++] = SAP_SSCOP; + upf = atm_cm_sscop_upper; + break; + + default: + upf = NULL; + } + + /* + * AAL Attributes + */ + switch (cvp->cvc_attr.aal.type) { + + case ATM_AAL5: + sl.sl_sap[sli++] = SAP_CPCS_AAL5; + sl.sl_sap[sli++] = SAP_SAR_AAL5; + sl.sl_sap[sli++] = SAP_ATM; + break; + + case ATM_AAL3_4: + sl.sl_sap[sli++] = SAP_CPCS_AAL3_4; + sl.sl_sap[sli++] = SAP_SAR_AAL3_4; + sl.sl_sap[sli++] = SAP_ATM; + break; + } + + /* + * Terminate stack list + */ + sl.sl_sap[sli] = 0; + + /* + * Create a service stack + */ + err = atm_create_stack(cvp, &sl, upf); + if (err) { + goto done; + } + + /* + * Let the signalling manager finish the VCC activation + */ + switch ((*cvp->cvc_sigmgr->sm_accept)(cvp->cvc_vcc, &err)) { + + case CALL_PROCEEDING: + /* + * Note that we're not finished yet + */ + err = EINPROGRESS; + /* FALLTHRU */ + + case CALL_CONNECTED: + /* + * Initialize the stack now, even if the call isn't totally + * active yet. We want to avoid the delay between getting + * the "call connected" event and actually notifying the + * adapter to accept cells on the new VCC - if the delay is + * too long, then we end up dropping the first pdus sent by + * the caller. + */ + cvp->cvc_state = CVCS_INIT; + STACK_CALL(atm_stackcmds[cvp->cvc_attr.api].init, + cvp->cvc_lower, cvp->cvc_tokl, cvp, + cvp->cvc_attr.api_init, 0, err2); + if (err2) + panic("atm_cm_accept: init"); + + if (cvp->cvc_flags & CVCF_ABORTING) { + /* + * Someone on the stack bailed out...schedule the + * VCC and stack termination + */ + err = ECONNABORTED; + } else { + /* + * Everything looks fine from here + */ + if (err) + cvp->cvc_state = CVCS_ACCEPT; + else + cvp->cvc_state = CVCS_ACTIVE; + } + break; + + case CALL_FAILED: + /* + * Terminate stack and clean up before we leave + */ + cvp->cvc_state = CVCS_CLEAR; + break; + + default: + panic("atm_cm_accept: accept"); + } + +done: + if (err == 0) { + /* + * Call has been connected, notify endpoints + */ + while (cop) { + Atm_connection *cop2 = cop->co_next; + + cop->co_state = COS_ACTIVE; + (*cop->co_endpt->ep_connected)(cop->co_toku); + cop = cop2; + } + + } else if (err == EINPROGRESS) { + /* + * Call is still in progress, endpoint must wait + */ + err = 0; + + } else { + /* + * Let caller know we failed + */ + cvp->cvc_attr.cause = atm_cause_tmpl; + cvp->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_UNSPECIFIED_RESOURCE_UNAVAILABLE; + } + + return (err); +} + + +/* + * Match Attributes on Listening Queue + * + * This function will attempt to match the supplied connection attributes + * with one of the registered attributes in the listening queue. The pcop + * argument may be supplied in order to allow multiple listeners to share + * an incoming call (if supported by the listeners). + * + * Called at splnet. + * + * Arguments: + * ap pointer to attributes to be matched + * pcop pointer to the previously matched connection + * + * Returns: + * addr connection with which a match was found + * 0 no match found + * + */ +Atm_connection * +atm_cm_match(ap, pcop) + Atm_attributes *ap; + Atm_connection *pcop; +{ + Atm_connection *cop; + Atm_attributes *lap; + + + /* + * If we've already matched a listener... + */ + if (pcop) { + /* + * Make sure already matched listener supports sharing + */ + if ((pcop->co_mpx != ATM_ENC_LLC) || + ((pcop->co_llc.v.flags & T_ATM_LLC_SHARING) == 0)) + return (NULL); + + /* + * Position ourselves after the matched entry + */ + for (cop = atm_listen_queue; cop; cop = cop->co_next) { + if (cop == pcop) { + cop = pcop->co_next; + break; + } + } + } else { + /* + * Start search at top of listening queue + */ + cop = atm_listen_queue; + } + + /* + * Search through listening queue + */ + for (; cop; cop = cop->co_next) { + + lap = cop->co_lattr; + + /* + * If we're trying to share, check that this entry allows it + */ + if (pcop) { + if ((cop->co_mpx != ATM_ENC_LLC) || + ((cop->co_llc.v.flags & T_ATM_LLC_SHARING) == 0)) + continue; + } + + /* + * ALL "matchable" attributes must match + */ + + /* + * BHLI + */ + if (lap->bhli.tag == T_ATM_ABSENT) { + if (ap->bhli.tag == T_ATM_PRESENT) + continue; + } else if (lap->bhli.tag == T_ATM_PRESENT) { + if (ap->bhli.tag == T_ATM_ABSENT) + continue; + if (ap->bhli.tag == T_ATM_PRESENT) + if (KM_CMP(&lap->bhli.v, &ap->bhli.v, + sizeof(struct t_atm_bhli))) + continue; + } + + /* + * BLLI Layer 2 + */ + if (lap->blli.tag_l2 == T_ATM_ABSENT) { + if (ap->blli.tag_l2 == T_ATM_PRESENT) + continue; + } else if (lap->blli.tag_l2 == T_ATM_PRESENT) { + if (ap->blli.tag_l2 == T_ATM_ABSENT) + continue; + if (ap->blli.tag_l2 == T_ATM_PRESENT) { + if (KM_CMP(&lap->blli.v.layer_2_protocol.ID, + &ap->blli.v.layer_2_protocol.ID, + sizeof( + ap->blli.v.layer_2_protocol.ID))) + continue; + } + } + + /* + * BLLI Layer 3 + */ + if (lap->blli.tag_l3 == T_ATM_ABSENT) { + if (ap->blli.tag_l3 == T_ATM_PRESENT) + continue; + } else if (lap->blli.tag_l3 == T_ATM_PRESENT) { + if (ap->blli.tag_l3 == T_ATM_ABSENT) + continue; + if (ap->blli.tag_l3 == T_ATM_PRESENT) { + if (KM_CMP(&lap->blli.v.layer_3_protocol.ID, + &ap->blli.v.layer_3_protocol.ID, + sizeof( + ap->blli.v.layer_3_protocol.ID))) + continue; + } + } + + /* + * LLC + */ + if (lap->llc.tag == T_ATM_ABSENT) { + if (ap->llc.tag == T_ATM_PRESENT) + continue; + } else if (lap->llc.tag == T_ATM_PRESENT) { + if (ap->llc.tag == T_ATM_ABSENT) + continue; + if (ap->llc.tag == T_ATM_PRESENT) { + int i = MIN(lap->llc.v.llc_len, + ap->llc.v.llc_len); + + if (KM_CMP(lap->llc.v.llc_info, + ap->llc.v.llc_info, i)) + continue; + } + } + + /* + * AAL + */ + if (lap->aal.tag == T_ATM_ABSENT) { + if (ap->aal.tag == T_ATM_PRESENT) + continue; + } else if (lap->aal.tag == T_ATM_PRESENT) { + if (ap->aal.tag == T_ATM_ABSENT) + continue; + if (ap->aal.tag == T_ATM_PRESENT) { + if (lap->aal.type != ap->aal.type) + continue; + if (lap->aal.type == ATM_AAL5) { + if (lap->aal.v.aal5.SSCS_type != + ap->aal.v.aal5.SSCS_type) + continue; + } else { + if (lap->aal.v.aal4.SSCS_type != + ap->aal.v.aal4.SSCS_type) + continue; + } + } + } + + /* + * Called Party + */ + if (lap->called.tag == T_ATM_ABSENT) { + if (ap->called.tag == T_ATM_PRESENT) + continue; + } else if (lap->called.tag == T_ATM_PRESENT) { + if (ap->called.tag == T_ATM_ABSENT) + continue; + if (ap->called.tag == T_ATM_PRESENT) { + if ((!ATM_ADDR_EQUAL(&lap->called.addr, + &ap->called.addr)) || + (!ATM_ADDR_EQUAL(&lap->called.subaddr, + &ap->called.subaddr))) + continue; + } + } + + /* + * Found a full match - return it + */ + break; + } + + return (cop); +} + + +/* + * Find Shareable LLC VCC + * + * Given a endpoint-supplied connection attribute using LLC multiplexing, + * this function will attempt to locate an existing connection which meets + * the requirements of the supplied attributes. + * + * Called at splnet. + * + * Arguments: + * ap pointer to requested attributes + * + * Returns: + * addr shareable LLC connection VCC + * 0 no shareable VCC available + * + */ +static Atm_connvc * +atm_cm_share_llc(ap) + Atm_attributes *ap; +{ + Atm_connection *cop; + Atm_connvc *cvp; + + /* + * Is requestor willing to share? + */ + if ((ap->llc.v.flags & T_ATM_LLC_SHARING) == 0) + return (NULL); + + /* + * Try to find a shareable connection + */ + for (cvp = Q_HEAD(atm_connection_queue, Atm_connvc); cvp; + cvp = Q_NEXT(cvp, Atm_connvc, cvc_q)) { + + /* + * Dont use terminating connections + */ + switch (cvp->cvc_state) { + + case CVCS_SETUP: + case CVCS_ACCEPT: + case CVCS_ACTIVE: + break; + + default: + continue; + } + + /* + * Is connection LLC and shareable? + */ + if ((cvp->cvc_attr.llc.tag != T_ATM_PRESENT) || + ((cvp->cvc_attr.llc.v.flags & T_ATM_LLC_SHARING) == 0)) + continue; + + /* + * Match requested attributes with existing connection + */ + if (ap->nif != cvp->cvc_attr.nif) + continue; + + if ((ap->api != cvp->cvc_attr.api) || + (ap->api_init != cvp->cvc_attr.api_init)) + continue; + + /* + * Remote Party + */ + if (cvp->cvc_flags & CVCF_CALLER) { + if ((!ATM_ADDR_EQUAL(&ap->called.addr, + &cvp->cvc_attr.called.addr)) || + (!ATM_ADDR_EQUAL(&ap->called.subaddr, + &cvp->cvc_attr.called.subaddr))) + continue; + } else { + if (cvp->cvc_attr.calling.tag != T_ATM_PRESENT) + continue; + if ((!ATM_ADDR_EQUAL(&ap->called.addr, + &cvp->cvc_attr.calling.addr)) || + (!ATM_ADDR_EQUAL(&ap->called.subaddr, + &cvp->cvc_attr.calling.subaddr))) + continue; + } + + /* + * AAL + */ + if (ap->aal.type = ATM_AAL5) { + struct t_atm_aal5 *ap5, *cv5; + + ap5 = &ap->aal.v.aal5; + cv5 = &cvp->cvc_attr.aal.v.aal5; + + if ((cvp->cvc_attr.aal.type != ATM_AAL5) || + (ap5->SSCS_type != cv5->SSCS_type)) + continue; + + if (cvp->cvc_flags & CVCF_CALLER) { + if (ap5->forward_max_SDU_size > + cv5->forward_max_SDU_size) + continue; + } else { + if (ap5->forward_max_SDU_size > + cv5->backward_max_SDU_size) + continue; + } + } else { + struct t_atm_aal4 *ap4, *cv4; + + ap4 = &ap->aal.v.aal4; + cv4 = &cvp->cvc_attr.aal.v.aal4; + + if ((cvp->cvc_attr.aal.type != ATM_AAL3_4) || + (ap4->SSCS_type != cv4->SSCS_type)) + continue; + + if (cvp->cvc_flags & CVCF_CALLER) { + if (ap4->forward_max_SDU_size > + cv4->forward_max_SDU_size) + continue; + } else { + if (ap4->forward_max_SDU_size > + cv4->backward_max_SDU_size) + continue; + } + } + + /* + * Traffic Descriptor + */ + if ((ap->traffic.tag != T_ATM_PRESENT) || + (cvp->cvc_attr.traffic.tag != T_ATM_PRESENT) || + (ap->traffic.v.best_effort != T_YES) || + (cvp->cvc_attr.traffic.v.best_effort != T_YES)) + continue; + + /* + * Broadband Bearer + */ + if (ap->bearer.v.connection_configuration != + cvp->cvc_attr.bearer.v.connection_configuration) + continue; + + /* + * QOS + */ + if (cvp->cvc_flags & CVCF_CALLER) { + if ((ap->qos.v.forward.qos_class != + cvp->cvc_attr.qos.v.forward.qos_class) || + (ap->qos.v.backward.qos_class != + cvp->cvc_attr.qos.v.backward.qos_class)) + continue; + } else { + if ((ap->qos.v.forward.qos_class != + cvp->cvc_attr.qos.v.backward.qos_class) || + (ap->qos.v.backward.qos_class != + cvp->cvc_attr.qos.v.forward.qos_class)) + continue; + } + + /* + * The new LLC header must also be unique for this VCC + */ + for (cop = cvp->cvc_conn; cop; cop = cop->co_next) { + int i = MIN(ap->llc.v.llc_len, + cop->co_llc.v.llc_len); + + if (KM_CMP(ap->llc.v.llc_info, + cop->co_llc.v.llc_info, i) == 0) + break; + } + + /* + * If no header overlaps, then we're done + */ + if (cop == NULL) + break; + } + + return (cvp); +} + + +/* + * Close Connection + * + * This function will terminate a connection, including notifying the + * user, if necessary, and freeing up control block memory. The caller + * is responsible for managing the connection VCC. + * + * Called at splnet. + * + * Arguments: + * cop pointer to connection block + * cause pointer to cause of close + * + * Returns: + * none + * + */ +static void +atm_cm_closeconn(cop, cause) + Atm_connection *cop; + struct t_atm_cause *cause; +{ + + /* + * Decide whether user needs notification + */ + switch (cop->co_state) { + + case COS_OUTCONN: + case COS_LISTEN: + case COS_INCONN: + case COS_INACCEPT: + case COS_ACTIVE: + /* + * Yup, let 'em know connection is gone + */ + if (cop->co_toku) + (*cop->co_endpt->ep_cleared)(cop->co_toku, cause); + break; + + case COS_CLEAR: + /* + * Nope,they should know already + */ + break; + + default: + panic("atm_cm_closeconn: bogus state"); + } + + /* + * Unlink connection from its queues + */ + switch (cop->co_state) { + + case COS_LISTEN: + atm_free((caddr_t)cop->co_lattr); + UNLINK(cop, Atm_connection, atm_listen_queue, co_next); + break; + + default: + /* + * Remove connection from multiplexor queue + */ + if (cop->co_mxh != cop) { + /* + * Connection is down the chain, just unlink it + */ + UNLINK(cop, Atm_connection, cop->co_mxh, co_next); + + } else if (cop->co_next != NULL) { + /* + * Connection is at the head of a non-singleton chain, + * so unlink and reset the chain head + */ + Atm_connection *t, *nhd; + + t = nhd = cop->co_next; + while (t) { + t->co_mxh = nhd; + t = t->co_next; + } + if (nhd->co_connvc) + nhd->co_connvc->cvc_conn = nhd; + } + } + + /* + * Free the connection block + */ + cop->co_state = COS_FREE; + atm_free((caddr_t)cop); + + return; +} + + +/* + * Close Connection VCC + * + * This function will terminate a connection VCC, including releasing the + * the call to the signalling manager, terminating the VCC protocol stack, + * and freeing up control block memory. + * + * Called at splnet. + * + * Arguments: + * cvp pointer to connection VCC block + * + * Returns: + * none + * + */ +static void +atm_cm_closevc(cvp) + Atm_connvc *cvp; +{ + int err; + + /* + * Break links with the connection block + */ + cvp->cvc_conn = NULL; + + /* + * Cancel any running timer + */ + CVC_CANCEL(cvp); + + /* + * Free queued packets + */ + while (cvp->cvc_rcvq) { + KBuffer *m; + + m = cvp->cvc_rcvq; + cvp->cvc_rcvq = KB_QNEXT(m); + KB_QNEXT(m) = NULL; + KB_FREEALL(m); + } + + /* + * Unlink from any queues + */ + if (cvp->cvc_flags & CVCF_INCOMQ) { + DEQUEUE(cvp, Atm_connvc, cvc_q, atm_incoming_queue); + atm_incoming_qlen--; + cvp->cvc_flags &= ~CVCF_INCOMQ; + + } else if (cvp->cvc_flags & CVCF_CONNQ) { + DEQUEUE(cvp, Atm_connvc, cvc_q, atm_connection_queue); + cvp->cvc_flags &= ~CVCF_CONNQ; + } + + /* + * Release the signalling call + */ + switch (cvp->cvc_state) { + + case CVCS_SETUP: + case CVCS_INIT: + case CVCS_ACCEPT: + case CVCS_ACTIVE: + case CVCS_RELEASE: + if (cvp->cvc_vcc) { + cvp->cvc_state = CVCS_RELEASE; + switch ((*cvp->cvc_sigmgr->sm_release) + (cvp->cvc_vcc, &err)) { + + case CALL_CLEARED: + /* + * Looks good so far... + */ + break; + + case CALL_PROCEEDING: + /* + * We'll have to wait for the call to clear + */ + return; + + case CALL_FAILED: + /* + * If there's a memory shortage, retry later. + * Otherwise who knows what's going on.... + */ + if ((err == ENOMEM) || (err == ENOBUFS)) { + CVC_TIMER(cvp, 1 * ATM_HZ); + return; + } + log(LOG_ERR, + "atm_cm_closevc: release %d\n", err); + break; + } + } + break; + + case CVCS_INCOMING: + case CVCS_REJECT: + if (cvp->cvc_vcc) { + cvp->cvc_state = CVCS_REJECT; + switch ((*cvp->cvc_sigmgr->sm_reject) + (cvp->cvc_vcc, &err)) { + + case CALL_CLEARED: + /* + * Looks good so far... + */ + break; + + case CALL_FAILED: + /* + * If there's a memory shortage, retry later. + * Otherwise who knows what's going on.... + */ + if ((err == ENOMEM) || (err == ENOBUFS)) { + CVC_TIMER(cvp, 1 * ATM_HZ); + return; + } + log(LOG_ERR, + "atm_cm_closevc: reject %d\n", err); + break; + } + } + break; + + case CVCS_CLEAR: + case CVCS_TERM: + /* + * No need for anything here + */ + break; + + default: + panic("atm_cm_closevc: bogus state"); + } + + /* + * Now terminate the stack + */ + if (cvp->cvc_tokl) { + cvp->cvc_state = CVCS_TERM; + + /* + * Wait until stack is unwound before terminating + */ + if ((cvp->cvc_downcnt > 0) || (cvp->cvc_upcnt > 0)) { + CVC_TIMER(cvp, 0); + return; + } + + STACK_CALL(atm_stackcmds[cvp->cvc_attr.api].term, + cvp->cvc_lower, cvp->cvc_tokl, cvp, 0, 0, err); + + cvp->cvc_tokl = NULL; + } + + /* + * Let signalling manager finish up + */ + cvp->cvc_state = CVCS_FREE; + if (cvp->cvc_vcc) { + (void) (*cvp->cvc_sigmgr->sm_free)(cvp->cvc_vcc); + } + + /* + * Finally, free our own control blocks + */ + atm_free((caddr_t)cvp); + + return; +} + + +/* + * Process a Connection VCC timeout + * + * Called when a previously scheduled cvc control block timer expires. + * Processing will be based on the current cvc state. + * + * Called at splnet. + * + * Arguments: + * tip pointer to cvc timer control block + * + * Returns: + * none + * + */ +static void +atm_cm_timeout(tip) + struct atm_time *tip; +{ + Atm_connection *cop, *cop2; + Atm_connvc *cvp; + + /* + * Back-off to cvc control block + */ + cvp = (Atm_connvc *) + ((caddr_t)tip - (int)(&((Atm_connvc *)0)->cvc_time)); + + /* + * Process timeout based on protocol state + */ + switch (cvp->cvc_state) { + + case CVCS_SETUP: + case CVCS_ACCEPT: + case CVCS_ACTIVE: + /* + * Handle VCC abort + */ + if ((cvp->cvc_flags & CVCF_ABORTING) == 0) + goto logerr; + + /* + * Terminate all connections + */ + cop = cvp->cvc_conn; + while (cop) { + cop2 = cop->co_next; + atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); + cop = cop2; + } + + /* + * Terminate VCC + */ + atm_cm_closevc(cvp); + + break; + + case CVCS_REJECT: + case CVCS_RELEASE: + case CVCS_TERM: + /* + * Retry failed operation + */ + atm_cm_closevc(cvp); + break; + + default: +logerr: + log(LOG_ERR, + "atm_cm_timeout: invalid state: cvp=0x%x, state=%d\n", + (int)cvp, cvp->cvc_state); + } +} + + +/* + * CPCS User Control Commands + * + * This function is called by an endpoint user to pass a control command + * across a CPCS data API. Mostly we just send these down the stack. + * + * Arguments: + * cmd stack command code + * cop pointer to connection block + * arg argument + * + * Returns: + * 0 command output successful + * errno output failed - reason indicated + * + */ +int +atm_cm_cpcs_ctl(cmd, cop, arg) + int cmd; + Atm_connection *cop; + void *arg; +{ + Atm_connvc *cvp; + int err = 0; + + /* + * Validate connection state + */ + if (cop->co_state != COS_ACTIVE) { + err = EFAULT; + goto done; + } + + cvp = cop->co_connvc; + if (cvp->cvc_state != CVCS_ACTIVE) { + err = EFAULT; + goto done; + } + + if (cvp->cvc_attr.api != CMAPI_CPCS) { + err = EFAULT; + goto done; + } + + switch (cmd) { + + default: + err = EINVAL; + } + +done: + return (err); +} + + +/* + * CPCS Data Output + * + * This function is called by an endpoint user to output a data packet + * across a CPCS data API. After we've validated the connection state, the + * packet will be encapsulated (if necessary) and sent down the data stack. + * + * Arguments: + * cop pointer to connection block + * m pointer to packet buffer chain to be output + * + * Returns: + * 0 packet output successful + * errno output failed - reason indicated + * + */ +int +atm_cm_cpcs_data(cop, m) + Atm_connection *cop; + KBuffer *m; +{ + Atm_connvc *cvp; + struct attr_llc *llcp; + int err, space; + void *bp; + + + /* + * Validate connection state + */ + if (cop->co_state != COS_ACTIVE) { + err = EFAULT; + goto done; + } + + cvp = cop->co_connvc; + if (cvp->cvc_state != CVCS_ACTIVE) { + err = EFAULT; + goto done; + } + + if (cvp->cvc_attr.api != CMAPI_CPCS) { + err = EFAULT; + goto done; + } + + /* + * Add any packet encapsulation + */ + switch (cop->co_mpx) { + + case ATM_ENC_NULL: + /* + * None needed... + */ + break; + + case ATM_ENC_LLC: + /* + * Need to add an LLC header + */ + llcp = &cop->co_llc; + + /* + * See if there's room to add LLC header to front of packet. + */ + KB_HEADROOM(m, space); + if (space < llcp->v.llc_len) { + KBuffer *n; + + /* + * We have to allocate another buffer and tack it + * onto the front of the packet + */ + KB_ALLOCPKT(n, llcp->v.llc_len, KB_F_NOWAIT, + KB_T_HEADER); + if (n == 0) { + err = ENOMEM; + goto done; + } + KB_TAILALIGN(n, llcp->v.llc_len); + KB_LINKHEAD(n, m); + m = n; + } else { + /* + * Header fits, just adjust buffer controls + */ + KB_HEADADJ(m, llcp->v.llc_len); + } + + /* + * Add the LLC header + */ + KB_DATASTART(m, bp, void *); + KM_COPY(llcp->v.llc_info, bp, llcp->v.llc_len); + KB_PLENADJ(m, llcp->v.llc_len); + break; + + default: + panic("atm_cm_cpcs_data: mpx"); + } + + /* + * Finally, we can send the packet on its way + */ + STACK_CALL(CPCS_UNITDATA_INV, cvp->cvc_lower, cvp->cvc_tokl, + cvp, (int)m, 0, err); + +done: + return (err); +} + + +/* + * Process CPCS Stack Commands + * + * This is the top of the CPCS API data stack. All upward stack commands + * for the CPCS data API will be received and processed here. + * + * Arguments: + * cmd stack command code + * tok session token (pointer to connection VCC control block) + * arg1 argument 1 + * arg2 argument 2 + * + * Returns: + * none + * + */ +static void +atm_cm_cpcs_upper(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + Atm_connection *cop; + Atm_connvc *cvp = tok; + KBuffer *m; + void *bp; + int s; + + switch (cmd) { + + case CPCS_UNITDATA_SIG: + /* + * Input data packet + */ + m = (KBuffer *)arg1; + + if (cvp->cvc_state != CVCS_ACTIVE) { + if (cvp->cvc_state == CVCS_ACCEPT) { + KBuffer *n; + + /* + * Queue up any packets received before sigmgr + * notifies us of incoming call completion + */ + if (cvp->cvc_rcvqlen >= CVC_RCVQ_MAX) { + KB_FREEALL(m); + atm_cm_stat.cms_rcvconnvc++; + return; + } + KB_QNEXT(m) = NULL; + if (cvp->cvc_rcvq == NULL) { + cvp->cvc_rcvq = m; + } else { + for (n = cvp->cvc_rcvq; + KB_QNEXT(n) != NULL; + n = KB_QNEXT(n)) + ; + KB_QNEXT(n) = m; + } + cvp->cvc_rcvqlen++; + return; + } else { + KB_FREEALL(m); + atm_cm_stat.cms_rcvconnvc++; + return; + } + } + + /* + * Locate packet's connection + */ + cop = cvp->cvc_conn; + switch (cop->co_mpx) { + + case ATM_ENC_NULL: + /* + * We're already there... + */ + break; + + case ATM_ENC_LLC: + /* + * Find connection with matching LLC header + */ + if (KB_LEN(m) < T_ATM_LLC_MAX_LEN) { + KB_PULLUP(m, T_ATM_LLC_MAX_LEN, m); + if (m == 0) { + atm_cm_stat.cms_llcdrop++; + return; + } + } + KB_DATASTART(m, bp, void *); + + s = splnet(); + + while (cop) { + if (KM_CMP(bp, cop->co_llc.v.llc_info, + cop->co_llc.v.llc_len) == 0) + break; + cop = cop->co_next; + } + + (void) splx(s); + + if (cop == NULL) { + /* + * No connected user for this LLC + */ + KB_FREEALL(m); + atm_cm_stat.cms_llcid++; + return; + } + + /* + * Strip off the LLC header + */ + KB_HEADADJ(m, -cop->co_llc.v.llc_len); + KB_PLENADJ(m, -cop->co_llc.v.llc_len); + break; + + default: + panic("atm_cm_cpcs_upper: mpx"); + } + + /* + * We've found our connection, so hand the packet off + */ + if (cop->co_state != COS_ACTIVE) { + KB_FREEALL(m); + atm_cm_stat.cms_rcvconn++; + return; + } + (*cop->co_endpt->ep_cpcs_data)(cop->co_toku, m); + break; + + case CPCS_UABORT_SIG: + case CPCS_PABORT_SIG: + /* + * We don't support these (yet), so just fall thru... + */ + + default: + log(LOG_ERR, "atm_cm_cpcs_upper: unknown cmd 0x%x\n", cmd); + } +} + + +/* + * SAAL User Control Commands + * + * This function is called by an endpoint user to pass a control command + * across a SAAL data API. Mostly we just send these down the stack. + * + * Arguments: + * cmd stack command code + * cop pointer to connection block + * arg argument + * + * Returns: + * 0 command output successful + * errno output failed - reason indicated + * + */ +int +atm_cm_saal_ctl(cmd, cop, arg) + int cmd; + Atm_connection *cop; + void *arg; +{ + Atm_connvc *cvp; + int err = 0; + + /* + * Validate connection state + */ + if (cop->co_state != COS_ACTIVE) { + err = EFAULT; + goto done; + } + + cvp = cop->co_connvc; + if (cvp->cvc_state != CVCS_ACTIVE) { + err = EFAULT; + goto done; + } + + if (cvp->cvc_attr.api != CMAPI_SAAL) { + err = EFAULT; + goto done; + } + + switch (cmd) { + + case SSCF_UNI_ESTABLISH_REQ: + case SSCF_UNI_RELEASE_REQ: + /* + * Pass command down the stack + */ + STACK_CALL(cmd, cvp->cvc_lower, cvp->cvc_tokl, cvp, + (int)arg, 0, err); + break; + + default: + err = EINVAL; + } + +done: + return (err); +} + + +/* + * SAAL Data Output + * + * This function is called by an endpoint user to output a data packet + * across a SAAL data API. After we've validated the connection state, + * the packet will be sent down the data stack. + * + * Arguments: + * cop pointer to connection block + * m pointer to packet buffer chain to be output + * + * Returns: + * 0 packet output successful + * errno output failed - reason indicated + * + */ +int +atm_cm_saal_data(cop, m) + Atm_connection *cop; + KBuffer *m; +{ + Atm_connvc *cvp; + int err; + + + /* + * Validate connection state + */ + if (cop->co_state != COS_ACTIVE) { + err = EFAULT; + goto done; + } + + cvp = cop->co_connvc; + if (cvp->cvc_state != CVCS_ACTIVE) { + err = EFAULT; + goto done; + } + + if (cvp->cvc_attr.api != CMAPI_SAAL) { + err = EFAULT; + goto done; + } + + /* + * Finally, we can send the packet on its way + */ + STACK_CALL(SSCF_UNI_DATA_REQ, cvp->cvc_lower, cvp->cvc_tokl, + cvp, (int)m, 0, err); + +done: + return (err); +} + + +/* + * Process SAAL Stack Commands + * + * This is the top of the SAAL API data stack. All upward stack commands + * for the SAAL data API will be received and processed here. + * + * Arguments: + * cmd stack command code + * tok session token (pointer to connection VCC control block) + * arg1 argument 1 + * arg2 argument 2 + * + * Returns: + * none + * + */ +static void +atm_cm_saal_upper(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + Atm_connection *cop; + Atm_connvc *cvp = tok; + + + switch (cmd) { + + case SSCF_UNI_ESTABLISH_IND: + case SSCF_UNI_ESTABLISH_CNF: + case SSCF_UNI_RELEASE_IND: + case SSCF_UNI_RELEASE_CNF: + /* + * Control commands + */ + cop = cvp->cvc_conn; + if (cvp->cvc_state != CVCS_ACTIVE) + break; + if (cop->co_state != COS_ACTIVE) + break; + + (*cop->co_endpt->ep_saal_ctl)(cmd, cop->co_toku, (void *)arg1); + break; + + case SSCF_UNI_DATA_IND: + /* + * User data + */ + cop = cvp->cvc_conn; + if (cvp->cvc_state != CVCS_ACTIVE) { + atm_cm_stat.cms_rcvconnvc++; + KB_FREEALL((KBuffer *)arg1); + break; + } + if (cop->co_state != COS_ACTIVE) { + atm_cm_stat.cms_rcvconn++; + KB_FREEALL((KBuffer *)arg1); + break; + } + + (*cop->co_endpt->ep_saal_data)(cop->co_toku, (KBuffer *)arg1); + break; + + case SSCF_UNI_UNITDATA_IND: + /* + * Not supported + */ + KB_FREEALL((KBuffer *)arg1); + + /* FALLTHRU */ + + default: + log(LOG_ERR, "atm_cm_saal_upper: unknown cmd 0x%x\n", cmd); + } +} + + +/* + * SSCOP User Control Commands + * + * This function is called by an endpoint user to pass a control command + * across a SSCOP data API. Mostly we just send these down the stack. + * + * Arguments: + * cmd stack command code + * cop pointer to connection block + * arg1 argument + * arg2 argument + * + * Returns: + * 0 command output successful + * errno output failed - reason indicated + * + */ +int +atm_cm_sscop_ctl(cmd, cop, arg1, arg2) + int cmd; + Atm_connection *cop; + void *arg1; + void *arg2; +{ + Atm_connvc *cvp; + int err = 0; + + /* + * Validate connection state + */ + if (cop->co_state != COS_ACTIVE) { + err = EFAULT; + goto done; + } + + cvp = cop->co_connvc; + if (cvp->cvc_state != CVCS_ACTIVE) { + err = EFAULT; + goto done; + } + + if (cvp->cvc_attr.api != CMAPI_SSCOP) { + err = EFAULT; + goto done; + } + + switch (cmd) { + + case SSCOP_ESTABLISH_REQ: + case SSCOP_ESTABLISH_RSP: + case SSCOP_RELEASE_REQ: + case SSCOP_RESYNC_REQ: + case SSCOP_RESYNC_RSP: + case SSCOP_RECOVER_RSP: + case SSCOP_RETRIEVE_REQ: + /* + * Pass command down the stack + */ + STACK_CALL(cmd, cvp->cvc_lower, cvp->cvc_tokl, cvp, + (int)arg1, (int)arg2, err); + break; + + default: + err = EINVAL; + } + +done: + return (err); +} + + +/* + * SSCOP Data Output + * + * This function is called by an endpoint user to output a data packet + * across a SSCOP data API. After we've validated the connection state, + * the packet will be encapsulated and sent down the data stack. + * + * Arguments: + * cop pointer to connection block + * m pointer to packet buffer chain to be output + * + * Returns: + * 0 packet output successful + * errno output failed - reason indicated + * + */ +int +atm_cm_sscop_data(cop, m) + Atm_connection *cop; + KBuffer *m; +{ + Atm_connvc *cvp; + int err; + + + /* + * Validate connection state + */ + if (cop->co_state != COS_ACTIVE) { + err = EFAULT; + goto done; + } + + cvp = cop->co_connvc; + if (cvp->cvc_state != CVCS_ACTIVE) { + err = EFAULT; + goto done; + } + + if (cvp->cvc_attr.api != CMAPI_SSCOP) { + err = EFAULT; + goto done; + } + + /* + * Finally, we can send the packet on its way + */ + STACK_CALL(SSCOP_DATA_REQ, cvp->cvc_lower, cvp->cvc_tokl, + cvp, (int)m, 0, err); + +done: + return (err); +} + + +/* + * Process SSCOP Stack Commands + * + * This is the top of the SSCOP API data stack. All upward stack commands + * for the SSCOP data API will be received and processed here. + * + * Arguments: + * cmd stack command code + * tok session token (pointer to connection VCC control block) + * arg1 argument 1 + * arg2 argument 2 + * + * Returns: + * none + * + */ +static void +atm_cm_sscop_upper(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + Atm_connection *cop; + Atm_connvc *cvp = tok; + + switch (cmd) { + + case SSCOP_ESTABLISH_IND: + case SSCOP_ESTABLISH_CNF: + case SSCOP_RELEASE_IND: + case SSCOP_RESYNC_IND: + /* + * Control commands + */ + cop = cvp->cvc_conn; + if ((cvp->cvc_state != CVCS_ACTIVE) || + (cop->co_state != COS_ACTIVE)) { + KB_FREEALL((KBuffer *)arg1); + break; + } + + (*cop->co_endpt->ep_sscop_ctl) + (cmd, cop->co_toku, (void *)arg1, (void *)arg2); + break; + + case SSCOP_RELEASE_CNF: + case SSCOP_RESYNC_CNF: + case SSCOP_RECOVER_IND: + case SSCOP_RETRIEVE_IND: + case SSCOP_RETRIEVECMP_IND: + /* + * Control commands + */ + cop = cvp->cvc_conn; + if ((cvp->cvc_state != CVCS_ACTIVE) || + (cop->co_state != COS_ACTIVE)) + break; + + (*cop->co_endpt->ep_sscop_ctl) + (cmd, cop->co_toku, (void *)arg1, (void *)arg2); + break; + + case SSCOP_DATA_IND: + /* + * User data + */ + cop = cvp->cvc_conn; + if (cvp->cvc_state != CVCS_ACTIVE) { + atm_cm_stat.cms_rcvconnvc++; + KB_FREEALL((KBuffer *)arg1); + break; + } + if (cop->co_state != COS_ACTIVE) { + atm_cm_stat.cms_rcvconn++; + KB_FREEALL((KBuffer *)arg1); + break; + } + + (*cop->co_endpt->ep_sscop_data) + (cop->co_toku, (KBuffer *)arg1, arg2); + break; + + case SSCOP_UNITDATA_IND: + /* + * Not supported + */ + KB_FREEALL((KBuffer *)arg1); + + /* FALLTHRU */ + + default: + log(LOG_ERR, "atm_cm_sscop_upper: unknown cmd 0x%x\n", cmd); + } +} + + +/* + * Register an ATM Endpoint Service + * + * Every ATM endpoint service must register itself here before it can + * issue or receive any connection requests. + * + * Arguments: + * epp pointer to endpoint definition structure + * + * Returns: + * 0 registration successful + * errno registration failed - reason indicated + * + */ +int +atm_endpoint_register(epp) + Atm_endpoint *epp; +{ + int s = splnet(); + + /* + * See if we need to be initialized + */ + if (!atm_init) + atm_initialize(); + + /* + * Validate endpoint + */ + if (epp->ep_id > ENDPT_MAX) { + (void) splx(s); + return (EINVAL); + } + if (atm_endpoints[epp->ep_id] != NULL) { + (void) splx(s); + return (EEXIST); + } + + /* + * Add endpoint to list + */ + atm_endpoints[epp->ep_id] = epp; + + (void) splx(s); + return (0); +} + + +/* + * De-register an ATM Endpoint Service + * + * Each ATM endpoint service provider must de-register its registered + * endpoint(s) before terminating. Specifically, loaded kernel modules + * must de-register their services before unloading themselves. + * + * Arguments: + * epp pointer to endpoint definition structure + * + * Returns: + * 0 de-registration successful + * errno de-registration failed - reason indicated + * + */ +int +atm_endpoint_deregister(epp) + Atm_endpoint *epp; +{ + int s = splnet(); + + /* + * Validate endpoint + */ + if (epp->ep_id > ENDPT_MAX) { + (void) splx(s); + return (EINVAL); + } + if (atm_endpoints[epp->ep_id] != epp) { + (void) splx(s); + return (ENOENT); + } + + /* + * Remove endpoint from list + */ + atm_endpoints[epp->ep_id] = NULL; + + (void) splx(s); + return (0); +} + diff --git a/sys/netatm/atm_cm.h b/sys/netatm/atm_cm.h new file mode 100644 index 0000000..9e000ab --- /dev/null +++ b/sys/netatm/atm_cm.h @@ -0,0 +1,348 @@ +/* + * + * =================================== + * 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: atm_cm.h,v 1.3 1998/03/24 20:41:40 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Connection Management definitions + * + */ + +#ifndef _NETATM_ATM_CM_H +#define _NETATM_ATM_CM_H + +/* + * Forward declaration + */ +struct atm_connection; +struct atm_attributes; + + +#ifdef ATM_KERNEL +/* + * Structure used to define a kernel ATM endpoint service module and its + * associated entry points. An endpoint service is defined as a kernel + * entity which will serve as the endpoint of an ATM connection, i.e. it is + * responsible for issuing/receiving requests to/from the connection manager. + */ +struct atm_endpoint { + struct atm_endpoint *ep_next; /* Next in registry list */ + u_int ep_id; /* Endpoint ID (see below) */ + +/* Exported functions: Miscellaneous */ + int (*ep_ioctl) /* Ioctl */ + __P((int, caddr_t, caddr_t)); + caddr_t (*ep_getname) /* Get application/owner name */ + __P((void *)); + +/* Exported functions: Connection Manager Control API */ + void (*ep_connected) /* Call connected */ + __P((void *)); + void (*ep_cleared) /* Call cleared */ + __P((void *, struct t_atm_cause *)); + int (*ep_incoming) /* Incoming call */ + __P((void *, struct atm_connection *, + struct atm_attributes *, void **)); + int (*ep_addparty) /* Add Party notification */ + __P((void *, int, int)); + int (*ep_dropparty) /* Drop Party notification */ + __P((void *, int, int)); + +/* Exported functions: Connection Manager Data API: CPCS */ + void (*ep_cpcs_ctl) /* Control operation */ + __P((int, void *, void *)); + void (*ep_cpcs_data) /* Received data */ + __P((void *, KBuffer *)); + +/* Exported functions: Connection Manager Data API: SAAL */ + void (*ep_saal_ctl) /* Control operation */ + __P((int, void *, void *)); + void (*ep_saal_data) /* Received data */ + __P((void *, KBuffer *)); + +/* Exported functions: Connection Manager Data API: SSCOP */ + void (*ep_sscop_ctl) /* Control operation */ + __P((int, void *, void *, void *)); + void (*ep_sscop_data) /* Received data */ + __P((void *, KBuffer *, u_int)); +}; +typedef struct atm_endpoint Atm_endpoint; +#endif /* ATM_KERNEL */ + +/* + * Endpoint IDs + */ +#define ENDPT_UNKNOWN 0 /* Unknown */ +#define ENDPT_IP 1 /* IP over ATM */ +#define ENDPT_ATMARP 2 /* ATMARP */ +#define ENDPT_SPANS_SIG 3 /* SPANS Signalling */ +#define ENDPT_SPANS_CLS 4 /* SPANS CLS */ +#define ENDPT_UNI_SIG 5 /* UNI Signalling */ +#define ENDPT_SOCK_AAL5 6 /* Socket - AAL5 */ +#define ENDPT_SOCK_SSCOP 7 /* Socket - SSCOP */ +#define ENDPT_MAX 7 + + +/* + * ATM Connection Attributes + * + * Note: Attribute tag values are the same as the SVE_tag values. + * Unless otherwise specified, attribute field values are the same + * as the corresponding socket option values. + * The above values are all defined in netatm/atm.h. + */ + +/* AAL Attributes */ +struct t_atm_aal4 { + int32_t forward_max_SDU_size; + int32_t backward_max_SDU_size; + int32_t SSCS_type; + int32_t mid_low; + int32_t mid_high; +}; + +struct attr_aal { + int tag; /* Attribute tag */ + Aal_t type; /* AAL type (discriminator) */ + union { + struct t_atm_aal4 aal4; + struct t_atm_aal5 aal5; + } v; /* Attribute value */ +}; + +/* Traffic Descriptor Attributes */ +struct attr_traffic { + int tag; /* Attribute tag */ + struct t_atm_traffic v; /* Attribute value */ +}; + +/* Broadband Bearer Attributes */ +struct attr_bearer { + int tag; /* Attribute tag */ + struct t_atm_bearer v; /* Attribute value */ +}; + +/* Broadband High Layer Information Attributes */ +struct attr_bhli { + int tag; /* Attribute tag */ + struct t_atm_bhli v; /* Attribute value */ +}; + +/* Broadband Low Layer Information Attributes */ +struct attr_blli { + int tag_l2; /* Layer 2 attribute tag */ + int tag_l3; /* Layer 3 attribute tag */ + struct t_atm_blli v; /* Attribute value */ +}; + +/* Logical Link Control Attributes (multiplexing use only, not signalled) */ +struct attr_llc { + int tag; /* Attribute tag */ + struct t_atm_llc v; /* Attribute value */ +}; + +/* Called Party Attributes */ +struct attr_called { + int tag; /* Attribute tag */ + Atm_addr addr; /* Called party address */ + Atm_addr subaddr; /* Called party subaddress */ +}; + +/* Calling Party Attributes */ +struct attr_calling { + int tag; /* Attribute tag */ + Atm_addr addr; /* Calling party address */ + Atm_addr subaddr; /* Calling party subaddress */ + struct t_atm_caller_id cid; /* Caller ID */ +}; + +/* Quality of Service Attributes */ +struct attr_qos { + int tag; /* Attribute tag */ + struct t_atm_qos v; /* Attribute value */ +}; + +/* Transit Network Attributes */ +struct attr_transit { + int tag; /* Attribute tag */ + struct t_atm_transit v; /* Attribute value */ +}; + +/* Cause Attributes */ +struct attr_cause { + int tag; /* Attribute tag */ + struct t_atm_cause v; /* Attribute value */ +}; + + +struct atm_attributes { + struct atm_nif *nif; /* Network interface */ + u_int api; /* Connect Mgr Data API (see below) */ + int api_init;/* API initialization parameter */ + u_short headin; /* Input buffer headroom */ + u_short headout;/* Output buffer headroom */ + struct attr_aal aal; /* AAL attributes */ + struct attr_traffic traffic;/* Traffic descriptor attributes */ + struct attr_bearer bearer; /* Broadband bearer attributes */ + struct attr_bhli bhli; /* Broadband high layer attributes */ + struct attr_blli blli; /* Broadband low layer attributes */ + struct attr_llc llc; /* Logical link control attributes */ + struct attr_called called; /* Called party attributes */ + struct attr_calling calling;/* Calling party attributes */ + struct attr_qos qos; /* Quality of service attributes */ + struct attr_transit transit;/* Transit network attributes */ + struct attr_cause cause; /* Cause attributes */ +}; +typedef struct atm_attributes Atm_attributes; + +/* + * Connection Manager Data APIs + */ +#define CMAPI_CPCS 0 /* AAL CPCS */ +#define CMAPI_SAAL 1 /* Signalling AAL */ +#define CMAPI_SSCOP 2 /* Reliable data (SSCOP) */ + + +#ifdef ATM_KERNEL +/* + * ATM Connection Instance + * + * There will be one connection block for each endpoint <-> Connection Manager + * API instance. Note that with connection multiplexors (e.g. LLC), there + * may be multiple connections per VCC. + */ +struct atm_connection { + struct atm_connection *co_next; /* Multiplexor/listen queue link */ + struct atm_connection *co_mxh; /* Connection multiplexor head */ + u_char co_flags; /* Connection flags (see below) */ + u_char co_state; /* User <-> CM state (see below) */ + Encaps_t co_mpx; /* Multiplexor type */ + void *co_toku; /* Endpoint's session token */ + Atm_endpoint *co_endpt; /* Endpoint service */ + struct atm_connvc *co_connvc; /* Connection VCC */ + struct attr_llc co_llc; /* Connection LLC header */ + Atm_attributes *co_lattr; /* Listening attributes */ +}; +typedef struct atm_connection Atm_connection; + +/* + * Connection Flags + */ +#define COF_P2P 0x01 /* Point-to-point */ +#define COF_P2MP 0x02 /* Point-to-multipoint */ + +/* + * Endpoint <-> Connection Manager States + */ +#define COS_FREE 0 /* Not allocated */ +#define COS_OUTCONN 1 /* Outgoing connection pending */ +#define COS_LISTEN 2 /* Listening for connection */ +#define COS_INCONN 3 /* Incoming connection pending */ +#define COS_INACCEPT 4 /* Incoming connection accepted */ +#define COS_ACTIVE 5 /* Connection active */ +#define COS_CLEAR 6 /* Connection is clearing */ + + +/* + * ATM Connection VCC Instance + * + * There will be one connection-vcc block for each VCC created by the + * Connection Manager. For multiplexed connections, there may be multiple + * connection blocks associated with each connection-vcc. This block is + * used to control the Connection Manager <-> VCC interface, including the + * interfaces to stack management and the signalling manager. + */ +struct atm_connvc { + Qelem_t cvc_q; /* Queueing links */ + Atm_connection *cvc_conn; /* Connection head */ + struct vccb *cvc_vcc; /* VCC for connection */ + struct sigmgr *cvc_sigmgr; /* VCC signalling manager */ + u_char cvc_flags; /* Connection flags (see below) */ + u_char cvc_state; /* CM - VCC state (see below) */ + void *cvc_tokl; /* Stack lower layer token */ + void (*cvc_lower) /* Stack lower layer handler */ + __P((int, void *, int, int)); + u_short cvc_upcnt; /* Up stack calls in progress */ + u_short cvc_downcnt; /* Down stack calls in progress */ + KBuffer *cvc_rcvq; /* Packet receive queue */ + int cvc_rcvqlen; /* Receive queue length */ + Atm_attributes cvc_attr; /* VCC attributes */ + struct atm_time cvc_time; /* Timer controls */ +}; +typedef struct atm_connvc Atm_connvc; + +/* + * Connection Flags + */ +#define CVCF_ABORTING 0x01 /* VCC abort is pending */ +#define CVCF_INCOMQ 0x02 /* VCC is on incoming queue */ +#define CVCF_CONNQ 0x04 /* VCC is on connection queue */ +#define CVCF_CALLER 0x08 /* We are the call originator */ + +/* + * Connection Manager <-> VCC States + */ +#define CVCS_FREE 0 /* Not allocated */ +#define CVCS_SETUP 1 /* Call setup pending */ +#define CVCS_INIT 2 /* Stack INIT pending */ +#define CVCS_INCOMING 3 /* Incoming call present */ +#define CVCS_ACCEPT 4 /* Incoming call accepted */ +#define CVCS_REJECT 5 /* Incoming call rejected */ +#define CVCS_ACTIVE 6 /* Stack active */ +#define CVCS_RELEASE 7 /* Connection release pending */ +#define CVCS_CLEAR 8 /* Call has been cleared */ +#define CVCS_TERM 9 /* Stack TERM pending */ + + +/* + * Connection VCC variables + */ +#define CVC_RCVQ_MAX 3 /* Max length of receive queue */ + + +/* + * Timer macros + */ +#define CVC_TIMER(s, t) atm_timeout(&(s)->cvc_time, (t), atm_cm_timeout) +#define CVC_CANCEL(s) atm_untimeout(&(s)->cvc_time) + + +/* + * Connection Manager Statistics + */ +struct atm_cm_stat { + u_long cms_llcdrop; /* Packets dropped by llc demux'ing */ + u_long cms_llcid; /* Packets with unknown llc id */ + u_long cms_rcvconn; /* Packets dropped, bad conn state */ + u_long cms_rcvconnvc; /* Packets dropped, bad connvc state */ +}; +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_CM_H */ diff --git a/sys/netatm/atm_device.c b/sys/netatm/atm_device.c new file mode 100644 index 0000000..7f26695 --- /dev/null +++ b/sys/netatm/atm_device.c @@ -0,0 +1,883 @@ +/* + * + * =================================== + * 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: atm_device.c,v 1.7 1998/03/24 20:42:39 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM device support functions + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_device.c,v 1.7 1998/03/24 20:42:39 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Private structures for managing allocated kernel memory resources + * + * For each allocation of kernel memory, one Mem_ent will be used. + * The Mem_ent structures will be allocated in blocks inside of a + * Mem_blk structure. + */ +#define MEM_NMEMENT 10 /* How many Mem_ent's in a Mem_blk */ + +struct mem_ent { + void *me_kaddr; /* Allocated memory address */ + u_int me_ksize; /* Allocated memory length */ + void *me_uaddr; /* Memory address returned to caller */ + u_int me_flags; /* Flags (see below) */ +}; +typedef struct mem_ent Mem_ent; + +/* + * Memory entry flags + */ +#define MEF_NONCACHE 1 /* Memory is noncacheable */ + + +struct mem_blk { + struct mem_blk *mb_next; /* Next block in chain */ + Mem_ent mb_mement[MEM_NMEMENT]; /* Allocated memory entries */ +}; +typedef struct mem_blk Mem_blk; + +static Mem_blk *atm_mem_head = NULL; + +static struct t_atm_cause atm_dev_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_VPCI_VCI_ASSIGNMENT_FAILURE, + {0, 0, 0, 0} +}; + + +/* + * ATM Device Stack Instantiation + * + * Called at splnet. + * + * Arguments + * ssp pointer to array of stack definition pointers + * for connection + * ssp[0] points to upper layer's stack definition + * ssp[1] points to this layer's stack definition + * ssp[2] points to lower layer's stack definition + * cvcp pointer to connection vcc for this stack + * + * Returns + * 0 instantiation successful + * err instantiation failed - reason indicated + * + */ +int +atm_dev_inst(ssp, cvcp) + struct stack_defn **ssp; + Atm_connvc *cvcp; +{ + Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif; + Cmn_vcc *cvp; + int err; + + /* + * Check to see if device has been initialized + */ + if ((cup->cu_flags & CUF_INITED) == 0) + return ( EIO ); + + /* + * Validate lower SAP + */ + /* + * Device driver is the lowest layer - no need to validate + */ + + /* + * Validate PVC vpi.vci + */ + if (cvcp->cvc_attr.called.addr.address_format == T_ATM_PVC_ADDR) { + /* + * Look through existing circuits - return error if found + */ + Atm_addr_pvc *pp; + + pp = (Atm_addr_pvc *)cvcp->cvc_attr.called.addr.address; + if (atm_dev_vcc_find(cup, ATM_PVC_GET_VPI(pp), + ATM_PVC_GET_VCI(pp), 0)) + return ( EADDRINUSE ); + } + + /* + * Validate our SAP type + */ + switch ((*(ssp+1))->sd_sap) { + case SAP_CPCS_AAL3_4: + case SAP_CPCS_AAL5: + case SAP_ATM: + break; + default: + return (EINVAL); + } + + /* + * Allocate a VCC control block + */ + if ( ( cvp = (Cmn_vcc *)atm_allocate(cup->cu_vcc_pool) ) == NULL ) + return ( ENOMEM ); + + cvp->cv_state = CVS_INST; + cvp->cv_toku = (*ssp)->sd_toku; + cvp->cv_upper = (*ssp)->sd_upper; + cvp->cv_connvc = cvcp; + + /* + * Let device have a look at the connection request + */ + err = (*cup->cu_instvcc)(cup, cvp); + if (err) { + atm_free((caddr_t)cvp); + return (err); + } + + /* + * Looks good so far, so link in device VCC + */ + LINK2TAIL ( cvp, Cmn_vcc, cup->cu_vcc, cv_next ); + + /* + * Save my token + */ + (*++ssp)->sd_toku = cvp; + + /* + * Pass instantiation down the stack + */ + /* + * No need - we're the lowest point. + */ + /* err = (*(ssp + 1))->sd_inst(ssp, cvcp); */ + + /* + * Save the lower layer's interface info + */ + /* + * No need - we're the lowest point + */ + /* cvp->cv_lower = (*++ssp)->sd_lower; */ + /* cvp->cv_tok1 = (*ssp)->sd_toku; */ + + return (0); +} + + +/* + * ATM Device Stack Command Handler + * + * Arguments + * cmd stack command code + * tok session token (Cmn_vcc) + * arg1 command specific argument + * arg2 command specific argument + * + * Returns + * none + * + */ +/*ARGSUSED*/ +void +atm_dev_lower(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + Cmn_vcc *cvp = (Cmn_vcc *)tok; + Atm_connvc *cvcp = cvp->cv_connvc; + Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif; + struct vccb *vcp; + u_int state; + int s; + + switch ( cmd ) { + + case CPCS_INIT: + /* + * Sanity check + */ + if ( cvp->cv_state != CVS_INST ) { + log ( LOG_ERR, + "atm_dev_lower: INIT: tok=0x%x, state=%d\n", + (int)tok, cvp->cv_state ); + break; + } + + vcp = cvp->cv_connvc->cvc_vcc; + + /* + * Validate SVC vpi.vci + */ + if ( vcp->vc_type & VCC_SVC ) { + + if (atm_dev_vcc_find(cup, vcp->vc_vpi, vcp->vc_vci, + vcp->vc_type & (VCC_IN | VCC_OUT)) + != cvp){ + log ( LOG_ERR, + "atm_dev_lower: dup SVC (%d,%d) tok=0x%x\n", + vcp->vc_vpi, vcp->vc_vci, (int)tok ); + atm_cm_abort(cvp->cv_connvc, &atm_dev_cause); + break; + } + } + + /* + * Tell the device to open the VCC + */ + cvp->cv_state = CVS_INITED; + s = splimp(); + if ((*cup->cu_openvcc)(cup, cvp)) { + atm_cm_abort(cvp->cv_connvc, &atm_dev_cause); + (void) splx(s); + break; + } + (void) splx(s); + break; + + case CPCS_TERM: { + KBuffer *m, *prev, *next; + int *ip; + + s = splimp(); + + /* + * Disconnect the VCC - ignore return code + */ + if ((cvp->cv_state == CVS_INITED) || + (cvp->cv_state == CVS_ACTIVE)) { + (void) (*cup->cu_closevcc)(cup, cvp); + } + cvp->cv_state = CVS_TERM; + + /* + * Remove from interface list + */ + UNLINK ( cvp, Cmn_vcc, cup->cu_vcc, cv_next ); + + /* + * Free any buffers from this VCC on the ATM interrupt queue + */ + prev = NULL; + for (m = atm_intrq.ifq_head; m; m = next) { + next = KB_QNEXT(m); + + /* + * See if this entry is for the terminating VCC + */ + KB_DATASTART(m, ip, int *); + ip++; + if (*ip == (int)cvp) { + /* + * Yep, so dequeue the entry + */ + if (prev == NULL) + atm_intrq.ifq_head = next; + else + KB_QNEXT(prev) = next; + + if (next == NULL) + atm_intrq.ifq_tail = prev; + + atm_intrq.ifq_len--; + + /* + * Free the unwanted buffers + */ + KB_FREEALL(m); + } else { + prev = m; + } + } + (void) splx(s); + + /* + * Free VCC resources + */ + (void) atm_free((caddr_t)cvp); + break; + } + + case CPCS_UNITDATA_INV: + + /* + * Sanity check + * + * Use temp state variable since we dont want to lock out + * interrupts, but initial VC activation interrupt may + * happen here, changing state somewhere in the middle. + */ + state = cvp->cv_state; + if ((state != CVS_ACTIVE) && + (state != CVS_INITED)) { + log ( LOG_ERR, + "atm_dev_lower: UNITDATA: tok=0x%x, state=%d\n", + (int)tok, state ); + KB_FREEALL((KBuffer *)arg1); + break; + } + + /* + * Hand the data off to the device + */ + (*cup->cu_output)(cup, cvp, (KBuffer *)arg1); + + break; + + case CPCS_UABORT_INV: + log ( LOG_ERR, + "atm_dev_lower: unimplemented stack cmd 0x%x, tok=0x%x\n", + cmd, (int)tok ); + break; + + default: + log ( LOG_ERR, + "atm_dev_lower: unknown stack cmd 0x%x, tok=0x%x\n", + cmd, (int)tok ); + + } + + return; +} + + + +/* + * Allocate kernel memory block + * + * This function will allocate a kernel memory block of the type specified + * in the flags parameter. The returned address will point to a memory + * block of the requested size and alignment. The memory block will also + * be zeroed. The alloc/free functions will manage/mask both the OS-specific + * kernel memory management requirements and the bookkeeping required to + * deal with data alignment issues. + * + * This function should not be called from interrupt level. + * + * Arguments: + * size size of memory block to allocate + * align data alignment requirement + * flags allocation flags (ATM_DEV_*) + * + * Returns: + * uaddr pointer to aligned memory block + * NULL unable to allocate memory + * + */ +void * +atm_dev_alloc(size, align, flags) + u_int size; + u_int align; + u_int flags; +{ + Mem_blk *mbp; + Mem_ent *mep; + u_int kalign, ksize; + int s, i; + + s = splimp(); + + /* + * Find a free Mem_ent + */ + mep = NULL; + for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) { + for (i = 0; i < MEM_NMEMENT; i++) { + if (mbp->mb_mement[i].me_uaddr == NULL) { + mep = &mbp->mb_mement[i]; + break; + } + } + } + + /* + * If there are no free Mem_ent's, then allocate a new Mem_blk + * and link it into the chain + */ + if (mep == NULL) { + mbp = (Mem_blk *) KM_ALLOC(sizeof(Mem_blk), M_DEVBUF, M_NOWAIT); + if (mbp == NULL) { + log(LOG_ERR, "atm_dev_alloc: Mem_blk failure\n"); + (void) splx(s); + return (NULL); + } + KM_ZERO(mbp, sizeof(Mem_blk)); + + mbp->mb_next = atm_mem_head; + atm_mem_head = mbp; + mep = mbp->mb_mement; + } + + /* + * Now we need to get the kernel's allocation alignment minimum + * + * This is obviously very OS-specific stuff + */ +#ifdef sun + if (flags & ATM_DEV_NONCACHE) { + /* Byte-aligned */ + kalign = sizeof(long); + } else { + /* Doubleword-aligned */ + kalign = sizeof(double); + } +#elif (defined(BSD) && (BSD >= 199103)) + kalign = MINALLOCSIZE; +#else + #error Unsupported/unconfigured OS +#endif + + /* + * Figure out how much memory we must allocate to satify the + * user's size and alignment needs + */ + if (align <= kalign) + ksize = size; + else + ksize = size + align - kalign; + + /* + * Finally, go get the memory + */ + if (flags & ATM_DEV_NONCACHE) { +#ifdef sun + mep->me_kaddr = IOPBALLOC(ksize); +#elif defined(__i386__) + mep->me_kaddr = KM_ALLOC(ksize, M_DEVBUF, M_NOWAIT); +#else + #error Unsupported/unconfigured OS +#endif + } else { + mep->me_kaddr = KM_ALLOC(ksize, M_DEVBUF, M_NOWAIT); + } + + if (mep->me_kaddr == NULL) { + log(LOG_ERR, "atm_dev_alloc: %skernel memory unavailable\n", + (flags & ATM_DEV_NONCACHE) ? "non-cacheable " : ""); + (void) splx(s); + return (NULL); + } + + /* + * Calculate correct alignment address to pass back to user + */ + mep->me_uaddr = (void *) roundup((u_int)mep->me_kaddr, align); + mep->me_ksize = ksize; + mep->me_flags = flags; + + /* + * Clear memory for user + */ + KM_ZERO(mep->me_uaddr, size); + + ATM_DEBUG4("atm_dev_alloc: size=%d, align=%d, flags=%d, uaddr=0x%x\n", + size, align, flags, (int)mep->me_uaddr); + + (void) splx(s); + + return (mep->me_uaddr); +} + + +/* + * Free kernel memory block + * + * This function will free a kernel memory block previously allocated by + * the atm_dev_alloc function. + * + * This function should not be called from interrupt level. + * + * Arguments: + * uaddr pointer to allocated aligned memory block + * + * Returns: + * none + * + */ +void +atm_dev_free(uaddr) + void *uaddr; +{ + Mem_blk *mbp; + Mem_ent *mep; + int s, i; + + ATM_DEBUG1("atm_dev_free: uaddr=0x%x\n", (int)uaddr); + + s = splimp(); + + /* + * Protect ourselves... + */ + if (uaddr == NULL) + panic("atm_dev_free: trying to free null address"); + + /* + * Find our associated entry + */ + mep = NULL; + for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) { + for (i = 0; i < MEM_NMEMENT; i++) { + if (mbp->mb_mement[i].me_uaddr == uaddr) { + mep = &mbp->mb_mement[i]; + break; + } + } + } + + /* + * If we didn't find our entry, then unceremoniously let the caller + * know they screwed up (it certainly couldn't be a bug here...) + */ + if (mep == NULL) + panic("atm_dev_free: trying to free unknown address"); + + /* + * Give the memory space back to the kernel + */ + if (mep->me_flags & ATM_DEV_NONCACHE) { +#ifdef sun + IOPBFREE(mep->me_kaddr, mep->me_ksize); +#elif defined(__i386__) + KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF); +#else + #error Unsupported/unconfigured OS +#endif + } else { + KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF); + } + + /* + * Free our entry + */ + mep->me_uaddr = NULL; + + (void) splx(s); + + return; +} + + +#ifdef sun4m + +typedef int (*func_t)(); + +/* + * Map an address into DVMA space + * + * This function will take a kernel virtual address and map it to + * a DMA virtual address which can be used during SBus DMA cycles. + * + * Arguments: + * addr kernel virtual address + * len length of DVMA space requested + * flags allocation flags (ATM_DEV_*) + * + * Returns: + * a DVMA address + * NULL unable to map into DMA space + * + */ +void * +atm_dma_map(addr, len, flags) + caddr_t addr; + int len; + int flags; +{ + if (flags & ATM_DEV_NONCACHE) + /* + * Non-cacheable memory is already DMA'able + */ + return ((void *)addr); + else + return ((void *)mb_nbmapalloc(bigsbusmap, addr, len, + MDR_BIGSBUS|MB_CANTWAIT, (func_t)NULL, (caddr_t)NULL)); +} + + +/* + * Free a DVMA map address + * + * This function will free DVMA map resources (addresses) previously + * allocated with atm_dma_map(). + * + * Arguments: + * addr DMA virtual address + * flags allocation flags (ATM_DEV_*) + * + * Returns: + * none + * + */ +void +atm_dma_free(addr, flags) + caddr_t addr; + int flags; +{ + if ((flags & ATM_DEV_NONCACHE) == 0) + mb_mapfree(bigsbusmap, (int)&addr); + + return; +} +#endif /* sun4m */ + + +/* + * Compress buffer chain + * + * This function will compress a supplied buffer chain into a minimum number + * of kernel buffers. Typically, this function will be used because the + * number of buffers in an output buffer chain is too large for a device's + * DMA capabilities. This should only be called as a last resort, since + * all the data copying will surely kill any hopes of decent performance. + * + * Arguments: + * m pointer to source buffer chain + * + * Returns: + * n pointer to compressed buffer chain + * + */ +KBuffer * +atm_dev_compress(m) + KBuffer *m; +{ + KBuffer *n, *n0, **np; + int len, space; + caddr_t src, dst; + + n = n0 = NULL; + np = &n0; + dst = NULL; + space = 0; + + /* + * Copy each source buffer into compressed chain + */ + while (m) { + + if (space == 0) { + + /* + * Allocate another buffer for compressed chain + */ + KB_ALLOCEXT(n, ATM_DEV_CMPR_LG, KB_F_NOWAIT, KB_T_DATA); + if (n) { + space = ATM_DEV_CMPR_LG; + } else { + KB_ALLOC(n, ATM_DEV_CMPR_SM, KB_F_NOWAIT, + KB_T_DATA); + if (n) { + space = ATM_DEV_CMPR_SM; + } else { + /* + * Unable to get any new buffers, so + * just return the partially compressed + * chain and hope... + */ + *np = m; + break; + } + } + + KB_HEADSET(n, 0); + KB_LEN(n) = 0; + KB_BFRSTART(n, dst, caddr_t); + + *np = n; + np = &KB_NEXT(n); + } + + /* + * Copy what we can from source buffer + */ + len = MIN(space, KB_LEN(m)); + KB_DATASTART(m, src, caddr_t); + KM_COPY(src, dst, len); + + /* + * Adjust for copied data + */ + dst += len; + space -= len; + + KB_HEADADJ(m, -len); + KB_TAILADJ(n, len); + + /* + * If we've exhausted our current source buffer, free it + * and move to the next one + */ + if (KB_LEN(m) == 0) { + KB_FREEONE(m, m); + } + } + + return (n0); +} + + +/* + * Locate VCC entry + * + * This function will return the VCC entry for a specified interface and + * VPI/VCI value. + * + * Arguments: + * cup pointer to interface unit structure + * vpi VPI value + * vci VCI value + * type VCC type + * + * Returns: + * vcp pointer to located VCC entry matching + * NULL no VCC found + * + */ +Cmn_vcc * +atm_dev_vcc_find(cup, vpi, vci, type) + Cmn_unit *cup; + u_int vpi; + u_int vci; + u_int type; +{ + Cmn_vcc *cvp; + int s = splnet(); + + /* + * Go find VCC + * + * (Probably should stick in a hash table some time) + */ + for (cvp = cup->cu_vcc; cvp; cvp = cvp->cv_next) { + struct vccb *vcp; + + vcp = cvp->cv_connvc->cvc_vcc; + if ((vcp->vc_vci == vci) && (vcp->vc_vpi == vpi) && + ((vcp->vc_type & type) == type)) + break; + } + + (void) splx(s); + return (cvp); +} + + +#ifdef notdef +/* + * Module unloading notification + * + * This function must be called just prior to unloading the module from + * memory. All allocated memory will be freed here and anything else that + * needs cleaning up. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +atm_unload() +{ + Mem_blk *mbp; + Mem_ent *mep; + int s, i; + + s = splimp(); + + /* + * Free up all of our memory management storage + */ + while (mbp = atm_mem_head) { + + /* + * Make sure users have freed up all of their memory + */ + for (i = 0; i < MEM_NMEMENT; i++) { + if (mbp->mb_mement[i].me_uaddr != NULL) { + panic("atm_unload: unfreed memory"); + } + } + + atm_mem_head = mbp->mb_next; + + /* + * Hand this block back to the kernel + */ + KM_FREE((caddr_t) mbp, sizeof(Mem_blk), M_DEVBUF); + } + + (void) splx(s); + + return; +} +#endif /* notdef */ + + +/* + * Print a PDU + * + * Arguments: + * cup pointer to device unit + * cvp pointer to VCC control block + * m pointer to pdu buffer chain + * msg pointer to message string + * + * Returns: + * none + * + */ +void +atm_dev_pdu_print(cup, cvp, m, msg) + Cmn_unit *cup; + Cmn_vcc *cvp; + KBuffer *m; + char *msg; +{ + char buf[128]; + + sprintf(buf, "%s vcc=(%d,%d)", msg, + cvp->cv_connvc->cvc_vcc->vc_vpi, + cvp->cv_connvc->cvc_vcc->vc_vci); + + atm_pdu_print(m, buf); +} + diff --git a/sys/netatm/atm_if.c b/sys/netatm/atm_if.c new file mode 100644 index 0000000..a07ccc2 --- /dev/null +++ b/sys/netatm/atm_if.c @@ -0,0 +1,1202 @@ +/* + * + * =================================== + * 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: atm_if.c,v 1.13 1998/07/23 21:43:55 root Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM interface management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_if.c,v 1.13 1998/07/23 21:43:55 root Exp $"; +#endif + +#include <netatm/kern_include.h> + + +#if (defined(BSD) && (BSD < 199506)) +extern int ifqmaxlen; +#endif + +/* + * Local functions + */ +static int atm_physif_ioctl __P((int, caddr_t, caddr_t)); +#if (defined(BSD) && (BSD >= 199306)) +static int atm_netif_rtdel __P((struct radix_node *, void *)); +#endif +static int atm_if_ioctl __P((struct ifnet *, u_long, caddr_t)); +static int atm_ifparse __P((char *, char *, int, int *)); + +/* + * Local variables + */ +static int (*atm_ifouttbl[AF_MAX+1]) + __P((struct ifnet *, KBuffer *, struct sockaddr *)) + = {NULL}; + + +/* + * Register an ATM physical interface + * + * Each ATM device interface must register itself here upon completing + * its internal initialization. This applies to both linked and loaded + * device drivers. The interface must be registered before a signalling + * manager can be attached. + * + * Arguments: + * cup pointer to interface's common unit structure + * name pointer to device name string + * sdp pointer to interface's stack services + * + * Returns: + * 0 registration successful + * errno registration failed - reason indicated + * + */ +int +atm_physif_register(cup, name, sdp) + Cmn_unit *cup; + char *name; + struct stack_defn *sdp; +{ + struct atm_pif *pip; + int s; + + /* + * See if we need to be initialized + */ + if (!atm_init) + atm_initialize(); + + /* + * Make sure we're not already registered + */ + if (cup->cu_flags & CUF_REGISTER) { + return (EALREADY); + } + + s = splnet(); + + /* + * Make sure an interface is only registered once + */ + for (pip = atm_interface_head; pip != NULL; pip = pip->pif_next) { + if ((cup->cu_unit == pip->pif_unit) && + (strcmp(name, pip->pif_name) == 0)) { + (void) splx(s); + return (EEXIST); + } + } + + /* + * Fill in physical interface parameters + */ + pip = &cup->cu_pif; + pip->pif_name = name; + pip->pif_unit = cup->cu_unit; + pip->pif_flags = PIF_UP; + pip->pif_services = sdp; + pip->pif_ioctl = atm_physif_ioctl; + + /* + * Link in the interface and mark us registered + */ + LINK2TAIL(pip, struct atm_pif, atm_interface_head, pif_next); + cup->cu_flags |= CUF_REGISTER; + + (void) splx(s); + return (0); +} + + +/* + * De-register an ATM physical interface + * + * Each ATM interface must de-register itself before downing the interface. + * The interface's signalling manager will be detached and any network + * interface and VCC control blocks will be freed. + * + * Arguments: + * cup pointer to interface's common unit structure + * + * Returns: + * 0 de-registration successful + * errno de-registration failed - reason indicated + * + */ +int +atm_physif_deregister(cup) + Cmn_unit *cup; +{ + struct atm_pif *pip = (struct atm_pif *)&cup->cu_pif; + Cmn_vcc *cvp; + int err; + int s = splnet(); + + /* + * Detach and deregister, if needed + */ + if ((cup->cu_flags & CUF_REGISTER)) { + + /* + * Detach from signalling manager + */ + if (pip->pif_sigmgr != NULL) { + err = atm_sigmgr_detach(pip); + if (err && (err != ENOENT)) { + (void) splx(s); + return (err); + } + } + + /* + * Make sure signalling manager is detached + */ + if (pip->pif_sigmgr != NULL) { + (void) splx(s); + return (EBUSY); + } + + /* + * Unlink interface + */ + UNLINK(pip, struct atm_pif, atm_interface_head, pif_next); + + cup->cu_flags &= ~CUF_REGISTER; + } + + /* + * Free all of our network interfaces + */ + atm_physif_freenifs(pip); + + /* + * Free unit's vcc information + */ + cvp = cup->cu_vcc; + while (cvp) { + atm_free(cvp); + cvp = cvp->cv_next; + } + cup->cu_vcc = (Cmn_vcc *)NULL; + + (void) splx(s); + + return (0); +} + + +/* + * Free all network interfaces on a physical interface + * + * Arguments + * pip pointer to physical interface structure + * + * Returns + * none + * + */ +void +atm_physif_freenifs(pip) + struct atm_pif *pip; +{ + struct atm_nif *nip = pip->pif_nif; + int s = splnet(); + + while ( nip ) + { + /* + * atm_nif_detach zeros pointers - save so we can + * walk the chain. + */ + struct atm_nif *nipp = nip->nif_pnext; + + /* + * Clean up network i/f trails + */ + atm_nif_detach ( nip ); + atm_free ((caddr_t)nip); + nip = nipp; + } + pip->pif_nif = (struct atm_nif *)NULL; + + (void) splx(s); + + return; +} + + +/* + * Handle physical interface ioctl's + * + * See <netatm/atm_ioctl.h> for definitions. + * + * Called at splnet. + * + * Arguments: + * code Ioctl function (sub)code + * data Data block. On input contains command, + * on output, contains results + * arg Optional code specific arguments + * + * Returns: + * 0 Request processed successfully + * errno Request failed - reason code + * + */ +static int +atm_physif_ioctl(code, data, arg) + int code; + caddr_t data; + caddr_t arg; +{ + struct atminfreq *aip = (struct atminfreq *)data; + struct atmsetreq *asr = (struct atmsetreq *)data; + struct atm_pif *pip; + struct atm_nif *nip; + struct sigmgr *smp; + struct siginst *sip; + struct ifnet *ifp; + Cmn_unit *cup; + Atm_config *acp; + caddr_t buf = aip->air_buf_addr; + struct air_phy_stat_rsp *apsp; + struct air_int_rsp apr; + struct air_netif_rsp anr; + struct air_cfg_rsp acr; + int count, len, buf_len = aip->air_buf_len; + int err = 0; + char ifname[2*IFNAMSIZ]; +#if (defined(BSD) && (BSD >= 199103)) + struct ifaddr *ifa; + struct in_ifaddr *ia; + struct sockaddr_dl *sdl; +#endif + + + switch ( aip->air_opcode ) { + + case AIOCS_INF_INT: + /* + * Get physical interface information + */ + aip = (struct atminfreq *)data; + pip = (struct atm_pif *)arg; + + /* + * Make sure there's room in user buffer + */ + if (aip->air_buf_len < sizeof(apr)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + KM_ZERO((caddr_t)&apr, sizeof(apr)); + smp = pip->pif_sigmgr; + sip = pip->pif_siginst; + (void) sprintf(apr.anp_intf, "%s%d", pip->pif_name, + pip->pif_unit ); + if ( pip->pif_nif ) + { + strcpy(apr.anp_nif_pref, pip->pif_nif->nif_if.if_name); + + nip = pip->pif_nif; + while ( nip ) { + apr.anp_nif_cnt++; + nip = nip->nif_pnext; + } + } + if (sip) { + ATM_ADDR_COPY(&sip->si_addr, &apr.anp_addr); + ATM_ADDR_COPY(&sip->si_subaddr, &apr.anp_subaddr); + apr.anp_sig_proto = smp->sm_proto; + apr.anp_sig_state = sip->si_state; + } + + /* + * Copy data to user buffer + */ + err = copyout((caddr_t)&apr, aip->air_buf_addr, sizeof(apr)); + if (err) + break; + + /* + * Update buffer pointer/count + */ + aip->air_buf_addr += sizeof(apr); + aip->air_buf_len -= sizeof(apr); + break; + + case AIOCS_INF_NIF: + /* + * Get network interface information + */ + aip = (struct atminfreq *)data; + nip = (struct atm_nif *)arg; + ifp = &nip->nif_if; + pip = nip->nif_pif; + + /* + * Make sure there's room in user buffer + */ + if (aip->air_buf_len < sizeof(anr)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + KM_ZERO((caddr_t)&anr, sizeof(anr)); + (void) sprintf(anr.anp_intf, "%s%d", ifp->if_name, + ifp->if_unit); + IFP_TO_IA(ifp, ia); + if (ia) { + anr.anp_proto_addr = *ia->ia_ifa.ifa_addr; + } + (void) sprintf(anr.anp_phy_intf, "%s%d", pip->pif_name, + pip->pif_unit); + + /* + * Copy data to user buffer + */ + err = copyout((caddr_t)&anr, aip->air_buf_addr, sizeof(anr)); + if (err) + break; + + /* + * Update buffer pointer/count + */ + aip->air_buf_addr += sizeof(anr); + aip->air_buf_len -= sizeof(anr); + break; + + case AIOCS_INF_PIS: + /* + * Get per interface statistics + */ + pip = (struct atm_pif *)arg; + if ( pip == NULL ) + return ( ENXIO ); + sprintf ( ifname, "%s%d", pip->pif_name, pip->pif_unit ); + + /* + * Cast response into users buffer + */ + apsp = (struct air_phy_stat_rsp *)buf; + + /* + * Sanity check + */ + len = sizeof ( struct air_phy_stat_rsp ); + if ( buf_len < len ) + return ( ENOSPC ); + + /* + * Copy interface name into response + */ + if ( err = copyout ( ifname, apsp->app_intf, IFNAMSIZ ) ) + break; + + /* + * Copy counters + */ + if ( err = copyout ( &pip->pif_ipdus, &apsp->app_ipdus, + len - sizeof ( apsp->app_intf ) ) ) + break; + + /* + * Adjust buffer elements + */ + buf += len; + buf_len -= len; + + aip->air_buf_addr = buf; + aip->air_buf_len = buf_len; + break; + + case AIOCS_SET_NIF: + /* + * Set NIF - allow user to configure 1 or more logical + * interfaces per physical interface. + */ + + /* + * Get pointer to physical interface structure from + * ioctl argument. + */ + pip = (struct atm_pif *)arg; + cup = (Cmn_unit *)pip; + + /* + * Sanity check - are we already connected to something? + */ + if ( pip->pif_sigmgr ) + { + err = EBUSY; + break; + } + + /* + * Free any previously allocated NIFs + */ + atm_physif_freenifs(pip); + + /* + * Add list of interfaces + */ + for ( count = 0; count < asr->asr_nif_cnt; count++ ) + { + nip = (struct atm_nif *)atm_allocate(cup->cu_nif_pool); + if ( nip == NULL ) + { + /* + * Destroy any successful nifs + */ + atm_physif_freenifs(pip); + err = ENOMEM; + break; + } + + nip->nif_pif = pip; + ifp = &nip->nif_if; + + strcpy ( nip->nif_name, asr->asr_nif_pref ); + nip->nif_sel = count; + + ifp->if_name = nip->nif_name; + ifp->if_unit = count; + ifp->if_mtu = ATM_NIF_MTU; + ifp->if_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; + ifp->if_output = atm_ifoutput; + ifp->if_ioctl = atm_if_ioctl; + ifp->if_snd.ifq_maxlen = ifqmaxlen; +#if (defined(BSD) && (BSD >= 199103)) + /* + * Set if_type and if_baudrate + */ + ifp->if_type = IFT_ATM; + switch ( cup->cu_config.ac_media ) { + case MEDIA_TAXI_100: + ifp->if_baudrate = 100000000; + break; + case MEDIA_TAXI_140: + ifp->if_baudrate = 140000000; + break; + case MEDIA_OC3C: + case MEDIA_OC12C: + case MEDIA_UTP155: + ifp->if_baudrate = 155000000; + break; + } +#endif + + if ( err = atm_nif_attach ( nip ) ) + { + atm_free ( (caddr_t)nip ); + + /* + * Destroy any successful nifs + */ + atm_physif_freenifs(pip); + break; + } +#if (defined(BSD) && (BSD >= 199103)) + /* + * Set macaddr in <Link> address + */ + ifp->if_addrlen = 6; + ifa = ifnet_addrs[ifp->if_index - 1]; + if ( ifa ) { + sdl = (struct sockaddr_dl *) + ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ifp->if_addrlen; + bcopy ( (caddr_t)&cup->cu_config.ac_macaddr, + LLADDR(sdl), ifp->if_addrlen ); + } +#endif + } + break; + + case AIOCS_INF_CFG: + /* + * Get adapter configuration information + */ + aip = (struct atminfreq *)data; + pip = (struct atm_pif *)arg; + cup = (Cmn_unit *)pip; + acp = &cup->cu_config; + + /* + * Make sure there's room in user buffer + */ + if (aip->air_buf_len < sizeof(acr)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + KM_ZERO((caddr_t)&acr, sizeof(acr)); + (void) sprintf(acr.acp_intf, "%s%d", pip->pif_name, + pip->pif_unit); + KM_COPY((caddr_t)acp, (caddr_t)&acr.acp_cfg, + sizeof(Atm_config)); + + /* + * Copy data to user buffer + */ + err = copyout((caddr_t)&acr, aip->air_buf_addr, + sizeof(acr)); + if (err) + break; + + /* + * Update buffer pointer/count + */ + aip->air_buf_addr += sizeof(acr); + aip->air_buf_len -= sizeof(acr); + break; + + case AIOCS_INF_VST: + /* + * Pass off to device-specific handler + */ + cup = (Cmn_unit *)arg; + if (cup == NULL) + err = ENXIO; + else + err = (*cup->cu_ioctl)(code, data, arg); + break; + + default: + err = ENOSYS; + } + + return ( err ); +} + + +/* + * Register a Network Convergence Module + * + * Each ATM network convergence module must register itself here before + * it will receive network interface status notifications. + * + * Arguments: + * ncp pointer to network convergence definition structure + * + * Returns: + * 0 registration successful + * errno registration failed - reason indicated + * + */ +int +atm_netconv_register(ncp) + struct atm_ncm *ncp; +{ + struct atm_ncm *tdp; + int s = splnet(); + + /* + * See if we need to be initialized + */ + if (!atm_init) + atm_initialize(); + + /* + * Validate protocol family + */ + if (ncp->ncm_family > AF_MAX) { + (void) splx(s); + return (EINVAL); + } + + /* + * Ensure no duplicates + */ + for (tdp = atm_netconv_head; tdp != NULL; tdp = tdp->ncm_next) { + if (tdp->ncm_family == ncp->ncm_family) { + (void) splx(s); + return (EEXIST); + } + } + + /* + * Add module to list + */ + LINK2TAIL(ncp, struct atm_ncm, atm_netconv_head, ncm_next); + + /* + * Add new interface output function + */ + atm_ifouttbl[ncp->ncm_family] = ncp->ncm_ifoutput; + + (void) splx(s); + return (0); +} + + +/* + * De-register an ATM Network Convergence Module + * + * Each ATM network convergence provider must de-register its registered + * service(s) before terminating. Specifically, loaded kernel modules + * must de-register their services before unloading themselves. + * + * Arguments: + * ncp pointer to network convergence definition structure + * + * Returns: + * 0 de-registration successful + * errno de-registration failed - reason indicated + * + */ +int +atm_netconv_deregister(ncp) + struct atm_ncm *ncp; +{ + int found, s = splnet(); + + /* + * Remove module from list + */ + UNLINKF(ncp, struct atm_ncm, atm_netconv_head, ncm_next, found); + + if (!found) { + (void) splx(s); + return (ENOENT); + } + + /* + * Remove module's interface output function + */ + atm_ifouttbl[ncp->ncm_family] = NULL; + + (void) splx(s); + return (0); +} + + +/* + * Attach an ATM Network Interface + * + * Before an ATM network interface can be used by the system, the owning + * device interface must attach the network interface using this function. + * The physical interface for this network interface must have been previously + * registered (using atm_interface_register). The network interface will be + * added to the kernel's interface list and to the physical interface's list. + * The caller is responsible for initializing the control block fields. + * + * Arguments: + * nip pointer to atm network interface control block + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +int +atm_nif_attach(nip) + struct atm_nif *nip; +{ + struct atm_pif *pip, *pip2; + struct ifnet *ifp; + struct atm_ncm *ncp; + int s; + + ifp = &nip->nif_if; + pip = nip->nif_pif; + + s = splimp(); + + /* + * Verify physical interface is registered + */ + for (pip2 = atm_interface_head; pip2 != NULL; pip2 = pip2->pif_next) { + if (pip == pip2) + break; + } + if ((pip == NULL) || (pip2 == NULL)) { + (void) splx(s); + return (EFAULT); + } + + /* + * Add to system interface list + */ + if_attach(ifp); + + /* + * Add to physical interface list + */ + LINK2TAIL(nip, struct atm_nif, pip->pif_nif, nif_pnext); + + /* + * Notify network convergence modules of new network i/f + */ + for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) { + int err; + + err = (*ncp->ncm_stat)(NCM_ATTACH, nip, 0); + if (err) { + atm_nif_detach(nip); + (void) splx(s); + return (err); + } + } + + (void) splx(s); + return (0); +} + + +/* + * Detach an ATM Network Interface + * + * Before an ATM network interface control block can be freed, all kernel + * references to/from this block must be released. This function will delete + * all routing references to the interface and free all interface addresses + * for the interface. The network interface will then be removed from the + * kernel's interface list and from the owning physical interface's list. + * The caller is responsible for free'ing the control block. + * + * Arguments: + * nip pointer to atm network interface control block + * + * Returns: + * none + * + */ +void +atm_nif_detach(nip) + struct atm_nif *nip; +{ + struct atm_ncm *ncp; + int s, i; + struct ifnet *ifp = &nip->nif_if; + struct ifaddr *ifa; + struct in_ifaddr *ia; + struct radix_node_head *rnh; + + + s = splimp(); + + /* + * Notify convergence modules of network i/f demise + */ + for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) { + (void) (*ncp->ncm_stat)(NCM_DETACH, nip, 0); + } + + /* + * Mark interface down + */ + if_down(ifp); + + /* + * Free all interface routes and addresses + */ + while (1) { + IFP_TO_IA(ifp, ia); + if (ia == NULL) + break; + + /* Delete interface route */ + in_ifscrub(ifp, ia); + + /* Remove interface address from queues */ + ifa = &ia->ia_ifa; + TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); + TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); + + /* Free interface address */ + IFAFREE(ifa); + } + + /* + * Delete all remaining routes using this interface + * Unfortuneatly the only way to do this is to slog through + * the entire routing table looking for routes which point + * to this interface...oh well... + */ + for (i = 1; i <= AF_MAX; i++) { + if ((rnh = rt_tables[i]) == NULL) + continue; + (void) rnh->rnh_walktree(rnh, atm_netif_rtdel, ifp); + } + + /* + * Remove from system interface list (ie. if_detach()) + */ + TAILQ_REMOVE(&ifnet, ifp, if_link); + + /* + * Remove from physical interface list + */ + UNLINK(nip, struct atm_nif, nip->nif_pif->pif_nif, nif_pnext); + + (void) splx(s); +} + + +/* + * Delete Routes for a Network Interface + * + * Called for each routing entry via the rnh->rnh_walktree() call above + * to delete all route entries referencing a detaching network interface. + * + * Arguments: + * rn pointer to node in the routing table + * arg argument passed to rnh->rnh_walktree() - detaching interface + * + * Returns: + * 0 successful + * errno failed - reason indicated + * + */ +static int +atm_netif_rtdel(rn, arg) + struct radix_node *rn; + void *arg; +{ + struct rtentry *rt = (struct rtentry *)rn; + struct ifnet *ifp = arg; + int err; + + if (rt->rt_ifp == ifp) { + + /* + * Protect (sorta) against walktree recursion problems + * with cloned routes + */ + if ((rt->rt_flags & RTF_UP) == 0) + return (0); + + err = rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, + rt_mask(rt), rt->rt_flags, + (struct rtentry **) NULL); + if (err) { + log(LOG_WARNING, "atm_netif_rtdel: error %d\n", err); + } + } + + return (0); +} + + +/* + * Set an ATM Network Interface address + * + * This is called from a device interface when processing an SIOCSIFADDR + * ioctl request. We just notify all convergence modules of the new address + * and hope everyone has non-overlapping interests, since if someone reports + * an error we don't go back and tell everyone to undo the change. + * + * Arguments: + * nip pointer to atm network interface control block + * ifa pointer to new interface address + * + * Returns: + * 0 set successful + * errno set failed - reason indicated + * + */ +int +atm_nif_setaddr(nip, ifa) + struct atm_nif *nip; + struct ifaddr *ifa; +{ + struct atm_ncm *ncp; + int err = 0, s = splnet(); + + /* + * Notify convergence modules of network i/f change + */ + for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) { + err = (*ncp->ncm_stat)(NCM_SETADDR, nip, (int)ifa); + if (err) + break; + } + (void) splx(s); + + return (err); +} + + +/* + * ATM Interface Packet Output + * + * All ATM network interfaces must have their ifnet if_output address set to + * this function. Since no existing network layer code is to be modified + * for ATM support, this function serves as the hook to allow network output + * packets to be assigned to their proper outbound VCC. Each network address + * family which is to be supported over ATM must be assigned an output + * packet processing function via atm_netconv_register(). + * + * Arguments: + * ifp pointer to ifnet structure + * m pointer to packet buffer chain to be output + * dst pointer to packet's network destination address + * + * Returns: + * 0 packet queued to interface + * errno output failed - reason indicated + * + */ +int +#if (defined(BSD) && (BSD >= 199103)) +atm_ifoutput(ifp, m, dst, rt) +#else +atm_ifoutput(ifp, m, dst) +#endif + struct ifnet *ifp; + KBuffer *m; + struct sockaddr *dst; +#if (defined(BSD) && (BSD >= 199103)) + struct rtentry *rt; +#endif +{ + u_short fam = dst->sa_family; + int (*func)__P((struct ifnet *, KBuffer *, + struct sockaddr *)); + + /* + * Validate address family + */ + if (fam > AF_MAX) { + KB_FREEALL(m); + return (EAFNOSUPPORT); + } + + /* + * Hand packet off for dst-to-VCC mapping + */ + func = atm_ifouttbl[fam]; + if (func == NULL) { + KB_FREEALL(m); + return (EAFNOSUPPORT); + } + return ((*func)(ifp, m, dst)); +} + + +/* + * Handle interface ioctl requests. + * + * Arguments: + * ifp pointer to network interface structure + * cmd IOCTL cmd + * data arguments to/from ioctl + * + * Returns: + * error errno value + */ +static int +atm_if_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + register struct ifreq *ifr = (struct ifreq *)data; + struct atm_nif *nip = (struct atm_nif *)ifp; + int error = 0; + int s = splnet(); + + switch ( cmd ) + { + case SIOCGIFADDR: + KM_COPY ( (caddr_t)&(nip->nif_pif->pif_macaddr), + (caddr_t)ifr->ifr_addr.sa_data, + sizeof(struct mac_addr) ); + break; + + case SIOCSIFADDR: + error = atm_nif_setaddr ( nip, (struct ifaddr *)data); + ifp->if_flags |= IFF_UP | IFF_RUNNING | IFF_BROADCAST; + break; + + case SIOCGIFFLAGS: + *(short *)data = ifp->if_flags; + break; + + case SIOCSIFFLAGS: + break; + + default: + error = EINVAL; + break; + } + + (void) splx(s); + return ( error ); +} + + +/* + * Parse interface name + * + * Parses an interface name string into a name and a unit component. + * + * Arguments: + * name pointer to interface name string + * namep address to store interface name + * size size available at namep + * unitp address to store interface unit number + * + * Returns: + * 0 name parsed + * else parse error + * + */ +static int +atm_ifparse(name, namep, size, unitp) + char *name; + char *namep; + int size; + int *unitp; +{ + char *cp, *np; + int len = 0, unit = 0; + + /* + * Separate supplied string into name and unit parts. + */ + cp = name; + np = namep; + while (*cp) { + if (*cp >= '0' && *cp <= '9') + break; + if (++len >= size) + return (-1); + *np++ = *cp++; + } + *np = '\0'; + while (*cp && *cp >= '0' && *cp <= '9') + unit = 10 * unit + *cp++ - '0'; + + *unitp = unit; + + return (0); +} + + +/* + * Locate ATM physical interface via name + * + * Uses the supplied interface name string to locate a registered + * ATM physical interface. + * + * Arguments: + * name pointer to interface name string + * + * Returns: + * 0 interface not found + * else pointer to atm physical interface structure + * + */ +struct atm_pif * +atm_pifname(name) + char *name; +{ + struct atm_pif *pip; + char n[IFNAMSIZ]; + int unit; + + /* + * Break down name + */ + if (atm_ifparse(name, n, sizeof(n), &unit)) + return ((struct atm_pif *)0); + + /* + * Look for the physical interface + */ + for (pip = atm_interface_head; pip; pip = pip->pif_next) { + if ((pip->pif_unit == unit) && (strcmp(pip->pif_name, n) == 0)) + break; + } + + return (pip); +} + + +/* + * Locate ATM network interface via name + * + * Uses the supplied interface name string to locate an ATM network interface. + * + * Arguments: + * name pointer to interface name string + * + * Returns: + * 0 interface not found + * else pointer to atm network interface structure + * + */ +struct atm_nif * +atm_nifname(name) + char *name; +{ + struct atm_pif *pip; + struct atm_nif *nip; + char n[IFNAMSIZ]; + int unit; + + /* + * Break down name + */ + if (atm_ifparse(name, n, sizeof(n), &unit)) + return ((struct atm_nif *)0); + + /* + * Search thru each physical interface + */ + for (pip = atm_interface_head; pip; pip = pip->pif_next) { + /* + * Looking for network interface + */ + for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) { + struct ifnet *ifp = (struct ifnet *)nip; + if ((ifp->if_unit == unit) && + (strcmp(ifp->if_name, n) == 0)) + return (nip); + } + } + return (NULL); +} + diff --git a/sys/netatm/atm_if.h b/sys/netatm/atm_if.h new file mode 100644 index 0000000..ccf9d03 --- /dev/null +++ b/sys/netatm/atm_if.h @@ -0,0 +1,392 @@ +/* + * + * =================================== + * 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: atm_if.h,v 1.13 1998/08/07 22:10:37 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Physical and Network Interface definitions + * + */ + +#ifndef _NETATM_ATM_IF_H +#define _NETATM_ATM_IF_H + +/* + * Handy constants + */ +#define ATM_NIF_MTU 9180 /* Default network interface MTU */ + +#define ATM_PCR_TAXI100 227273 /* Peak Cell Rate for 100 Mbs TAXI */ +#define ATM_PCR_TAXI140 318181 /* Peak Cell Rate for 140 Mbs TAXI */ +#define ATM_PCR_OC3C 353207 /* Peak Cell Rate for OC3c */ +#define ATM_PCR_OC12C 1416905 /* Peak Cell Rate for OC12c */ + + +/* + * Media Access Control (MAC) address + */ +struct mac_addr { + u_char ma_data[6]; /* MAC address */ +}; +typedef struct mac_addr Mac_addr; + + +/* + * Adapter vendor identifiers + */ +enum atm_vendor { + VENDOR_UNKNOWN, /* Unknown vendor */ + VENDOR_FORE, /* FORE Systems, Inc. */ + VENDOR_ENI /* Efficient Networks, Inc. */ +}; +typedef enum atm_vendor Atm_vendor; + + +/* + * Adapter vendor interface identifiers + */ +enum atm_vendapi { + VENDAPI_UNKNOWN, /* Unknown interface */ + VENDAPI_FORE_1, /* FORE - 200 Series */ + VENDAPI_ENI_1 /* ENI - Midway */ +}; +typedef enum atm_vendapi Atm_vendapi; + + +/* + * Adapter device model identifiers + */ +enum atm_device { + DEV_UNKNOWN, /* Unknown device */ + DEV_FORE_SBA200E, /* FORE SBA-200E */ + DEV_FORE_SBA200, /* FORE SBA-200 */ + DEV_FORE_PCA200E, /* FORE PCA-200E */ + DEV_ENI_155P /* ENI-155p */ +}; +typedef enum atm_device Atm_device; + + +/* + * Adapter media identifiers + */ +enum atm_media { + MEDIA_UNKNOWN, /* Unknown media type */ + MEDIA_TAXI_100, /* TAXI - 100 Mbps */ + MEDIA_TAXI_140, /* TAXI - 140 Mbps */ + MEDIA_OC3C, /* OC-3C */ + MEDIA_OC12C, /* OC-12C */ + MEDIA_UTP155 /* UTP-155 */ +}; +typedef enum atm_media Atm_media; + + +/* + * Bus type identifiers + */ +enum atm_bus { + BUS_UNKNOWN, /* Unknown bus type */ + BUS_SBUS_B16, /* SBus: 16 byte (4 word) max burst */ + BUS_SBUS_B32, /* SBus: 32 byte (8 word) max burst */ + BUS_PCI /* PCI */ +}; +typedef enum atm_bus Atm_bus; + + +#define VERSION_LEN 16 /* Length of version info string */ + + +/* + * ATM adapter configuration information structure + */ +struct atm_config { + Atm_vendor ac_vendor; /* Vendor */ + Atm_vendapi ac_vendapi; /* Vendor interface */ + Atm_device ac_device; /* Device model */ + Atm_media ac_media; /* Media type */ + u_long ac_serial; /* Serial number */ + Atm_bus ac_bustype; /* Bus type */ + u_long ac_busslot; /* Bus slot info (bus type dependent) */ + u_long ac_ram; /* Device ram offset */ + u_long ac_ramsize; /* Device ram size */ + Mac_addr ac_macaddr; /* MAC address */ + char ac_hard_vers[VERSION_LEN]; /* Hardware version */ + char ac_firm_vers[VERSION_LEN]; /* Firmware version */ +}; +typedef struct atm_config Atm_config; + + +#ifdef ATM_KERNEL +/* + * Common structure used to define each physical ATM device interface. + * This structure will (normally) be embedded at the top of each driver's + * device-specific interface structure. + */ +struct atm_pif { + struct atm_pif *pif_next; /* Next registered atm interface */ + char *pif_name; /* Device name */ + short pif_unit; /* Device unit number */ + u_char pif_flags; /* Interface flags (see below) */ + struct sigmgr *pif_sigmgr; /* Signalling Manager for interface */ + struct siginst *pif_siginst; /* Signalling protocol instance */ + struct stack_defn *pif_services; /* Interface's stack services */ + struct mac_addr pif_macaddr; /* Interface's MAC address */ + struct atm_nif *pif_nif; /* List of network interfaces */ + struct atm_pif *pif_grnext; /* Next atm device in group */ + +/* Exported functions */ + int (*pif_ioctl) /* Interface ioctl handler */ + __P((int, caddr_t, caddr_t)); + +/* Interface statistics */ + long pif_ipdus; /* PDUs received from interface */ + long pif_opdus; /* PDUs sent to interface */ + long pif_ibytes; /* Bytes received from interface */ + long pif_obytes; /* Bytes sent to interface */ + long pif_ierrors; /* Errors receiving from interface */ + long pif_oerrors; /* Errors sending to interface */ + long pif_cmderrors; /* Interface command errors */ + caddr_t pif_cardstats; /* Card specific statistics */ + +/* Interface capabilities */ + u_short pif_maxvpi; /* Maximum VPI value supported */ + u_short pif_maxvci; /* Maximum VCI value supported */ + u_int pif_pcr; /* Peak Cell Rate */ +}; + +/* + * Physical interface flags + */ +#define PIF_UP 0x01 /* Interface is up */ +#define PIF_LOOPBACK 0x02 /* Loopback local packets */ + + +/* + * Structure defining an ATM network interface. This structure is used as + * the hook between the standard BSD network layer interface mechanism and + * the ATM device layer. There may be one or more network interfaces for + * each physical ATM interface. + */ +struct atm_nif { + struct ifnet nif_if; /* Network interface */ + struct atm_pif *nif_pif; /* Our physical interface */ + char nif_name[IFNAMSIZ];/* Network interface name */ + u_char nif_sel; /* Interface's address selector */ + struct atm_nif *nif_pnext; /* Next net interface on phys i/f */ + +/* Interface statistics (in addition to ifnet stats) */ + long nif_ibytes; /* Bytes received from interface */ + long nif_obytes; /* Bytes sent to interface */ +}; + + +/* + * Common Device VCC Entry + * + * Contains the common information for each VCC which is opened + * through a particular device. + */ +struct cmn_vcc { + struct cmn_vcc *cv_next; /* Next in list */ + void *cv_toku; /* Upper layer's token */ + void (*cv_upper) /* Upper layer's interface */ + __P((int, void *, int, int)); + Atm_connvc *cv_connvc; /* Associated connection VCC */ + u_char cv_state; /* VCC state (see below) */ + u_char cv_flags; /* VCC flags (see below) */ +}; +typedef struct cmn_vcc Cmn_vcc; + +/* + * VCC States + */ +#define CVS_FREE 0 /* Not allocated */ +#define CVS_INST 1 /* Instantiated, waiting for INIT */ +#define CVS_INITED 2 /* Initialized, waiting for driver */ +#define CVS_ACTIVE 3 /* Device activated by driver */ +#define CVS_PTERM 4 /* Waiting for TERM */ +#define CVS_TERM 5 /* Terminated */ + +/* + * VCC Flags + */ +#define CVF_RSVD 0x0f /* Reserved for device-specific use */ + + +/* + * Common Device Unit Structure + * + * Contains the common information for a single device (adapter). + */ +struct cmn_unit { + struct atm_pif cu_pif; /* Physical interface */ + u_int cu_unit; /* Local unit number */ + u_char cu_flags; /* Device flags (see below) */ + u_int cu_mtu; /* Interface MTU */ + + u_int cu_open_vcc; /* Open VCC count */ + Cmn_vcc *cu_vcc; /* List of VCC's on interface */ + + u_int cu_intrpri; /* Highest unit interrupt priority */ + int cu_savepri; /* Saved priority for locking device */ + + struct sp_info *cu_vcc_pool; /* Device VCC pool */ + struct sp_info *cu_nif_pool; /* Device NIF pool */ + + int (*cu_ioctl) /* Interface ioctl handler */ + __P((int, caddr_t, caddr_t)); + int (*cu_instvcc) /* VCC stack instantion handler */ + __P((struct cmn_unit *, Cmn_vcc *)); + int (*cu_openvcc) /* Open VCC handler */ + __P((struct cmn_unit *, Cmn_vcc *)); + int (*cu_closevcc) /* Close VCC handler */ + __P((struct cmn_unit *, Cmn_vcc *)); + void (*cu_output) /* Data output handler */ + __P((struct cmn_unit *, Cmn_vcc *, KBuffer *)); + + Atm_config cu_config; /* Device configuration data */ +}; +typedef struct cmn_unit Cmn_unit; + +/* + * Device flags + */ +#define CUF_REGISTER 0x01 /* Device is registered */ +#define CUF_INITED 0x02 /* Device is initialized */ + + +/* + * Structure used to define a network convergence module and its associated + * entry points. A convergence module is used to provide the interface + * translations necessary between the ATM system and the BSD network layer + * interface mechanism. There will be one network convergence module for + * each protocol address family supporting ATM connections. + */ +struct atm_ncm { + struct atm_ncm *ncm_next; /* Next in registry list */ + u_short ncm_family; /* Protocol family */ +/* Exported functions */ + int (*ncm_ifoutput) /* Interface if_output handler */ + __P((struct ifnet *, KBuffer *, + struct sockaddr *)); + int (*ncm_stat) /* Network i/f status handler */ + __P((int, struct atm_nif *, int)); +}; + +/* + * ncm_stat() commands + */ +#define NCM_ATTACH 1 /* Attaching a new net i/f */ +#define NCM_DETACH 2 /* Detaching a current net i/f */ +#define NCM_SETADDR 3 /* Net i/f address change */ +#define NCM_SIGATTACH 4 /* Attaching a signalling manager */ +#define NCM_SIGDETACH 5 /* Detaching a signalling manager */ + + +/* + * atm_dev_alloc() parameters + */ +#define ATM_DEV_NONCACHE 1 /* Allocate non-cacheable memory */ + +/* + * atm_dev_compress() buffer allocation sizes + */ +#if defined(BSD) +#define ATM_DEV_CMPR_LG MCLBYTES /* Size of large buffers */ +#define ATM_DEV_CMPR_SM MLEN /* Size of small buffers */ +#endif + +/* + * Macros to manage DMA addresses + */ +#if defined(sun4c) +#define DMA_INIT() +#define DMA_GET_ADDR(addr,len,align,flags) ((void *)(addr)) +#define DMA_FREE_ADDR(addr,daddr,len,flags) +#define DMA_RELEASE() + +#elif defined(sun4m) +#define DMA_INIT() +#define DMA_GET_ADDR(addr,len,align,flags) \ + (void *)atm_dma_map((addr),(len),(flags)) +#define DMA_FREE_ADDR(addr,daddr,len,flags) \ + (void)atm_dma_free((daddr),(flags)) +#define DMA_RELEASE() + +#elif defined(BSD) && defined(__i386__) +#define DMA_INIT() +#define DMA_GET_ADDR(addr,len,align,flags) ((void *)vtophys(addr)) +#define DMA_FREE_ADDR(addr,daddr,len,flags) +#define DMA_RELEASE() + +#else + #error - Must define hardware-specific requirements here +#endif + + +/* + * Macros to lock out device interrupts + */ +#if defined(sun) +#define DEVICE_LOCK(u) ((u)->cu_savepri = splr((u)->cu_intrpri)) +#endif +#if defined(__FreeBSD__) +#define DEVICE_LOCK(u) ((u)->cu_savepri = splimp()) +#endif +#define DEVICE_UNLOCK(u) ((void) splx((u)->cu_savepri)) + + +/* + * SBus defines + */ +#if defined(sun) +#define SBUS_BURST32 0x20 /* Device supports 32-byte bursts */ +#endif + + +/* + * Macro to schedule the ATM interrupt queue handler + */ +typedef void (atm_intr_t) __P((void *, KBuffer *)); /* Callback function type */ +typedef atm_intr_t *atm_intr_func_t; /* Pointer to callback function */ + +#ifdef sun +#define SCHED_ATM schednetisr(atm_intr) +#endif +#ifdef __FreeBSD__ +#define NETISR_ATM AF_ATM +#define SCHED_ATM schednetisr(NETISR_ATM) +#endif +#ifdef sgi +extern int atm_intr_index; +#define SCHED_ATM schednetisr(atm_intr_index) +#endif +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_IF_H */ diff --git a/sys/netatm/atm_ioctl.h b/sys/netatm/atm_ioctl.h new file mode 100644 index 0000000..a38d183 --- /dev/null +++ b/sys/netatm/atm_ioctl.h @@ -0,0 +1,432 @@ +/* + * + * =================================== + * 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: atm_ioctl.h,v 1.9 1998/08/26 23:29:01 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * PF_ATM socket ioctl definitions + * + */ + +#ifndef _NETATM_ATM_IOCTL_H +#define _NETATM_ATM_IOCTL_H + + +/* + * Structure for PF_ATM configure (AIOCCFG) socket ioctls + */ +struct atmcfgreq { + int acr_opcode; /* Sub-operation */ + union { + struct { + /* Configure - attach */ + char acru_att_intf[IFNAMSIZ];/* Interface name */ + u_char acru_att_proto; /* Signalling protocol */ + } acru_att; + struct { + /* Configure - detach */ + char acru_det_intf[IFNAMSIZ];/* Interface name */ + } acru_det; + } acr_u; +}; +#define acr_att_intf acr_u.acru_att.acru_att_intf +#define acr_att_proto acr_u.acru_att.acru_att_proto +#define acr_det_intf acr_u.acru_det.acru_det_intf + + +/* + * Structure for PF_ATM set (AIOCSET) socket ioctls + */ +struct atmsetreq { + int asr_opcode; /* Sub-operation */ + union { + /* ARP server */ + struct { + char asru_arp_intf[IFNAMSIZ];/* Interface name */ + Atm_addr asru_arp_addr; /* ARP srvr address */ + Atm_addr asru_arp_subaddr;/* ARP srvr subaddr */ + caddr_t asru_arp_pbuf; /* Prefix buffer addr */ + int asru_arp_plen; /* Prefix buffer len */ + } asru_asrvr; + /* MAC address */ + struct { + char asru_mac_intf[IFNAMSIZ];/* Interface name */ + struct mac_addr asru_mac_addr; /* MAC address */ + } asru_mac; + /* Network interface */ + struct { + char asru_nif_intf[IFNAMSIZ];/* Interface name */ + char asru_nif_pref[IFNAMSIZ];/* I/f prefix name */ + int asru_nif_cnt; /* Number of i/fs */ + } asru_nif; + /* NSAP prefix */ + struct { + char asru_prf_intf[IFNAMSIZ];/* Interface name */ + u_char asru_prf_pref[13]; /* NSAP prefix */ + } asru_prf; + } asr_u; +}; +#define asr_arp_intf asr_u.asru_asrvr.asru_arp_intf +#define asr_arp_addr asr_u.asru_asrvr.asru_arp_addr +#define asr_arp_pbuf asr_u.asru_asrvr.asru_arp_pbuf +#define asr_arp_plen asr_u.asru_asrvr.asru_arp_plen +#define asr_arp_subaddr asr_u.asru_asrvr.asru_arp_subaddr +#define asr_mac_intf asr_u.asru_mac.asru_mac_intf +#define asr_mac_addr asr_u.asru_mac.asru_mac_addr +#define asr_nif_intf asr_u.asru_nif.asru_nif_intf +#define asr_nif_pref asr_u.asru_nif.asru_nif_pref +#define asr_nif_cnt asr_u.asru_nif.asru_nif_cnt +#define asr_prf_intf asr_u.asru_prf.asru_prf_intf +#define asr_prf_pref asr_u.asru_prf.asru_prf_pref + + +/* + * Structure for PF_ATM add (AIOCADD) socket ioctls + */ +struct atmaddreq { + int aar_opcode; /* Sub-operation */ + union { + /* Add PVC */ + struct { + char aaru_pvc_intf[IFNAMSIZ];/* Interface name */ + u_short aaru_pvc_vpi; /* VPI value */ + u_short aaru_pvc_vci; /* VCI value */ + struct sockaddr aaru_pvc_dst; /* Destination addr */ + Sap_t aaru_pvc_sap; /* Endpoint SAP */ + Aal_t aaru_pvc_aal; /* AAL */ + Encaps_t aaru_pvc_encaps; /* Encapsulation */ + u_char aaru_pvc_flags; /* Flags (see below) */ + } aaru_add_pvc; + + /* Add ARP table entry */ + struct { + char aaru_arp_intf[IFNAMSIZ];/* Interface name */ + struct sockaddr aaru_arp_dst; /* IP addr */ + Atm_addr aaru_arp_addr; /* ATM addr */ + u_char aaru_arp_origin; /* Entry origin */ + } aaru_add_arp; + } aar_u; +}; +#define aar_pvc_intf aar_u.aaru_add_pvc.aaru_pvc_intf +#define aar_pvc_vpi aar_u.aaru_add_pvc.aaru_pvc_vpi +#define aar_pvc_vci aar_u.aaru_add_pvc.aaru_pvc_vci +#define aar_pvc_dst aar_u.aaru_add_pvc.aaru_pvc_dst +#define aar_pvc_sap aar_u.aaru_add_pvc.aaru_pvc_sap +#define aar_pvc_aal aar_u.aaru_add_pvc.aaru_pvc_aal +#define aar_pvc_encaps aar_u.aaru_add_pvc.aaru_pvc_encaps +#define aar_pvc_flags aar_u.aaru_add_pvc.aaru_pvc_flags +#define aar_arp_intf aar_u.aaru_add_arp.aaru_arp_intf +#define aar_arp_dst aar_u.aaru_add_arp.aaru_arp_dst +#define aar_arp_addr aar_u.aaru_add_arp.aaru_arp_addr +#define aar_arp_origin aar_u.aaru_add_arp.aaru_arp_origin + +/* PVC flags */ +#define PVC_DYN 0x01 /* Dest addr is dynamic */ + + +/* + * Structure for PF_ATM delete (AIOCDEL) socket ioctls + */ +struct atmdelreq { + int adr_opcode; /* Sub-operation */ + union { + /* Delete PVC */ + struct { + char adru_pvc_intf[IFNAMSIZ];/* Interface name */ + u_short adru_pvc_vpi; /* VPI value */ + u_short adru_pvc_vci; /* VCI value */ + } adru_del_pvc; + + /* Delete SVC */ + struct { + char adru_svc_intf[IFNAMSIZ];/* Interface name */ + u_short adru_svc_vpi; /* VPI value */ + u_short adru_svc_vci; /* VCI value */ + } adru_del_svc; + + /* Delete ARP table entry */ + struct { + char adru_arp_intf[IFNAMSIZ];/* Interface name */ + struct sockaddr adru_arp_dst; /* IP addr */ + } adru_del_arp; + } adr_u; +}; +#define adr_pvc_intf adr_u.adru_del_pvc.adru_pvc_intf +#define adr_pvc_vpi adr_u.adru_del_pvc.adru_pvc_vpi +#define adr_pvc_vci adr_u.adru_del_pvc.adru_pvc_vci +#define adr_svc_intf adr_u.adru_del_svc.adru_svc_intf +#define adr_svc_vpi adr_u.adru_del_svc.adru_svc_vpi +#define adr_svc_vci adr_u.adru_del_svc.adru_svc_vci +#define adr_arp_intf adr_u.adru_del_arp.adru_arp_intf +#define adr_arp_dst adr_u.adru_del_arp.adru_arp_dst + + +/* + * Structure for PF_ATM information (AIOCINFO) socket ioctls + */ +struct atminfreq { + int air_opcode; /* Sub-operation */ + caddr_t air_buf_addr; /* Buffer for returned info */ + int air_buf_len; /* Buffer length */ + union { + /* Vendor info */ + char airu_vinfo_intf[IFNAMSIZ];/* Interface name */ + /* IP VCC */ + struct sockaddr airu_ip_addr; /* Destination host */ + /* ARP table */ + struct { + struct sockaddr airu_arp_addr; /* Destination host */ + u_char airu_arp_flags; /* Flags (see below) */ + } airu_arp; + /* ARP server */ + char airu_asrv_intf[IFNAMSIZ];/* Interface name */ + /* Interface */ + char airu_int_intf[IFNAMSIZ];/* Interface name */ + /* VCC */ + char airu_vcc_intf[IFNAMSIZ];/* Interface name */ + /* Configuration */ + char airu_cfg_intf[IFNAMSIZ];/* Interface name */ + /* Network interface */ + char airu_netif_intf[IFNAMSIZ];/* Interface name */ + /* Physical interface statistics */ + char airu_physt_intf[IFNAMSIZ];/* Interface name */ + } air_u; +}; +#define air_vinfo_intf air_u.airu_vinfo_intf +#define air_ip_addr air_u.airu_ip_addr +#define air_arp_addr air_u.airu_arp.airu_arp_addr +#define air_arp_flags air_u.airu_arp.airu_arp_flags +#define air_asrv_intf air_u.airu_asrv_intf +#define air_int_intf air_u.airu_int_intf +#define air_vcc_intf air_u.airu_vcc_intf +#define air_cfg_intf air_u.airu_cfg_intf +#define air_netif_intf air_u.airu_netif_intf +#define air_physt_intf air_u.airu_physt_intf + +/* ARP table info flags */ +#define ARP_RESET_REF 0x01 /* Reset refresh status */ + + +/* + * Structures returned by information requests + */ + +/* + * Vendor-specific interface information + */ +struct air_vinfo_rsp { + char avsp_intf[IFNAMSIZ]; /* Interface name */ + int avsp_len; /* Length of returned + Vendor Info block */ + /* Vendor info ... */ +}; + + +/* + * ARP table information + */ +struct air_arp_rsp { + struct sockaddr aap_arp_addr; /* Destination host */ + char aap_intf[IFNAMSIZ]; /* Interface name */ + u_char aap_flags; /* Flags (see below) */ + u_char aap_origin; /* Entry origin (see below) */ + u_char aap_age; /* Aging timeout (minutes) */ + Atm_addr aap_addr; /* ATM address */ + Atm_addr aap_subaddr; /* ATM subaddress */ +}; + +/* + * ARP entry flags + */ +#define ARPF_VALID 0x01 /* Entry is valid */ +#define ARPF_REFRESH 0x02 /* Entry has been refreshed */ + +/* + * ARP entry origin + */ +#define ARP_ORIG_PERM 50 /* Permanent entry */ + +/* + * IP VCC information + */ +struct air_ip_vcc_rsp { + struct sockaddr aip_dst_addr; /* Destination host */ + char aip_intf[IFNAMSIZ]; /* Interface name */ + u_short aip_vpi; /* VPI value */ + u_short aip_vci; /* VCI value */ + u_char aip_sig_proto; /* Signalling protocol */ + u_char aip_flags; /* Flags (IVF_*) */ + u_char aip_state; /* IP VCC state */ +}; + +/* + * ARP server information + */ +struct air_asrv_rsp { + char asp_intf[IFNAMSIZ]; /* Interface name */ + Atm_addr asp_addr; /* Server ATM address */ + Atm_addr asp_subaddr; /* Server ATM subaddress */ + int asp_state; /* Server state */ + int asp_nprefix; /* Number of prefix entries */ +}; + +/* + * Interface information + */ +struct air_int_rsp { + char anp_intf[IFNAMSIZ]; /* Interface name */ + Atm_addr anp_addr; /* ATM address */ + Atm_addr anp_subaddr; /* ATM subaddress */ + u_char anp_sig_proto; /* Signalling protocol */ + u_char anp_sig_state; /* Signalling protocol state */ + char anp_nif_pref[IFNAMSIZ]; /* Netif prefix */ + u_char anp_nif_cnt; /* No. of netifs */ +}; + +/* + * Network interface information + */ +struct air_netif_rsp { + char anp_intf[IFNAMSIZ]; /* Interface name */ + struct sockaddr anp_proto_addr; /* Protocol address */ + char anp_phy_intf[IFNAMSIZ]; /* Interface name */ +}; + +/* + * VCC information + */ +#define O_CNT 8 +struct air_vcc_rsp { + char avp_intf[IFNAMSIZ]; /* Interface name */ + u_short avp_vpi; /* VPI value */ + u_short avp_vci; /* VCI value */ + u_char avp_type; /* Type (SVC or PVC) */ + u_char avp_aal; /* AAL */ + u_char avp_sig_proto; /* Signalling protocol */ + Encaps_t avp_encaps; /* Encapsulation */ + u_char avp_state; /* State (sig mgr specific) */ + char avp_owners[(T_ATM_APP_NAME_LEN+1)*O_CNT];/* VCC users */ + Atm_addr avp_daddr; /* Address of far end */ + Atm_addr avp_dsubaddr; /* Subaddress of far end */ + long avp_ipdus; /* PDUs received from VCC */ + long avp_opdus; /* PDUs sent to VCC */ + long avp_ibytes; /* Bytes received from VCC */ + long avp_obytes; /* Bytes sent to VCC */ + long avp_ierrors; /* Errors receiving from VCC */ + long avp_oerrors; /* Errors sending to VCC */ + time_t avp_tstamp; /* State transition timestamp */ +}; + +/* + * Adapter configuration information + */ +struct air_cfg_rsp { + char acp_intf[IFNAMSIZ]; /* Interface name */ + Atm_config acp_cfg; /* Config info */ +}; +#define acp_vendor acp_cfg.ac_vendor +#define acp_vendapi acp_cfg.ac_vendapi +#define acp_device acp_cfg.ac_device +#define acp_media acp_cfg.ac_media +#define acp_serial acp_cfg.ac_serial +#define acp_bustype acp_cfg.ac_bustype +#define acp_busslot acp_cfg.ac_busslot +#define acp_ram acp_cfg.ac_ram +#define acp_ramsize acp_cfg.ac_ramsize +#define acp_macaddr acp_cfg.ac_macaddr +#define acp_hard_vers acp_cfg.ac_hard_vers +#define acp_firm_vers acp_cfg.ac_firm_vers + +/* + * Version information + */ +struct air_version_rsp { + int avp_version; /* Software version */ +}; + +/* + * Physical interface statistics + */ +struct air_phy_stat_rsp { + char app_intf[IFNAMSIZ]; /* Interface name */ + long app_ipdus; /* PDUs received from I/F */ + long app_opdus; /* PDUs sent to I/F */ + long app_ibytes; /* Bytes received from I/F */ + long app_obytes; /* Bytes sent to I/F */ + long app_ierrors; /* Errors receiving from I/F */ + long app_oerrors; /* Errors sending to I/F */ + long app_cmderrors; /* I/F command errors */ +}; + + +/* + * PF_ATM sub-operation codes + */ +#define AIOCS_CFG_ATT 1 +#define AIOCS_CFG_DET 2 +#define AIOCS_ADD_PVC 32 +#define AIOCS_ADD_ARP 33 +#define AIOCS_DEL_PVC 64 +#define AIOCS_DEL_SVC 65 +#define AIOCS_DEL_ARP 66 +#define AIOCS_SET_ASV 96 +#define AIOCS_SET_NIF 97 +#define AIOCS_SET_PRF 98 +#define AIOCS_SET_MAC 99 +#define AIOCS_INF_VST 160 +#define AIOCS_INF_IPM 161 +#define AIOCS_INF_ARP 162 +#define AIOCS_INF_ASV 163 +#define AIOCS_INF_INT 164 +#define AIOCS_INF_VCC 165 +#define AIOCS_INF_CFG 166 +#define AIOCS_INF_NIF 167 +#define AIOCS_INF_PIS 168 +#define AIOCS_INF_VER 169 + + +/* + * PF_ATM ioctls + */ +#if defined(sun) && !defined(__GNUC__) +#define AIOCCFG _IOW(A, 128, struct atmcfgreq) /* Configure i/f */ +#define AIOCADD _IOW(A, 129, struct atmaddreq) /* Add (e.g. PVC) */ +#define AIOCDEL _IOW(A, 130, struct atmdelreq) /* Delete */ +#define AIOCSET _IOW(A, 132, struct atmsetreq) /* Set (e.g. net i/f) */ +#define AIOCINFO _IOWR(A, 133, struct atminfreq) /* Show kernel info */ +#else +#define AIOCCFG _IOW('A', 128, struct atmcfgreq)/* Configure i/f */ +#define AIOCADD _IOW('A', 129, struct atmaddreq)/* Add (e.g. PVC) */ +#define AIOCDEL _IOW('A', 130, struct atmdelreq)/* Delete */ +#define AIOCSET _IOW('A', 132, struct atmsetreq)/* Set (e.g. net i/f) */ +#define AIOCINFO _IOWR('A', 133, struct atminfreq)/* Show kernel info */ +#endif + +#endif /* _NETATM_ATM_IOCTL_H */ diff --git a/sys/netatm/atm_pcb.h b/sys/netatm/atm_pcb.h new file mode 100644 index 0000000..12ac5d0 --- /dev/null +++ b/sys/netatm/atm_pcb.h @@ -0,0 +1,92 @@ +/* + * + * =================================== + * 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: atm_pcb.h,v 1.2 1998/07/30 22:30:51 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM socket protocol definitions + * + */ + +#ifndef _NETATM_ATM_PCB_H +#define _NETATM_ATM_PCB_H + + +#ifdef ATM_KERNEL +/* + * ATM Socket PCB + * + * Common structure for all ATM protocol sockets. This control block + * will be used for all ATM socket types. + */ +struct atm_pcb { + struct socket *atp_socket; /* Socket */ + Atm_connection *atp_conn; /* Connection manager token */ + u_char atp_type; /* Protocol type (see below) */ + u_char atp_flags; /* Protocol flags (see below) */ + Atm_attributes atp_attr; /* Socket's call attributes */ + char atp_name[T_ATM_APP_NAME_LEN]; /* Owner's name */ +}; +typedef struct atm_pcb Atm_pcb; + +/* + * Protocol Types + */ +#define ATPT_AAL5 0 /* AAL type 5 protocol */ +#define ATPT_SSCOP 1 /* SSCOP protocol */ +#define ATPT_NUM 2 /* Number of protocols */ + +/* + * PCB Flags + */ + + +/* + * Handy macros + */ +#define sotoatmpcb(so) ((Atm_pcb *)(so)->so_pcb) + + +/* + * ATM Socket Statistics + */ +struct atm_sock_stat { + u_long as_connreq[ATPT_NUM]; /* Connection requests */ + u_long as_inconn[ATPT_NUM]; /* Incoming connection requests */ + u_long as_conncomp[ATPT_NUM]; /* Connections completed */ + u_long as_connfail[ATPT_NUM]; /* Connections failed */ + u_long as_connrel[ATPT_NUM]; /* Connections released */ + u_long as_connclr[ATPT_NUM]; /* Connections cleared */ + u_long as_indrop[ATPT_NUM]; /* Input packets dropped */ + u_long as_outdrop[ATPT_NUM]; /* Output packets dropped */ +}; +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_PCB_H */ diff --git a/sys/netatm/atm_proto.c b/sys/netatm/atm_proto.c new file mode 100644 index 0000000..ddd1a88 --- /dev/null +++ b/sys/netatm/atm_proto.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: atm_proto.c,v 1.6 1998/02/19 19:52:06 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM socket protocol family support definitions + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_proto.c,v 1.6 1998/02/19 19:52:06 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +struct protosw atmsw[] = { +{ SOCK_DGRAM, /* ioctl()-only */ + &atmdomain, + 0, + 0, + 0, /* pr_input */ + 0, /* pr_output */ + 0, /* pr_ctlinput */ + 0, /* pr_ctloutput */ + 0, /* pr_ousrreq */ + 0, /* pr_init */ + 0, /* pr_fasttimo */ + 0, /* pr_slowtimo */ + 0, /* pr_drain */ + &atm_dgram_usrreqs, /* pr_usrreqs */ +}, + +{ SOCK_SEQPACKET, /* AAL-5 */ + &atmdomain, + ATM_PROTO_AAL5, + PR_ATOMIC|PR_CONNREQUIRED, + 0, /* pr_input */ + 0, /* pr_output */ + 0, /* pr_ctlinput */ + atm_aal5_ctloutput, /* pr_ctloutput */ + 0, /* pr_ousrreq */ + 0, /* pr_init */ + 0, /* pr_fasttimo */ + 0, /* pr_slowtimo */ + 0, /* pr_drain */ + &atm_aal5_usrreqs, /* pr_usrreqs */ +}, + +#ifdef XXX +{ SOCK_SEQPACKET, /* SSCOP */ + &atmdomain, + ATM_PROTO_SSCOP, + PR_ATOMIC|PR_CONNREQUIRED|PR_WANTRCVD, + x, /* pr_input */ + x, /* pr_output */ + x, /* pr_ctlinput */ + x, /* pr_ctloutput */ + 0, /* pr_ousrreq */ + 0, /* pr_init */ + 0, /* pr_fasttimo */ + 0, /* pr_slowtimo */ + x, /* pr_drain */ + x, /* pr_usrreqs */ +}, +#endif +}; + +struct domain atmdomain = { + AF_ATM, + "atm", +#if defined(__FreeBSD__) + atm_initialize, +#else + 0, +#endif + 0, + 0, + atmsw, + &atmsw[sizeof(atmsw) / sizeof(atmsw[0])] +}; + +#ifdef __FreeBSD__ +DOMAIN_SET(atm); +#endif + + +#if (defined(__FreeBSD__) && (BSD >= 199506)) +/* + * Protocol request not supported + * + * Arguments: + * so pointer to socket + * + * Returns: + * errno error - operation not supported + * + */ +int +atm_proto_notsupp1(so) + struct socket *so; +{ + return (EOPNOTSUPP); +} + + +/* + * Protocol request not supported + * + * Arguments: + * so pointer to socket + * addr pointer to protocol address + * p pointer to process + * + * Returns: + * errno error - operation not supported + * + */ +int +atm_proto_notsupp2(so, addr, p) + struct socket *so; + struct sockaddr *addr; + struct proc *p; +{ + return (EOPNOTSUPP); +} + + +/* + * Protocol request not supported + * + * Arguments: + * so pointer to socket + * addr pointer to pointer to protocol address + * + * Returns: + * errno error - operation not supported + * + */ +int +atm_proto_notsupp3(so, addr) + struct socket *so; + struct sockaddr **addr; +{ + return (EOPNOTSUPP); +} + + +/* + * Protocol request not supported + * + * Arguments: + * so pointer to socket + * i integer + * m pointer to kernel buffer + * addr pointer to protocol address + * m2 pointer to kernel buffer + * p pointer to process + * + * Returns: + * errno error - operation not supported + * + */ +int +atm_proto_notsupp4(so, i, m, addr, m2, p) + struct socket *so; + int i; + KBuffer *m; + struct sockaddr *addr; + KBuffer *m2; + struct proc *p; +{ + return (EOPNOTSUPP); +} + +#endif /* (defined(__FreeBSD__) && (BSD >= 199506)) */ + diff --git a/sys/netatm/atm_sap.h b/sys/netatm/atm_sap.h new file mode 100644 index 0000000..1386516 --- /dev/null +++ b/sys/netatm/atm_sap.h @@ -0,0 +1,81 @@ +/* + * + * =================================== + * 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: atm_sap.h,v 1.3 1998/02/19 19:59:38 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Services definitions + * + */ + +#ifndef _NETATM_ATM_SAP_H +#define _NETATM_ATM_SAP_H + +/* + * Service Access Point (SAP) + * + * A SAP specifies the definition of an interface used between two adjacent + * layers. The SAP is named for the services the lower layer provides to + * the upper layer. + * + * The types of SAPs used are: + * Stack - defines the interfaces between stack service entities. + * These are further divided into: + * + * Stack class SAP - which identifies the type of interface + * used. All SAPs of a particular class will provide + * the same interface services to the higher layer. + * All stack command codes are constructed using class + * SAP values. + * + * Stack instance SAP - which identifies the specific identity + * of the layer providing the class interface. + */ +typedef u_short Sap_t; + +#define SAP_TYPE_MASK 0xc000 +#define SAP_TYPE_STACK 0x8000 +#define SAP_CLASS_MASK 0xff80 + +#define SAP_STACK(c, i) (SAP_TYPE_STACK | ((c) << 7) | (i)) + +/* Stack SAPs */ +#define SAP_ATM SAP_STACK(1, 0) /* ATM cell */ +#define SAP_SAR SAP_STACK(2, 0) /* AAL SAR */ +#define SAP_SAR_AAL3_4 SAP_STACK(2, 3) /* AAL3/4 SAR */ +#define SAP_SAR_AAL5 SAP_STACK(2, 5) /* AAL5 SAR */ +#define SAP_CPCS SAP_STACK(3, 0) /* AAL CPCS */ +#define SAP_CPCS_AAL3_4 SAP_STACK(3, 3) /* AAL3/4 CPCS */ +#define SAP_CPCS_AAL5 SAP_STACK(3, 5) /* AAL5 CPCS */ +#define SAP_SSCOP SAP_STACK(4, 0) /* ITU Q.2110 */ +#define SAP_SSCF_UNI SAP_STACK(5, 0) /* ITU Q.2130 */ +#define SAP_SSCF_NNI SAP_STACK(6, 0) /* ITU Q.2140 */ + +#endif /* _NETATM_ATM_SAP_H */ diff --git a/sys/netatm/atm_sigmgr.h b/sys/netatm/atm_sigmgr.h new file mode 100644 index 0000000..1b9388e --- /dev/null +++ b/sys/netatm/atm_sigmgr.h @@ -0,0 +1,109 @@ +/* + * + * =================================== + * 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: atm_sigmgr.h,v 1.7 1998/03/24 20:43:59 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Signalling Manager definitions + * + */ + +#ifndef _NETATM_ATM_SIGMGR_H +#define _NETATM_ATM_SIGMGR_H + +#ifdef ATM_KERNEL +/* + * Structure common to all ATM Signalling Managers. Each Signalling + * Manager must create one of these and use it to register itself + * with the system. + */ +struct sigmgr { + struct sigmgr *sm_next; /* Next registered sigmgr */ + u_char sm_proto; /* Signalling protocol (see below) */ + struct siginst *sm_prinst; /* List of protocol instances */ +/* Exported functions */ + int (*sm_attach) /* Attach interface */ + __P((struct sigmgr *, struct atm_pif *)); + int (*sm_detach) /* Detach interface */ + __P((struct atm_pif *)); + int (*sm_setup) /* Connection setup */ + __P((Atm_connvc *, int *)); + int (*sm_accept) /* Call accepted */ + __P((struct vccb *, int *)); + int (*sm_reject) /* Call rejected */ + __P((struct vccb *, int *)); + int (*sm_release) /* Connection release */ + __P((struct vccb *, int *)); + int (*sm_free) /* Free connection resources */ + __P((struct vccb *)); + int (*sm_ioctl) /* Ioctl handler */ + __P((int, caddr_t, caddr_t)); +}; +#endif /* ATM_KERNEL */ + +/* + * ATM Signalling Protocols + */ +#define ATM_SIG_PVC 1 /* PVC-only */ +#define ATM_SIG_SPANS 2 /* Fore Systems SPANS */ +#define ATM_SIG_UNI30 3 /* ATM Forum UNI 3.0 */ +#define ATM_SIG_UNI31 4 /* ATM Forum UNI 3.1 */ +#define ATM_SIG_UNI40 5 /* ATM Forum UNI 4.0 */ + + +#ifdef ATM_KERNEL +/* + * Signalling Protocol Instance control block header. Common header for + * every signalling protocol instance control block. + */ +struct siginst { + struct siginst *si_next; /* Next sigmgr protocol instance */ + struct atm_pif *si_pif; /* Device interface */ + Atm_addr si_addr; /* Interface ATM address */ + Atm_addr si_subaddr; /* Interface ATM subaddress */ + Queue_t si_vccq; /* VCCB queue */ + u_short si_state; /* Protocol state (sigmgr specific) */ + +/* Exported protocol services */ + struct ip_serv *si_ipserv; /* IP/ATM services */ +}; + + +/* + * Sigmgr function return codes + */ +#define CALL_PROCEEDING 1 /* Connection request is in progress */ +#define CALL_FAILED 2 /* Connection request failed */ +#define CALL_CONNECTED 3 /* Connection setup successful */ +#define CALL_CLEARED 4 /* Connection has been terminated */ + +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_SIGMGR_H */ diff --git a/sys/netatm/atm_signal.c b/sys/netatm/atm_signal.c new file mode 100644 index 0000000..8acbf7a --- /dev/null +++ b/sys/netatm/atm_signal.c @@ -0,0 +1,512 @@ +/* + * + * =================================== + * 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: atm_signal.c,v 1.8 1998/03/24 20:45:37 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * General ATM signalling management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_signal.c,v 1.8 1998/03/24 20:45:37 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Local variables + */ +static struct sigmgr *atm_sigmgr_head = NULL; +static struct stack_defn *atm_stack_head = NULL; + + +/* + * Register a new Signalling Manager + * + * Each Signalling Manager must register itself here upon completing + * its internal initialization. This applies to both linked and loaded + * managers. + * + * Arguments: + * smp pointer to Signalling Manager description + * + * Returns: + * 0 registration was successful + * errno registration failed - reason indicated + * + */ +int +atm_sigmgr_register(smp) + struct sigmgr *smp; +{ + struct sigmgr *smp2; + int s = splnet(); + + /* + * See if we need to be initialized + */ + if (!atm_init) + atm_initialize(); + + /* + * Make sure there's only one instance of each protocol + */ + for (smp2 = atm_sigmgr_head; smp2 != NULL; smp2 = smp2->sm_next) { + if (smp->sm_proto == smp2->sm_proto) { + (void) splx(s); + return (EEXIST); + } + } + + /* + * Looks okay, link it in + */ + LINK2TAIL(smp, struct sigmgr, atm_sigmgr_head, sm_next); + + (void) splx(s); + return (0); +} + + +/* + * De-register a Signalling Manager + * + * Each Signalling Manager must de-register (is this really a word?) + * itself before removing itself from the system. This really only + * applies to managers about to be modunload'ed. It is the signal + * manager's responsibility to ensure that all its protocol instances + * have been successfully terminated before de-registering itself. + * + * Arguments: + * smp pointer to Signalling Manager description + * + * Returns: + * 0 deregistration was successful + * errno deregistration failed - reason indicated + * + */ +int +atm_sigmgr_deregister(smp) + struct sigmgr *smp; +{ + int found, s = splnet(); + + /* + * Unlink descriptor + */ + UNLINKF(smp, struct sigmgr, atm_sigmgr_head, sm_next, found); + + (void) splx(s); + + if (!found) + return (ENOENT); + + return (0); +} + + +/* + * Attach a Signalling Manager to an ATM physical interface + * + * Each ATM physical interface must have a signalling manager attached to + * itself for the signalling protocol to be run across this interface. The + * interface must be registered and completely initialized before the attach, + * since the signalling manager may initiate virtual circuit activity as part + * its response to this call. + * + * Called at splnet. + * + * Arguments: + * pip pointer to atm physical interface control block + * proto requested signalling protocol + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +int +atm_sigmgr_attach(pip, proto) + struct atm_pif *pip; + u_char proto; +{ + struct atm_pif *tp; + struct sigmgr *smp; + int err; + + /* + * Make sure interface is registered + */ + for (tp = atm_interface_head; tp != NULL; tp = tp->pif_next) { + if (tp == pip) + break; + } + if (tp == NULL) { + return (ENOENT); + } + + /* + * Make sure no signalling manager is already attached + */ + if (pip->pif_sigmgr != NULL) { + return (EEXIST); + } + + /* + * Must have at least one network interface defined + */ + if (pip->pif_nif == NULL) + return (ETOOMANYREFS); + + /* + * Find requested protocol + */ + for (smp = atm_sigmgr_head; smp != NULL; smp = smp->sm_next) { + if (smp->sm_proto == proto) + break; + } + if (smp == NULL) { + return (EPROTONOSUPPORT); + } + + /* + * Tell the signal manager about it + */ + err = (*smp->sm_attach)(smp, pip); + + /* + * Tell all registered convergence modules about this + */ + if (!err) { + struct atm_nif *nip; + struct atm_ncm *ncp; + + for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) { + for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) { + if (err = (*ncp->ncm_stat) + (NCM_SIGATTACH, nip, 0)) + break; + } + if (err) + break; + } + + if (err) { + /* + * Someone's unhappy, so back all this out + */ + (void) atm_sigmgr_detach(pip); + } + } + + return (err); +} + + +/* + * Detach an ATM physical interface from a Signalling Manager + * + * The ATM interface must be detached from the signalling manager + * before the interface can be de-registered. + * + * Called at splnet. + * + * Arguments: + * pip pointer to atm physical interface control block + * + * Returns: + * 0 detach successful + * errno detach failed - reason indicated + * + */ +int +atm_sigmgr_detach(pip) + struct atm_pif *pip; +{ + struct atm_pif *tp; + struct atm_nif *nip; + struct atm_ncm *ncp; + int err; + + + /* + * Make sure interface is registered + */ + for (tp = atm_interface_head; tp != NULL; tp = tp->pif_next) { + if (tp == pip) + break; + } + if (tp == NULL) { + return (ENOENT); + } + + /* + * Make sure a signalling manager is attached + */ + if (pip->pif_sigmgr == NULL) { + return (ENOENT); + } + + /* + * Tell all registered convergence modules about this + */ + for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) { + for (ncp = atm_netconv_head; ncp; ncp = ncp->ncm_next) { + (void) (*ncp->ncm_stat)(NCM_SIGDETACH, nip, 0); + } + } + + /* + * Tell the signal manager about it + * + * NOTE: + * The only reason this should ever fail is if things are really + * hosed up somewhere, in which case doing a bunch of NCM_SIGATTACH's + * here just doesn't seem to help much. + */ + err = (*pip->pif_sigmgr->sm_detach)(pip); + + return (err); +} + + +/* + * Register an ATM Stack Service + * + * Each ATM stack service provider must register its provided service(s) here. + * Each service must be registered separately. Service providers include + * both loaded and linked kernel modules. Device driver services are NOT + * registered here - their service registry is performed implicitly through + * the device interface structure stack services list (pif_services). + * + * Arguments: + * sdp pointer to stack service definition block + * + * Returns: + * 0 registration successful + * errno registration failed - reason indicated + * + */ +int +atm_stack_register(sdp) + struct stack_defn *sdp; +{ + struct stack_defn *tdp; + int s = splnet(); + + /* + * See if we need to be initialized + */ + if (!atm_init) + atm_initialize(); + + /* + * Ensure no duplicates + */ + for (tdp = atm_stack_head; tdp != NULL; tdp = tdp->sd_next) { + if (tdp->sd_sap == sdp->sd_sap) + break; + } + if (tdp != NULL) { + (void) splx(s); + return (EEXIST); + } + + /* + * Add stack to list + */ + LINK2TAIL(sdp, struct stack_defn, atm_stack_head, sd_next); + + (void) splx(s); + return (0); +} + + +/* + * De-register an ATM Stack Service + * + * Each ATM stack service provider must de-register its registered service(s) + * before terminating the service. Specifically, loaded kernel modules + * must de-register their services before unloading themselves. + * + * Arguments: + * sdp pointer to stack service definition block + * + * Returns: + * 0 de-registration successful + * errno de-registration failed - reason indicated + * + */ +int +atm_stack_deregister(sdp) + struct stack_defn *sdp; +{ + int found, s = splnet(); + + /* + * Remove service from list + */ + UNLINKF(sdp, struct stack_defn, atm_stack_head, sd_next, found); + (void) splx(s); + + if (!found) + return (ENOENT); + + return (0); +} + + +/* + * Create and Instantiate a Stack + * + * For the requested stack list, locate the stack service definitions + * necessary to build the stack to implement the listed services. + * The stack service definitions provided by the interface device-driver + * are always preferred, since they are (hopefully) done with + * hardware assistance from the interface card. + * + * After the stack has been built, the selected services are called to + * notify them of the new stack instantiation. Each service should then + * allocate all the resources it requires for this new stack instance. + * The service should then wait for subsequent protocol notification + * via its stack command handlers. + * + * Must be called at splnet. + * + * Arguments: + * cvp pointer to connection vcc block for the created stack + * tlp pointer to stack list + * upf top-of-stack CM upper command handler + * + * Returns: + * 0 stack successfully created + * errno failed - reason indicated + * + */ +int +atm_create_stack(cvp, tlp, upf) + Atm_connvc *cvp; + struct stack_list *tlp; + void (*upf)__P((int, void *, int, int)); +{ + struct stack_defn *sdp, usd; + struct stack_inst svs; + struct atm_pif *pip = cvp->cvc_attr.nif->nif_pif; + int i, err; + + + /* + * Initialize stack (element 0 is for owner's services) + */ + svs.si_srvc[1] = sdp = NULL; + + /* + * Locate service provider for each service in the + * stack list. We prefer interface driver providers + * over kernel module providers. + */ + for (i = 0; i < STACK_CNT; i++) { + Sap_t sap; + + /* Stack list is 0-terminated */ + if ((sap = tlp->sl_sap[i]) == 0) + break; + + /* + * Search interface's services + */ + for (sdp = pip->pif_services; sdp; sdp = sdp->sd_next) + if (sdp->sd_sap == sap) + break; + if (sdp == NULL) { + + /* + * Search kernel services + */ + for (sdp = atm_stack_head; sdp; + sdp = sdp->sd_next) + if (sdp->sd_sap == sap) + break; + } + if (sdp == NULL) { + + /* + * Requested service id not found + */ + return (ENOENT); + } + + /* + * Save stack definition for this service + */ + svs.si_srvc[i+1] = sdp; + + /* + * Quit loop if this service is terminal, ie. if + * it takes care of the rest of the stack. + */ + if (sdp->sd_flag & SDF_TERM) + break; + } + + /* + * Ensure stack instance array is located and terminated + */ + if ((svs.si_srvc[1] == NULL) || !(sdp->sd_flag & SDF_TERM)) { + return (ENOENT); + } + + /* + * Setup owner service definition + */ + KM_ZERO((caddr_t)&usd, sizeof(struct stack_defn)); + usd.sd_upper = upf; + usd.sd_toku = cvp; + svs.si_srvc[0] = &usd; + + /* + * Instantiate the stack + */ + err = (*svs.si_srvc[1]->sd_inst)(&svs.si_srvc[0], cvp); + if (err) { + return (err); + } + + /* + * Save top 'o stack info + */ + cvp->cvc_lower = svs.si_srvc[1]->sd_lower; + cvp->cvc_tokl = svs.si_srvc[1]->sd_toku; + + return (0); +} + diff --git a/sys/netatm/atm_socket.c b/sys/netatm/atm_socket.c new file mode 100644 index 0000000..2370a15 --- /dev/null +++ b/sys/netatm/atm_socket.c @@ -0,0 +1,1311 @@ +/* + * + * =================================== + * 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: atm_socket.c,v 1.3 1998/07/30 22:30:53 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM common socket protocol processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_socket.c,v 1.3 1998/07/30 22:30:53 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Local functions + */ + + +/* + * Local variables + */ +static struct sp_info atm_pcb_pool = { + "atm pcb pool", /* si_name */ + sizeof(Atm_pcb), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + +static struct t_atm_cause atm_sock_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_UNSPECIFIED_NORMAL, + {0, 0, 0, 0} +}; + + +/* + * Allocate resources for a new ATM socket + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * send socket send buffer maximum + * recv socket receive buffer maximum + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +int +atm_sock_attach(so, send, recv) + struct socket *so; + u_long send; + u_long recv; +{ + Atm_pcb *atp = sotoatmpcb(so); + int err; + + /* + * Make sure initialization has happened + */ + if (!atm_init) + atm_initialize(); + + /* + * Make sure we're not already attached + */ + if (atp) + return (EISCONN); + + /* + * Reserve socket buffer space, if not already done + */ + if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) { + err = soreserve(so, send, recv); + if (err) + return (err); + } + + /* + * Allocate and initialize our control block + */ + atp = (Atm_pcb *)atm_allocate(&atm_pcb_pool); + if (atp == NULL) + return (ENOMEM); + + atp->atp_socket = so; + so->so_pcb = (caddr_t)atp; + return (0); +} + + +/* + * Detach from socket and free resources + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * + * Returns: + * 0 detach successful + * errno detach failed - reason indicated + * + */ +int +atm_sock_detach(so) + struct socket *so; +{ + Atm_pcb *atp = sotoatmpcb(so); + + /* + * Make sure we're still attached + */ + if (atp == NULL) + return (ENOTCONN); + + /* + * Terminate any (possibly pending) connection + */ + if (atp->atp_conn) { + (void) atm_sock_disconnect(so); + } + + /* + * Break links and free control blocks + */ + so->so_pcb = NULL; + sofree(so); + + atm_free((caddr_t)atp); + + return (0); +} + + +/* + * Bind local address to socket + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * addr pointer to protocol address + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_bind(so, addr) + struct socket *so; + struct sockaddr *addr; +{ + Atm_pcb *atp = sotoatmpcb(so); + Atm_attributes attr; + struct sockaddr_atm *satm; + struct t_atm_sap_addr *sapadr; + struct t_atm_sap_layer2 *sapl2; + struct t_atm_sap_layer3 *sapl3; + struct t_atm_sap_appl *sapapl; + + /* + * Make sure we're still attached + */ + if (atp == NULL) + return (ENOTCONN); + + /* + * Can't change local address once we've started connection process + */ + if (atp->atp_conn != NULL) + return (EADDRNOTAVAIL); + + /* + * Validate requested local address + */ + satm = (struct sockaddr_atm *)addr; + if (satm->satm_family != AF_ATM) + return (EAFNOSUPPORT); + + sapadr = &satm->satm_addr.t_atm_sap_addr; + if (sapadr->SVE_tag_addr == T_ATM_PRESENT) { + if (sapadr->address_format == T_ATM_ENDSYS_ADDR) { + if (sapadr->SVE_tag_selector != T_ATM_PRESENT) + return (EINVAL); + } else if (sapadr->address_format == T_ATM_E164_ADDR) { + if (sapadr->SVE_tag_selector != T_ATM_ABSENT) + return (EINVAL); + } else + return (EINVAL); + } else if ((sapadr->SVE_tag_addr != T_ATM_ABSENT) && + (sapadr->SVE_tag_addr != T_ATM_ANY)) + return (EINVAL); + if (sapadr->address_length > ATM_ADDR_LEN) + return (EINVAL); + + sapl2 = &satm->satm_addr.t_atm_sap_layer2; + if (sapl2->SVE_tag == T_ATM_PRESENT) { + if ((sapl2->ID_type != T_ATM_SIMPLE_ID) && + (sapl2->ID_type != T_ATM_USER_ID)) + return (EINVAL); + } else if ((sapl2->SVE_tag != T_ATM_ABSENT) && + (sapl2->SVE_tag != T_ATM_ANY)) + return (EINVAL); + + sapl3 = &satm->satm_addr.t_atm_sap_layer3; + if (sapl3->SVE_tag == T_ATM_PRESENT) { + if ((sapl3->ID_type != T_ATM_SIMPLE_ID) && + (sapl3->ID_type != T_ATM_IPI_ID) && + (sapl3->ID_type != T_ATM_SNAP_ID) && + (sapl3->ID_type != T_ATM_USER_ID)) + return (EINVAL); + } else if ((sapl3->SVE_tag != T_ATM_ABSENT) && + (sapl3->SVE_tag != T_ATM_ANY)) + return (EINVAL); + + sapapl = &satm->satm_addr.t_atm_sap_appl; + if (sapapl->SVE_tag == T_ATM_PRESENT) { + if ((sapapl->ID_type != T_ATM_ISO_APP_ID) && + (sapapl->ID_type != T_ATM_USER_APP_ID) && + (sapapl->ID_type != T_ATM_VENDOR_APP_ID)) + return (EINVAL); + } else if ((sapapl->SVE_tag != T_ATM_ABSENT) && + (sapapl->SVE_tag != T_ATM_ANY)) + return (EINVAL); + + /* + * Create temporary attributes list so that we can check out the + * new bind parameters before we modify the socket's values; + */ + attr = atp->atp_attr; + attr.called.tag = sapadr->SVE_tag_addr; + KM_COPY(&sapadr->address_format, &attr.called.addr, sizeof(Atm_addr)); + + attr.blli.tag_l2 = sapl2->SVE_tag; + if (sapl2->SVE_tag == T_ATM_PRESENT) { + attr.blli.v.layer_2_protocol.ID_type = sapl2->ID_type; + KM_COPY(&sapl2->ID, &attr.blli.v.layer_2_protocol.ID, + sizeof(attr.blli.v.layer_2_protocol.ID)); + } + + attr.blli.tag_l3 = sapl3->SVE_tag; + if (sapl3->SVE_tag == T_ATM_PRESENT) { + attr.blli.v.layer_3_protocol.ID_type = sapl3->ID_type; + KM_COPY(&sapl3->ID, &attr.blli.v.layer_3_protocol.ID, + sizeof(attr.blli.v.layer_3_protocol.ID)); + } + + attr.bhli.tag = sapapl->SVE_tag; + if (sapapl->SVE_tag == T_ATM_PRESENT) { + attr.bhli.v.ID_type = sapapl->ID_type; + KM_COPY(&sapapl->ID, &attr.bhli.v.ID, + sizeof(attr.bhli.v.ID)); + } + + /* + * Make sure we have unique listening attributes + */ + if (atm_cm_match(&attr, NULL) != NULL) + return (EADDRINUSE); + + /* + * Looks good, save new attributes + */ + atp->atp_attr = attr; + + return (0); +} + + +/* + * Listen for incoming connections + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * epp pointer to endpoint definition structure + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_listen(so, epp) + struct socket *so; + Atm_endpoint *epp; +{ + Atm_pcb *atp = sotoatmpcb(so); + + /* + * Make sure we're still attached + */ + if (atp == NULL) + return (ENOTCONN); + + /* + * Start listening for incoming calls + */ + return (atm_cm_listen(epp, atp, &atp->atp_attr, &atp->atp_conn)); +} + + +/* + * Connect socket to peer + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * addr pointer to protocol address + * epp pointer to endpoint definition structure + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_connect(so, addr, epp) + struct socket *so; + struct sockaddr *addr; + Atm_endpoint *epp; +{ + Atm_pcb *atp = sotoatmpcb(so); + struct sockaddr_atm *satm; + struct t_atm_sap_addr *sapadr; + struct t_atm_sap_layer2 *sapl2; + struct t_atm_sap_layer3 *sapl3; + struct t_atm_sap_appl *sapapl; + int err; + + /* + * Make sure we're still attached + */ + if (atp == NULL) + return (ENOTCONN); + + /* + * Validate requested peer address + */ + satm = (struct sockaddr_atm *)addr; + if (satm->satm_family != AF_ATM) + return (EAFNOSUPPORT); + + sapadr = &satm->satm_addr.t_atm_sap_addr; + if (sapadr->SVE_tag_addr != T_ATM_PRESENT) + return (EINVAL); + if (sapadr->address_format == T_ATM_ENDSYS_ADDR) { + if (sapadr->SVE_tag_selector != T_ATM_PRESENT) + return (EINVAL); + } else if (sapadr->address_format == T_ATM_E164_ADDR) { + if (sapadr->SVE_tag_selector != T_ATM_ABSENT) + return (EINVAL); + } else if (sapadr->address_format == T_ATM_PVC_ADDR) { + if (sapadr->SVE_tag_selector != T_ATM_ABSENT) + return (EINVAL); + } else + return (EINVAL); + if (sapadr->address_length > ATM_ADDR_LEN) + return (EINVAL); + + sapl2 = &satm->satm_addr.t_atm_sap_layer2; + if (sapl2->SVE_tag == T_ATM_PRESENT) { + if ((sapl2->ID_type != T_ATM_SIMPLE_ID) && + (sapl2->ID_type != T_ATM_USER_ID)) + return (EINVAL); + } else if (sapl2->SVE_tag != T_ATM_ABSENT) + return (EINVAL); + + sapl3 = &satm->satm_addr.t_atm_sap_layer3; + if (sapl3->SVE_tag == T_ATM_PRESENT) { + if ((sapl3->ID_type != T_ATM_SIMPLE_ID) && + (sapl3->ID_type != T_ATM_IPI_ID) && + (sapl3->ID_type != T_ATM_SNAP_ID) && + (sapl3->ID_type != T_ATM_USER_ID)) + return (EINVAL); + } else if (sapl3->SVE_tag != T_ATM_ABSENT) + return (EINVAL); + + sapapl = &satm->satm_addr.t_atm_sap_appl; + if (sapapl->SVE_tag == T_ATM_PRESENT) { + if ((sapapl->ID_type != T_ATM_ISO_APP_ID) && + (sapapl->ID_type != T_ATM_USER_APP_ID) && + (sapapl->ID_type != T_ATM_VENDOR_APP_ID)) + return (EINVAL); + } else if (sapapl->SVE_tag != T_ATM_ABSENT) + return (EINVAL); + + /* + * Select an outgoing network interface + */ + if (atp->atp_attr.nif == NULL) { + struct atm_pif *pip; + + for (pip = atm_interface_head; pip != NULL; + pip = pip->pif_next) { + if (pip->pif_nif != NULL) { + atp->atp_attr.nif = pip->pif_nif; + break; + } + } + if (atp->atp_attr.nif == NULL) + return (ENXIO); + } + + /* + * Set supplied connection attributes + */ + atp->atp_attr.called.tag = T_ATM_PRESENT; + KM_COPY(&sapadr->address_format, &atp->atp_attr.called.addr, + sizeof(Atm_addr)); + + atp->atp_attr.blli.tag_l2 = sapl2->SVE_tag; + if (sapl2->SVE_tag == T_ATM_PRESENT) { + atp->atp_attr.blli.v.layer_2_protocol.ID_type = sapl2->ID_type; + KM_COPY(&sapl2->ID, &atp->atp_attr.blli.v.layer_2_protocol.ID, + sizeof(atp->atp_attr.blli.v.layer_2_protocol.ID)); + } + + atp->atp_attr.blli.tag_l3 = sapl3->SVE_tag; + if (sapl3->SVE_tag == T_ATM_PRESENT) { + atp->atp_attr.blli.v.layer_3_protocol.ID_type = sapl3->ID_type; + KM_COPY(&sapl3->ID, &atp->atp_attr.blli.v.layer_3_protocol.ID, + sizeof(atp->atp_attr.blli.v.layer_3_protocol.ID)); + } + + atp->atp_attr.bhli.tag = sapapl->SVE_tag; + if (sapapl->SVE_tag == T_ATM_PRESENT) { + atp->atp_attr.bhli.v.ID_type = sapapl->ID_type; + KM_COPY(&sapapl->ID, &atp->atp_attr.bhli.v.ID, + sizeof(atp->atp_attr.bhli.v.ID)); + } + + /* + * We're finally ready to initiate the ATM connection + */ + soisconnecting(so); + atm_sock_stat.as_connreq[atp->atp_type]++; + err = atm_cm_connect(epp, atp, &atp->atp_attr, &atp->atp_conn); + if (err == 0) { + /* + * Connection is setup + */ + atm_sock_stat.as_conncomp[atp->atp_type]++; + soisconnected(so); + + } else if (err == EINPROGRESS) { + /* + * We've got to wait for a connected event + */ + err = 0; + + } else { + /* + * Call failed... + */ + atm_sock_stat.as_connfail[atp->atp_type]++; + soisdisconnected(so); + } + + return (err); +} + + +/* + * Disconnect connected socket + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_disconnect(so) + struct socket *so; +{ + Atm_pcb *atp = sotoatmpcb(so); + struct t_atm_cause *cause; + int err; + + /* + * Make sure we're still attached + */ + if (atp == NULL) + return (ENOTCONN); + + /* + * Release the ATM connection + */ + if (atp->atp_conn) { + if (atp->atp_attr.cause.tag == T_ATM_PRESENT) + cause = &atp->atp_attr.cause.v; + else + cause = &atm_sock_cause; + err = atm_cm_release(atp->atp_conn, cause); + if (err) + log(LOG_ERR, "atm_sock_disconnect: release fail (%d)\n", + err); + atm_sock_stat.as_connrel[atp->atp_type]++; + atp->atp_conn = NULL; + } + + soisdisconnected(so); + + return (0); +} + + +/* + * Retrieve local socket address + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * addr pointer to pointer to contain protocol address + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_sockaddr(so, addr) + struct socket *so; + struct sockaddr **addr; +{ + struct sockaddr_atm *satm; + struct t_atm_sap_addr *saddr; + Atm_pcb *atp = sotoatmpcb(so); + + /* + * Return local interface address, if known + */ + satm = KM_ALLOC(sizeof *satm, M_SONAME, M_WAITOK); + if (satm == NULL) + return (ENOMEM); + + KM_ZERO(satm, sizeof(*satm)); + satm->satm_family = AF_ATM; +#if (defined(BSD) && (BSD >= 199103)) + satm->satm_len = sizeof(*satm); +#endif + + saddr = &satm->satm_addr.t_atm_sap_addr; + if (atp->atp_attr.nif && atp->atp_attr.nif->nif_pif->pif_siginst) { + saddr->SVE_tag_addr = T_ATM_PRESENT; + ATM_ADDR_SEL_COPY( + &atp->atp_attr.nif->nif_pif->pif_siginst->si_addr, + atp->atp_attr.nif->nif_sel, saddr); + if (saddr->address_format == T_ATM_ENDSYS_ADDR) + saddr->SVE_tag_selector = T_ATM_PRESENT; + else + saddr->SVE_tag_selector = T_ATM_ABSENT; + } else { + saddr->SVE_tag_addr = T_ATM_ABSENT; + saddr->SVE_tag_selector = T_ATM_ABSENT; + saddr->address_format = T_ATM_ABSENT; + } + satm->satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_ABSENT; + satm->satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT; + satm->satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT; + + *addr = (struct sockaddr *)satm; + return (0); +} + + +/* + * Retrieve peer socket address + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * addr pointer to pointer to contain protocol address + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_peeraddr(so, addr) + struct socket *so; + struct sockaddr **addr; +{ + struct sockaddr_atm *satm; + struct t_atm_sap_addr *saddr; + Atm_pcb *atp = sotoatmpcb(so); + Atm_connvc *cvp; + + /* + * Return remote address, if known + */ + satm = KM_ALLOC(sizeof *satm, M_SONAME, M_WAITOK); + if (satm == NULL) + return (ENOMEM); + + KM_ZERO(satm, sizeof(*satm)); + satm->satm_family = AF_ATM; +#if (defined(BSD) && (BSD >= 199103)) + satm->satm_len = sizeof(*satm); +#endif + + saddr = &satm->satm_addr.t_atm_sap_addr; + if (so->so_state & SS_ISCONNECTED) { + cvp = atp->atp_conn->co_connvc; + saddr->SVE_tag_addr = T_ATM_PRESENT; + if (cvp->cvc_flags & CVCF_CALLER) { + ATM_ADDR_COPY(&cvp->cvc_attr.called.addr, saddr); + } else { + if (cvp->cvc_attr.calling.tag == T_ATM_PRESENT) { + ATM_ADDR_COPY(&cvp->cvc_attr.calling.addr, + saddr); + } else { + saddr->SVE_tag_addr = T_ATM_ABSENT; + saddr->address_format = T_ATM_ABSENT; + } + } + if (saddr->address_format == T_ATM_ENDSYS_ADDR) + saddr->SVE_tag_selector = T_ATM_PRESENT; + else + saddr->SVE_tag_selector = T_ATM_ABSENT; + } else { + saddr->SVE_tag_addr = T_ATM_ABSENT; + saddr->SVE_tag_selector = T_ATM_ABSENT; + saddr->address_format = T_ATM_ABSENT; + } + satm->satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_ABSENT; + satm->satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT; + satm->satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT; + + *addr = (struct sockaddr *)satm; + return (0); +} + + +/* + * Common setsockopt processing + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * sopt pointer to socket option info + * atp pointer to ATM PCB + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_setopt(so, sopt, atp) + struct socket *so; + struct sockopt *sopt; + Atm_pcb *atp; +{ + int err = 0; + union { + struct t_atm_aal5 aal5; + struct t_atm_traffic trf; + struct t_atm_bearer brr; + struct t_atm_bhli bhl; + struct t_atm_blli bll; + Atm_addr addr; + struct t_atm_cause cau; + struct t_atm_qos qos; + struct t_atm_transit trn; + struct t_atm_net_intf nif; + struct t_atm_llc llc; + struct t_atm_app_name appn; + } p; + +#define MAXVAL(bits) ((1 << bits) - 1) +#define MAXMASK(bits) (~MAXVAL(bits)) + + switch (sopt->sopt_name) { + + case T_ATM_AAL5: + err = sooptcopyin(sopt, &p.aal5, sizeof p.aal5, sizeof p.aal5); + if (err) + break; + if ((p.aal5.forward_max_SDU_size != T_ATM_ABSENT) && + (p.aal5.forward_max_SDU_size & MAXMASK(16))) + return (EINVAL); + if ((p.aal5.backward_max_SDU_size != T_ATM_ABSENT) && + (p.aal5.backward_max_SDU_size & MAXMASK(16))) + return (EINVAL); + if ((p.aal5.SSCS_type != T_ATM_ABSENT) && + (p.aal5.SSCS_type != T_ATM_NULL) && + (p.aal5.SSCS_type != T_ATM_SSCS_SSCOP_REL) && + (p.aal5.SSCS_type != T_ATM_SSCS_SSCOP_UNREL) && + (p.aal5.SSCS_type != T_ATM_SSCS_FR)) + return (EINVAL); + + if ((p.aal5.forward_max_SDU_size == T_ATM_ABSENT) && + (p.aal5.backward_max_SDU_size == T_ATM_ABSENT) && + (p.aal5.SSCS_type == T_ATM_ABSENT)) + atp->atp_attr.aal.tag = T_ATM_ABSENT; + else { + atp->atp_attr.aal.tag = T_ATM_PRESENT; + atp->atp_attr.aal.type = ATM_AAL5; + atp->atp_attr.aal.v.aal5 = p.aal5; + } + break; + + case T_ATM_TRAFFIC: + err = sooptcopyin(sopt, &p.trf, sizeof p.trf, sizeof p.trf); + if (err) + break; + if ((p.trf.forward.PCR_high_priority != T_ATM_ABSENT) && + (p.trf.forward.PCR_high_priority & MAXMASK(24))) + return (EINVAL); + if (p.trf.forward.PCR_all_traffic & MAXMASK(24)) + return (EINVAL); + if ((p.trf.forward.SCR_high_priority != T_ATM_ABSENT) && + (p.trf.forward.SCR_high_priority & MAXMASK(24))) + return (EINVAL); + if ((p.trf.forward.SCR_all_traffic != T_ATM_ABSENT) && + (p.trf.forward.SCR_all_traffic & MAXMASK(24))) + return (EINVAL); + if ((p.trf.forward.MBS_high_priority != T_ATM_ABSENT) && + (p.trf.forward.MBS_high_priority & MAXMASK(24))) + return (EINVAL); + if ((p.trf.forward.MBS_all_traffic != T_ATM_ABSENT) && + (p.trf.forward.MBS_all_traffic & MAXMASK(24))) + return (EINVAL); + if ((p.trf.forward.tagging != T_YES) && + (p.trf.forward.tagging != T_NO)) + return (EINVAL); + + if ((p.trf.backward.PCR_high_priority != T_ATM_ABSENT) && + (p.trf.backward.PCR_high_priority & MAXMASK(24))) + return (EINVAL); + if (p.trf.backward.PCR_all_traffic & MAXMASK(24)) + return (EINVAL); + if ((p.trf.backward.SCR_high_priority != T_ATM_ABSENT) && + (p.trf.backward.SCR_high_priority & MAXMASK(24))) + return (EINVAL); + if ((p.trf.backward.SCR_all_traffic != T_ATM_ABSENT) && + (p.trf.backward.SCR_all_traffic & MAXMASK(24))) + return (EINVAL); + if ((p.trf.backward.MBS_high_priority != T_ATM_ABSENT) && + (p.trf.backward.MBS_high_priority & MAXMASK(24))) + return (EINVAL); + if ((p.trf.backward.MBS_all_traffic != T_ATM_ABSENT) && + (p.trf.backward.MBS_all_traffic & MAXMASK(24))) + return (EINVAL); + if ((p.trf.backward.tagging != T_YES) && + (p.trf.backward.tagging != T_NO)) + return (EINVAL); + if ((p.trf.best_effort != T_YES) && + (p.trf.best_effort != T_NO)) + return (EINVAL); + + atp->atp_attr.traffic.tag = T_ATM_PRESENT; + atp->atp_attr.traffic.v = p.trf; + break; + + case T_ATM_BEARER_CAP: + err = sooptcopyin(sopt, &p.brr, sizeof p.brr, sizeof p.brr); + if (err) + break; + if ((p.brr.bearer_class != T_ATM_CLASS_A) && + (p.brr.bearer_class != T_ATM_CLASS_C) && + (p.brr.bearer_class != T_ATM_CLASS_X)) + return (EINVAL); + if ((p.brr.traffic_type != T_ATM_NULL) && + (p.brr.traffic_type != T_ATM_CBR) && + (p.brr.traffic_type != T_ATM_VBR)) + return (EINVAL); + if ((p.brr.timing_requirements != T_ATM_NULL) && + (p.brr.timing_requirements != T_ATM_END_TO_END) && + (p.brr.timing_requirements != T_ATM_NO_END_TO_END)) + return (EINVAL); + if ((p.brr.clipping_susceptibility != T_NO) && + (p.brr.clipping_susceptibility != T_YES)) + return (EINVAL); + if ((p.brr.connection_configuration != T_ATM_1_TO_1) && + (p.brr.connection_configuration != T_ATM_1_TO_MANY)) + return (EINVAL); + + atp->atp_attr.bearer.tag = T_ATM_PRESENT; + atp->atp_attr.bearer.v = p.brr; + break; + + case T_ATM_BHLI: + err = sooptcopyin(sopt, &p.bhl, sizeof p.bhl, sizeof p.bhl); + if (err) + break; + if ((p.bhl.ID_type != T_ATM_ABSENT) && + (p.bhl.ID_type != T_ATM_ISO_APP_ID) && + (p.bhl.ID_type != T_ATM_USER_APP_ID) && + (p.bhl.ID_type != T_ATM_VENDOR_APP_ID)) + return (EINVAL); + + if (p.bhl.ID_type == T_ATM_ABSENT) + atp->atp_attr.bhli.tag = T_ATM_ABSENT; + else { + atp->atp_attr.bhli.tag = T_ATM_PRESENT; + atp->atp_attr.bhli.v = p.bhl; + } + break; + + case T_ATM_BLLI: + err = sooptcopyin(sopt, &p.bll, sizeof p.bll, sizeof p.bll); + if (err) + break; + if ((p.bll.layer_2_protocol.ID_type != T_ATM_ABSENT) && + (p.bll.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) && + (p.bll.layer_2_protocol.ID_type != T_ATM_USER_ID)) + return (EINVAL); + if ((p.bll.layer_2_protocol.mode != T_ATM_ABSENT) && + (p.bll.layer_2_protocol.mode != T_ATM_BLLI_NORMAL_MODE) && + (p.bll.layer_2_protocol.mode != T_ATM_BLLI_EXTENDED_MODE)) + return (EINVAL); + if ((p.bll.layer_2_protocol.window_size != T_ATM_ABSENT) && + (p.bll.layer_2_protocol.window_size < 1)) + return (EINVAL); + + if ((p.bll.layer_3_protocol.ID_type != T_ATM_ABSENT) && + (p.bll.layer_3_protocol.ID_type != T_ATM_SIMPLE_ID) && + (p.bll.layer_3_protocol.ID_type != T_ATM_IPI_ID) && + (p.bll.layer_3_protocol.ID_type != T_ATM_SNAP_ID) && + (p.bll.layer_3_protocol.ID_type != T_ATM_USER_ID)) + return (EINVAL); + if ((p.bll.layer_3_protocol.mode != T_ATM_ABSENT) && + (p.bll.layer_3_protocol.mode != T_ATM_BLLI_NORMAL_MODE) && + (p.bll.layer_3_protocol.mode != T_ATM_BLLI_EXTENDED_MODE)) + return (EINVAL); + if ((p.bll.layer_3_protocol.packet_size != T_ATM_ABSENT) && + (p.bll.layer_3_protocol.packet_size & MAXMASK(4))) + return (EINVAL); + if ((p.bll.layer_3_protocol.window_size != T_ATM_ABSENT) && + (p.bll.layer_3_protocol.window_size < 1)) + return (EINVAL); + + if (p.bll.layer_2_protocol.ID_type == T_ATM_ABSENT) + atp->atp_attr.blli.tag_l2 = T_ATM_ABSENT; + else + atp->atp_attr.blli.tag_l2 = T_ATM_PRESENT; + + if (p.bll.layer_3_protocol.ID_type == T_ATM_ABSENT) + atp->atp_attr.blli.tag_l3 = T_ATM_ABSENT; + else + atp->atp_attr.blli.tag_l3 = T_ATM_PRESENT; + + if ((atp->atp_attr.blli.tag_l2 == T_ATM_PRESENT) || + (atp->atp_attr.blli.tag_l3 == T_ATM_PRESENT)) + atp->atp_attr.blli.v = p.bll; + break; + + case T_ATM_DEST_ADDR: + err = sooptcopyin(sopt, &p.addr, sizeof p.addr, sizeof p.addr); + if (err) + break; + if ((p.addr.address_format != T_ATM_ENDSYS_ADDR) && + (p.addr.address_format != T_ATM_E164_ADDR)) + return (EINVAL); + if (p.addr.address_length > ATM_ADDR_LEN) + return (EINVAL); + + atp->atp_attr.called.tag = T_ATM_PRESENT; + atp->atp_attr.called.addr = p.addr; + break; + + case T_ATM_DEST_SUB: + err = sooptcopyin(sopt, &p.addr, sizeof p.addr, sizeof p.addr); + if (err) + break; + if ((p.addr.address_format != T_ATM_ABSENT) && + (p.addr.address_format != T_ATM_NSAP_ADDR)) + return (EINVAL); + if (p.addr.address_length > ATM_ADDR_LEN) + return (EINVAL); + + /* T_ATM_DEST_ADDR controls tag */ + atp->atp_attr.called.subaddr = p.addr; + break; + + case T_ATM_ORIG_ADDR: + return (EACCES); + + case T_ATM_ORIG_SUB: + return (EACCES); + + case T_ATM_CALLER_ID: + return (EACCES); + + case T_ATM_CAUSE: + err = sooptcopyin(sopt, &p.cau, sizeof p.cau, sizeof p.cau); + if (err) + break; + if ((p.cau.coding_standard != T_ATM_ABSENT) && + (p.cau.coding_standard != T_ATM_ITU_CODING) && + (p.cau.coding_standard != T_ATM_NETWORK_CODING)) + return (EINVAL); + if ((p.cau.location != T_ATM_LOC_USER) && + (p.cau.location != T_ATM_LOC_LOCAL_PRIVATE_NET) && + (p.cau.location != T_ATM_LOC_LOCAL_PUBLIC_NET) && + (p.cau.location != T_ATM_LOC_TRANSIT_NET) && + (p.cau.location != T_ATM_LOC_REMOTE_PUBLIC_NET) && + (p.cau.location != T_ATM_LOC_REMOTE_PRIVATE_NET) && + (p.cau.location != T_ATM_LOC_INTERNATIONAL_NET) && + (p.cau.location != T_ATM_LOC_BEYOND_INTERWORKING)) + return (EINVAL); + + if (p.cau.coding_standard == T_ATM_ABSENT) + atp->atp_attr.cause.tag = T_ATM_ABSENT; + else { + atp->atp_attr.cause.tag = T_ATM_PRESENT; + atp->atp_attr.cause.v = p.cau; + } + break; + + case T_ATM_QOS: + err = sooptcopyin(sopt, &p.qos, sizeof p.qos, sizeof p.qos); + if (err) + break; + if ((p.qos.coding_standard != T_ATM_ABSENT) && + (p.qos.coding_standard != T_ATM_ITU_CODING) && + (p.qos.coding_standard != T_ATM_NETWORK_CODING)) + return (EINVAL); + if ((p.qos.forward.qos_class != T_ATM_QOS_CLASS_0) && + (p.qos.forward.qos_class != T_ATM_QOS_CLASS_1) && + (p.qos.forward.qos_class != T_ATM_QOS_CLASS_2) && + (p.qos.forward.qos_class != T_ATM_QOS_CLASS_3) && + (p.qos.forward.qos_class != T_ATM_QOS_CLASS_4)) + return (EINVAL); + if ((p.qos.backward.qos_class != T_ATM_QOS_CLASS_0) && + (p.qos.backward.qos_class != T_ATM_QOS_CLASS_1) && + (p.qos.backward.qos_class != T_ATM_QOS_CLASS_2) && + (p.qos.backward.qos_class != T_ATM_QOS_CLASS_3) && + (p.qos.backward.qos_class != T_ATM_QOS_CLASS_4)) + return (EINVAL); + + if (p.qos.coding_standard == T_ATM_ABSENT) + atp->atp_attr.qos.tag = T_ATM_ABSENT; + else { + atp->atp_attr.qos.tag = T_ATM_PRESENT; + atp->atp_attr.qos.v = p.qos; + } + break; + + case T_ATM_TRANSIT: + err = sooptcopyin(sopt, &p.trn, sizeof p.trn, sizeof p.trn); + if (err) + break; + if (p.trn.length > T_ATM_MAX_NET_ID) + return (EINVAL); + + if (p.trn.length == 0) + atp->atp_attr.transit.tag = T_ATM_ABSENT; + else { + atp->atp_attr.transit.tag = T_ATM_PRESENT; + atp->atp_attr.transit.v = p.trn; + } + break; + + case T_ATM_ADD_LEAF: + return (EPROTONOSUPPORT); /* XXX */ + + case T_ATM_DROP_LEAF: + return (EPROTONOSUPPORT); /* XXX */ + + case T_ATM_NET_INTF: + err = sooptcopyin(sopt, &p.nif, sizeof p.nif, sizeof p.nif); + if (err) + break; + + atp->atp_attr.nif = atm_nifname(p.nif.net_intf); + if (atp->atp_attr.nif == NULL) + return (ENXIO); + break; + + case T_ATM_LLC: + err = sooptcopyin(sopt, &p.llc, sizeof p.llc, sizeof p.llc); + if (err) + break; + if ((p.llc.llc_len < T_ATM_LLC_MIN_LEN) || + (p.llc.llc_len > T_ATM_LLC_MAX_LEN)) + return (EINVAL); + + atp->atp_attr.llc.tag = T_ATM_PRESENT; + atp->atp_attr.llc.v = p.llc; + break; + + case T_ATM_APP_NAME: + err = sooptcopyin(sopt, &p.appn, sizeof p.appn, sizeof p.appn); + if (err) + break; + + strncpy(atp->atp_name, p.appn.app_name, T_ATM_APP_NAME_LEN); + break; + + default: + return (ENOPROTOOPT); + } + + return (err); +} + + +/* + * Common getsockopt processing + * + * Called at splnet. + * + * Arguments: + * so pointer to socket + * sopt pointer to socket option info + * atp pointer to ATM PCB + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +int +atm_sock_getopt(so, sopt, atp) + struct socket *so; + struct sockopt *sopt; + Atm_pcb *atp; +{ + Atm_attributes *ap; + + /* + * If socket is connected, return attributes for the VCC in use, + * otherwise just return what the user has setup so far. + */ + if (so->so_state & SS_ISCONNECTED) + ap = &atp->atp_conn->co_connvc->cvc_attr; + else + ap = &atp->atp_attr; + + switch (sopt->sopt_name) { + + case T_ATM_AAL5: + if ((ap->aal.tag == T_ATM_PRESENT) && + (ap->aal.type == ATM_AAL5)) { + return (sooptcopyout(sopt, &ap->aal.v.aal5, + sizeof ap->aal.v.aal5)); + } else { + return (ENOENT); + } + break; + + case T_ATM_TRAFFIC: + if (ap->traffic.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->traffic.v, + sizeof ap->traffic.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_BEARER_CAP: + if (ap->bearer.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->bearer.v, + sizeof ap->bearer.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_BHLI: + if (ap->bhli.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->bhli.v, + sizeof ap->bhli.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_BLLI: + if ((ap->blli.tag_l2 == T_ATM_PRESENT) || + (ap->blli.tag_l3 == T_ATM_PRESENT)) { + return (sooptcopyout(sopt, &ap->blli.v, + sizeof ap->blli.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_DEST_ADDR: + if (ap->called.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->called.addr, + sizeof ap->called.addr)); + } else { + return (ENOENT); + } + break; + + case T_ATM_DEST_SUB: + if (ap->called.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->called.subaddr, + sizeof ap->called.subaddr)); + } else { + return (ENOENT); + } + break; + + case T_ATM_ORIG_ADDR: + if (ap->calling.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->calling.addr, + sizeof ap->calling.addr)); + } else { + return (ENOENT); + } + break; + + case T_ATM_ORIG_SUB: + if (ap->calling.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->calling.subaddr, + sizeof ap->calling.subaddr)); + } else { + return (ENOENT); + } + break; + + case T_ATM_CALLER_ID: + if (ap->calling.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->calling.cid, + sizeof ap->calling.cid)); + } else { + return (ENOENT); + } + break; + + case T_ATM_CAUSE: + if (ap->cause.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->cause.v, + sizeof ap->cause.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_QOS: + if (ap->qos.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->qos.v, + sizeof ap->qos.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_TRANSIT: + if (ap->transit.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->transit.v, + sizeof ap->transit.v)); + } else { + return (ENOENT); + } + break; + + case T_ATM_LEAF_IND: + return (EPROTONOSUPPORT); /* XXX */ + + case T_ATM_NET_INTF: + if (ap->nif) { + struct t_atm_net_intf netif; + struct ifnet *ifp; + + ifp = &ap->nif->nif_if; + (void) sprintf(netif.net_intf, "%s%d", + ifp->if_name, ifp->if_unit); + return (sooptcopyout(sopt, &netif, + sizeof netif)); + } else { + return (ENOENT); + } + break; + + case T_ATM_LLC: + if (ap->llc.tag == T_ATM_PRESENT) { + return (sooptcopyout(sopt, &ap->llc.v, + sizeof ap->llc.v)); + } else { + return (ENOENT); + } + break; + + default: + return (ENOPROTOOPT); + } + + return (0); +} + + +/* + * Process Socket VCC Connected Notification + * + * Arguments: + * toku owner's connection token (atm_pcb protocol block) + * + * Returns: + * none + * + */ +void +atm_sock_connected(toku) + void *toku; +{ + Atm_pcb *atp = (Atm_pcb *)toku; + + /* + * Connection is setup + */ + atm_sock_stat.as_conncomp[atp->atp_type]++; + soisconnected(atp->atp_socket); +} + + +/* + * Process Socket VCC Cleared Notification + * + * Arguments: + * toku owner's connection token (atm_pcb protocol block) + * cause pointer to cause code + * + * Returns: + * none + * + */ +void +atm_sock_cleared(toku, cause) + void *toku; + struct t_atm_cause *cause; +{ + Atm_pcb *atp = (Atm_pcb *)toku; + struct socket *so; + + so = atp->atp_socket; + + /* + * Save call clearing cause + */ + atp->atp_attr.cause.tag = T_ATM_PRESENT; + atp->atp_attr.cause.v = *cause; + + /* + * Set user error code + */ + if (so->so_state & SS_ISCONNECTED) { + so->so_error = ECONNRESET; + atm_sock_stat.as_connclr[atp->atp_type]++; + } else { + so->so_error = ECONNREFUSED; + atm_sock_stat.as_connfail[atp->atp_type]++; + } + + /* + * Connection is gone + */ + atp->atp_conn = NULL; + soisdisconnected(so); + + /* + * Cleanup failed incoming connection setup + */ + if (so->so_state & SS_NOFDREF) { + (void) atm_sock_detach(so); + } +} + diff --git a/sys/netatm/atm_stack.h b/sys/netatm/atm_stack.h new file mode 100644 index 0000000..abd32c4 --- /dev/null +++ b/sys/netatm/atm_stack.h @@ -0,0 +1,287 @@ +/* + * + * =================================== + * 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: atm_stack.h,v 1.5 1998/02/19 19:59:59 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Stack definitions + * + */ + +#ifndef _NETATM_ATM_STACK_H +#define _NETATM_ATM_STACK_H + +#ifdef ATM_KERNEL +/* + * Structure used to define a kernel-provided ATM stack service and its + * associated entry points. Each stack service provider must register + * themselves before they will be used. ATM stack service providers include + * kernel modules (both linked and loaded) and device drivers, which must list + * (via its atm_pif) any of its available hardware-supplied stack services + * (such as on-card AAL processing). + */ +struct stack_defn { + struct stack_defn *sd_next; /* Next in registry list */ + Sap_t sd_sap; /* Stack instance SAP */ + u_char sd_flag; /* Flags (see below) */ +/* Exported functions */ + int (*sd_inst) /* Stack instantiation */ + __P((struct stack_defn **, Atm_connvc *)); + void (*sd_lower) /* Lower (from above) command handler */ + __P((int, void *, int, int)); + void (*sd_upper) /* Upper (from below) command handler */ + __P((int, void *, int, int)); +/* Variables used during stack instantiation */ + void *sd_toku; /* Stack service instance token */ +}; + +/* + * Stack Service Flags + */ +#define SDF_TERM 0x01 /* Terminal (to lowest layer) service */ + + +/* + * Stack Specification List + * + * The list names the stack services and their layering relationships in + * order to construct a stack to provide the protocol services defined + * by the list. The list is ordered starting from the stack service + * interfacing with the user "down" to the ATM cell service. + */ +#define STACK_CNT 8 /* Max services in a stack list */ +struct stack_list { + Sap_t sl_sap[STACK_CNT]; /* Stack service SAP list */ +}; + + +/* + * Structure used during the construction and instantiation of a stack + * instance from a supplied stack list. It contains pointers to the stack + * service definitions which will be used to implement the stack. The first + * element in the array is reserved for the user's "stack service". + */ +struct stack_inst { + struct stack_defn *si_srvc[STACK_CNT+1]; /* Assigned services */ +}; + + +/* + * Macros to update buffer headroom values during stack instantiation. + * + * These values are advisory, i.e. every service must verify the amount + * of available space in input/output messages and allocate new buffers + * if needed. + * + * The 'maximum' and 'minimum' values used below may be chosen by a + * service to reflect the typical, expected message traffic pattern + * for a specific connection. + * + * The macro arguments are: + * cvp = pointer to connection vcc; + * hi = maximum amount of buffer headroom required by the current + * service during input message processing; + * si = minimum amount of buffer data stripped off the front + * of an input message by the current service; + * ho = maximum amount of buffer headroom required by the current + * service during output message processing; + * ao = maximum amount of buffer data added to the front + * of an output message by the current service; + */ +#define HEADIN(cvp, hi, si) \ +{ \ + short t = (cvp)->cvc_attr.headin - (si); \ + t = (t >= (hi)) ? t : (hi); \ + (cvp)->cvc_attr.headin = roundup(t, sizeof(long)); \ +} + +#define HEADOUT(cvp, ho, ao) \ +{ \ + short t = (cvp)->cvc_attr.headout + (ao); \ + t = (t >= (ho)) ? t : (ho); \ + (cvp)->cvc_attr.headout = roundup(t, sizeof(long)); \ +} + + +/* + * Stack command codes - All stack command codes are specific to the + * defined stack SAP across which the command is used. Command values 0-15 + * are reserved for any common codes, which all stack SAPs must support. + */ +#define STKCMD(s, d, v) (((s) << 16) | (d) | (v)) +#define STKCMD_DOWN 0 +#define STKCMD_UP 0x00008000 +#define STKCMD_SAP_MASK 0xffff0000 +#define STKCMD_VAL_MASK 0x00007fff + +/* Common command values (0-15) */ +#define CCV_INIT 1 /* DOWN */ +#define CCV_TERM 2 /* DOWN */ + +/* SAP_ATM */ +#define ATM_INIT STKCMD(SAP_ATM, STKCMD_DOWN, CCV_INIT) +#define ATM_TERM STKCMD(SAP_ATM, STKCMD_DOWN, CCV_TERM) +#define ATM_DATA_REQ STKCMD(SAP_ATM, STKCMD_DOWN, 16) +#define ATM_DATA_IND STKCMD(SAP_ATM, STKCMD_UP, 17) + +/* SAP_SAR */ +#define SAR_INIT STKCMD(SAP_SAR, STKCMD_DOWN, CCV_INIT) +#define SAR_TERM STKCMD(SAP_SAR, STKCMD_DOWN, CCV_TERM) +#define SAR_UNITDATA_INV STKCMD(SAP_SAR, STKCMD_DOWN, 16) +#define SAR_UNITDATA_SIG STKCMD(SAP_SAR, STKCMD_UP, 17) +#define SAR_UABORT_INV STKCMD(SAP_SAR, STKCMD_DOWN, 18) +#define SAR_UABORT_SIG STKCMD(SAP_SAR, STKCMD_UP, 19) +#define SAR_PABORT_SIG STKCMD(SAP_SAR, STKCMD_UP, 20) + +/* SAP_CPCS */ +#define CPCS_INIT STKCMD(SAP_CPCS, STKCMD_DOWN, CCV_INIT) +#define CPCS_TERM STKCMD(SAP_CPCS, STKCMD_DOWN, CCV_TERM) +#define CPCS_UNITDATA_INV STKCMD(SAP_CPCS, STKCMD_DOWN, 16) +#define CPCS_UNITDATA_SIG STKCMD(SAP_CPCS, STKCMD_UP, 17) +#define CPCS_UABORT_INV STKCMD(SAP_CPCS, STKCMD_DOWN, 18) +#define CPCS_UABORT_SIG STKCMD(SAP_CPCS, STKCMD_UP, 19) +#define CPCS_PABORT_SIG STKCMD(SAP_CPCS, STKCMD_UP, 20) + +/* SAP_SSCOP */ +#define SSCOP_INIT STKCMD(SAP_SSCOP, STKCMD_DOWN, CCV_INIT) +#define SSCOP_TERM STKCMD(SAP_SSCOP, STKCMD_DOWN, CCV_TERM) +#define SSCOP_ESTABLISH_REQ STKCMD(SAP_SSCOP, STKCMD_DOWN, 16) +#define SSCOP_ESTABLISH_IND STKCMD(SAP_SSCOP, STKCMD_UP, 17) +#define SSCOP_ESTABLISH_RSP STKCMD(SAP_SSCOP, STKCMD_DOWN, 18) +#define SSCOP_ESTABLISH_CNF STKCMD(SAP_SSCOP, STKCMD_UP, 19) +#define SSCOP_RELEASE_REQ STKCMD(SAP_SSCOP, STKCMD_DOWN, 20) +#define SSCOP_RELEASE_IND STKCMD(SAP_SSCOP, STKCMD_UP, 21) +#define SSCOP_RELEASE_CNF STKCMD(SAP_SSCOP, STKCMD_UP, 22) +#define SSCOP_DATA_REQ STKCMD(SAP_SSCOP, STKCMD_DOWN, 23) +#define SSCOP_DATA_IND STKCMD(SAP_SSCOP, STKCMD_UP, 24) +#define SSCOP_RESYNC_REQ STKCMD(SAP_SSCOP, STKCMD_DOWN, 25) +#define SSCOP_RESYNC_IND STKCMD(SAP_SSCOP, STKCMD_UP, 26) +#define SSCOP_RESYNC_RSP STKCMD(SAP_SSCOP, STKCMD_DOWN, 27) +#define SSCOP_RESYNC_CNF STKCMD(SAP_SSCOP, STKCMD_UP, 28) +#define SSCOP_RECOVER_IND STKCMD(SAP_SSCOP, STKCMD_UP, 29) +#define SSCOP_RECOVER_RSP STKCMD(SAP_SSCOP, STKCMD_DOWN, 30) +#define SSCOP_UNITDATA_REQ STKCMD(SAP_SSCOP, STKCMD_DOWN, 31) +#define SSCOP_UNITDATA_IND STKCMD(SAP_SSCOP, STKCMD_UP, 32) +#define SSCOP_RETRIEVE_REQ STKCMD(SAP_SSCOP, STKCMD_DOWN, 33) +#define SSCOP_RETRIEVE_IND STKCMD(SAP_SSCOP, STKCMD_UP, 34) +#define SSCOP_RETRIEVECMP_IND STKCMD(SAP_SSCOP, STKCMD_UP, 35) + +/* SAP_SSCF_UNI */ +#define SSCF_UNI_INIT STKCMD(SAP_SSCF_UNI, STKCMD_DOWN, CCV_INIT) +#define SSCF_UNI_TERM STKCMD(SAP_SSCF_UNI, STKCMD_DOWN, CCV_TERM) +#define SSCF_UNI_ESTABLISH_REQ STKCMD(SAP_SSCF_UNI, STKCMD_DOWN, 16) +#define SSCF_UNI_ESTABLISH_IND STKCMD(SAP_SSCF_UNI, STKCMD_UP, 17) +#define SSCF_UNI_ESTABLISH_CNF STKCMD(SAP_SSCF_UNI, STKCMD_UP, 18) +#define SSCF_UNI_RELEASE_REQ STKCMD(SAP_SSCF_UNI, STKCMD_DOWN, 19) +#define SSCF_UNI_RELEASE_IND STKCMD(SAP_SSCF_UNI, STKCMD_UP, 20) +#define SSCF_UNI_RELEASE_CNF STKCMD(SAP_SSCF_UNI, STKCMD_UP, 21) +#define SSCF_UNI_DATA_REQ STKCMD(SAP_SSCF_UNI, STKCMD_DOWN, 22) +#define SSCF_UNI_DATA_IND STKCMD(SAP_SSCF_UNI, STKCMD_UP, 23) +#define SSCF_UNI_UNITDATA_REQ STKCMD(SAP_SSCF_UNI, STKCMD_DOWN, 24) +#define SSCF_UNI_UNITDATA_IND STKCMD(SAP_SSCF_UNI, STKCMD_UP, 25) + + +/* + * The STACK_CALL macro must be used for all stack calls between adjacent + * entities. In order to avoid the problem with recursive stack calls + * modifying protocol state, this macro will only allow calls to proceed if + * they are not "against the flow" of any currently pending calls for a + * stack instance. If the requested call can't be processed now, it will + * be deferred and queued until a later, safe time (but before control is + * returned back to the kernel scheduler) when it will be dispatched. + * + * The STACK_CALL macro arguments are: + * cmd = command code; + * fn = Destination entity processing function + * tok = Destination layer's session token; + * cvp = Connection VCC address; + * a1 = command specific argument; + * a2 = command specific argument; + * ret = call result value (0 => success) + * + * The receiving entity command processing function prototype is: + * + * void (fn)(int cmd, int tok, int arg1, int arg2) + * + */ +#define STACK_CALL(cmd, fn, tok, cvp, a1, a2, ret) \ +{ \ + if ((cmd) & STKCMD_UP) { \ + if ((cvp)->cvc_downcnt) { \ + (ret) = atm_stack_enq((cmd), (fn), (tok), \ + (cvp), (a1), (a2)); \ + } else { \ + (cvp)->cvc_upcnt++; \ + (*fn)(cmd, tok, a1, a2); \ + (cvp)->cvc_upcnt--; \ + (ret) = 0; \ + } \ + } else { \ + if ((cvp)->cvc_upcnt) { \ + (ret) = atm_stack_enq((cmd), (fn), (tok), \ + (cvp), (a1), (a2)); \ + } else { \ + (cvp)->cvc_downcnt++; \ + (*fn)(cmd, tok, a1, a2); \ + (cvp)->cvc_downcnt--; \ + (ret) = 0; \ + } \ + } \ +} + + +/* + * Stack queue entry - The stack queue will contain stack calls which have + * been deferred in order to avoid recursive calls to a single protocol + * control block. The queue entries are allocated from its own storage pool. + */ +struct stackq_entry { + struct stackq_entry *sq_next; /* Next entry in queue */ + int sq_cmd; /* Stack command */ + void (*sq_func) /* Destination function */ + __P((int, void *, int, int)); + void *sq_token; /* Destination token */ + int sq_arg1; /* Command-specific argument */ + int sq_arg2; /* Command-specific argument */ + Atm_connvc *sq_connvc; /* Connection VCC */ +}; + + +/* + * Macro to avoid unnecessary function call when draining the stack queue. + */ +#define STACK_DRAIN() \ +{ \ + if (atm_stackq_head) \ + atm_stack_drain(); \ +} +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_STACK_H */ diff --git a/sys/netatm/atm_subr.c b/sys/netatm/atm_subr.c new file mode 100644 index 0000000..40fc433 --- /dev/null +++ b/sys/netatm/atm_subr.c @@ -0,0 +1,970 @@ +/* + * + * =================================== + * 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: atm_subr.c,v 1.10 1998/05/18 19:06:02 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * Miscellaneous ATM subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_subr.c,v 1.10 1998/05/18 19:06:02 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Global variables + */ +struct atm_pif *atm_interface_head = NULL; +struct atm_ncm *atm_netconv_head = NULL; +Atm_endpoint *atm_endpoints[ENDPT_MAX+1] = {NULL}; +struct sp_info *atm_pool_head = NULL; +struct stackq_entry *atm_stackq_head = NULL, *atm_stackq_tail; +struct ifqueue atm_intrq; +#ifdef sgi +int atm_intr_index; +#endif +struct atm_sock_stat atm_sock_stat = {0}; +int atm_init = 0; +int atm_debug = 0; +int atm_dev_print = 0; +int atm_print_data = 0; +int atm_version = ATM_VERSION; +struct timeval atm_debugtime = {0, 0}; + +struct sp_info atm_attributes_pool = { + "atm attributes pool", /* si_name */ + sizeof(Atm_attributes), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + + +/* + * Local functions + */ +static void atm_compact __P((struct atm_time *)); +static KTimeout_ret atm_timexp __P((void *)); + +/* + * Local variables + */ +static struct atm_time *atm_timeq = NULL; +static struct atm_time atm_compactimer = {0, 0}; + +static struct sp_info atm_stackq_pool = { + "Service stack queue pool", /* si_name */ + sizeof(struct stackq_entry), /* si_blksiz */ + 10, /* si_blkcnt */ + 10 /* si_maxallow */ +}; + + +/* + * Initialize ATM kernel + * + * Performs any initialization required before things really get underway. + * Called from ATM domain initialization or from first registration function + * which gets called. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +atm_initialize() +{ + /* + * Never called from interrupts, so no locking needed + */ + if (atm_init) + return; + atm_init = 1; + +#ifndef __FreeBSD__ + /* + * Add ATM protocol family + */ + (void) protocol_family(&atmdomain, NULL, NULL); +#endif + + atm_intrq.ifq_maxlen = ATM_INTRQ_MAX; +#ifdef sgi + atm_intr_index = register_isr(atm_intr); +#endif + + /* + * Initialize subsystems + */ + atm_aal5_init(); + + /* + * Prime the timer + */ + (void) timeout(atm_timexp, (void *)0, hz/ATM_HZ); + + /* + * Start the compaction timer + */ + atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact); +} + + +/* + * Allocate a Control Block + * + * Gets a new control block allocated from the specified storage pool, + * acquiring memory for new pool chunks if required. The returned control + * block's contents will be cleared. + * + * Arguments: + * sip pointer to sp_info for storage pool + * + * Returns: + * addr pointer to allocated control block + * 0 allocation failed + * + */ +void * +atm_allocate(sip) + struct sp_info *sip; +{ + void *bp; + struct sp_chunk *scp; + struct sp_link *slp; + int s = splnet(); + + /* + * Count calls + */ + sip->si_allocs++; + + /* + * Are there any free in the pool? + */ + if (sip->si_free) { + + /* + * Find first chunk with a free block + */ + for (scp = sip->si_poolh; scp; scp = scp->sc_next) { + if (scp->sc_freeh != NULL) + break; + } + + } else { + + /* + * No free blocks - have to allocate a new + * chunk (but put a limit to this) + */ + struct sp_link *slp_next; + int i; + + /* + * First time for this pool?? + */ + if (sip->si_chunksiz == 0) { + size_t n; + + /* + * Initialize pool information + */ + n = sizeof(struct sp_chunk) + + sip->si_blkcnt * + (sip->si_blksiz + sizeof(struct sp_link)); + sip->si_chunksiz = roundup(n, SPOOL_ROUNDUP); + + /* + * Place pool on kernel chain + */ + LINK2TAIL(sip, struct sp_info, atm_pool_head, si_next); + } + + if (sip->si_chunks >= sip->si_maxallow) { + sip->si_fails++; + (void) splx(s); + return (NULL); + } + + scp = (struct sp_chunk *) + KM_ALLOC(sip->si_chunksiz, M_DEVBUF, M_NOWAIT); + if (scp == NULL) { + sip->si_fails++; + (void) splx(s); + return (NULL); + } + scp->sc_next = NULL; + scp->sc_info = sip; + scp->sc_magic = SPOOL_MAGIC; + scp->sc_used = 0; + + /* + * Divy up chunk into free blocks + */ + slp = (struct sp_link *)(scp + 1); + scp->sc_freeh = slp; + + for (i = sip->si_blkcnt; i > 1; i--) { + slp_next = (struct sp_link *)((caddr_t)(slp + 1) + + sip->si_blksiz); + slp->sl_u.slu_next = slp_next; + slp = slp_next; + } + slp->sl_u.slu_next = NULL; + scp->sc_freet = slp; + + /* + * Add new chunk to end of pool + */ + if (sip->si_poolh) + sip->si_poolt->sc_next = scp; + else + sip->si_poolh = scp; + sip->si_poolt = scp; + + sip->si_chunks++; + sip->si_total += sip->si_blkcnt; + sip->si_free += sip->si_blkcnt; + if (sip->si_chunks > sip->si_maxused) + sip->si_maxused = sip->si_chunks; + } + + /* + * Allocate the first free block in chunk + */ + slp = scp->sc_freeh; + scp->sc_freeh = slp->sl_u.slu_next; + scp->sc_used++; + sip->si_free--; + bp = (slp + 1); + + /* + * Save link back to pool chunk + */ + slp->sl_u.slu_chunk = scp; + + /* + * Clear out block + */ + KM_ZERO(bp, sip->si_blksiz); + + (void) splx(s); + return (bp); +} + + +/* + * Free a Control Block + * + * Returns a previously allocated control block back to the owners + * storage pool. + * + * Arguments: + * bp pointer to block to be freed + * + * Returns: + * none + * + */ +void +atm_free(bp) + void *bp; +{ + struct sp_info *sip; + struct sp_chunk *scp; + struct sp_link *slp; + int s = splnet(); + + /* + * Get containing chunk and pool info + */ + slp = (struct sp_link *)bp; + slp--; + scp = slp->sl_u.slu_chunk; + if (scp->sc_magic != SPOOL_MAGIC) + panic("atm_free: chunk magic missing"); + sip = scp->sc_info; + + /* + * Add block to free chain + */ + if (scp->sc_freeh) { + scp->sc_freet->sl_u.slu_next = slp; + scp->sc_freet = slp; + } else + scp->sc_freeh = scp->sc_freet = slp; + slp->sl_u.slu_next = NULL; + sip->si_free++; + scp->sc_used--; + + (void) splx(s); + return; +} + + +/* + * Storage Pool Compaction + * + * Called periodically in order to perform compaction of the + * storage pools. Each pool will be checked to see if any chunks + * can be freed, taking some care to avoid freeing too many chunks + * in order to avoid memory thrashing. + * + * Called at splnet. + * + * Arguments: + * tip pointer to timer control block (atm_compactimer) + * + * Returns: + * none + * + */ +static void +atm_compact(tip) + struct atm_time *tip; +{ + struct sp_info *sip; + struct sp_chunk *scp; + int i; + struct sp_chunk *scp_prev; + + /* + * Check out all storage pools + */ + for (sip = atm_pool_head; sip; sip = sip->si_next) { + + /* + * Always keep a minimum number of chunks around + */ + if (sip->si_chunks <= SPOOL_MIN_CHUNK) + continue; + + /* + * Maximum chunks to free at one time will leave + * pool with at least 50% utilization, but never + * go below minimum chunk count. + */ + i = ((sip->si_free * 2) - sip->si_total) / sip->si_blkcnt; + i = MIN(i, sip->si_chunks - SPOOL_MIN_CHUNK); + + /* + * Look for chunks to free + */ + scp_prev = NULL; + for (scp = sip->si_poolh; scp && i > 0; ) { + + if (scp->sc_used == 0) { + + /* + * Found a chunk to free, so do it + */ + if (scp_prev) { + scp_prev->sc_next = scp->sc_next; + if (sip->si_poolt == scp) + sip->si_poolt = scp_prev; + } else + sip->si_poolh = scp->sc_next; + + KM_FREE((caddr_t)scp, sip->si_chunksiz, + M_DEVBUF); + + /* + * Update pool controls + */ + sip->si_chunks--; + sip->si_total -= sip->si_blkcnt; + sip->si_free -= sip->si_blkcnt; + i--; + if (scp_prev) + scp = scp_prev->sc_next; + else + scp = sip->si_poolh; + } else { + scp_prev = scp; + scp = scp->sc_next; + } + } + } + + /* + * Restart the compaction timer + */ + atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact); + + return; +} + + +/* + * Release a Storage Pool + * + * Frees all dynamic storage acquired for a storage pool. + * This function is normally called just prior to a module's unloading. + * + * Arguments: + * sip pointer to sp_info for storage pool + * + * Returns: + * none + * + */ +void +atm_release_pool(sip) + struct sp_info *sip; +{ + struct sp_chunk *scp, *scp_next; + int s = splnet(); + + /* + * Free each chunk in pool + */ + for (scp = sip->si_poolh; scp; scp = scp_next) { + + /* + * Check for memory leaks + */ + if (scp->sc_used) + panic("atm_release_pool: unfreed blocks"); + + scp_next = scp->sc_next; + + KM_FREE((caddr_t)scp, sip->si_chunksiz, M_DEVBUF); + } + + /* + * Update pool controls + */ + sip->si_poolh = NULL; + sip->si_chunks = 0; + sip->si_total = 0; + sip->si_free = 0; + + /* + * Unlink pool from active chain + */ + sip->si_chunksiz = 0; + UNLINK(sip, struct sp_info, atm_pool_head, si_next); + + (void) splx(s); + return; +} + + +/* + * Handle timer tick expiration + * + * Decrement tick count in first block on timer queue. If there + * are blocks with expired timers, call their timeout function. + * This function is called ATM_HZ times per second. + * + * Arguments: + * arg argument passed on timeout() call + * + * Returns: + * none + * + */ +static KTimeout_ret +atm_timexp(arg) + void *arg; +{ + struct atm_time *tip; + int s = splimp(); + + + /* + * Decrement tick count + */ + if (((tip = atm_timeq) == NULL) || (--tip->ti_ticks > 0)) { + goto restart; + } + + /* + * Stack queue should have been drained + */ +#ifdef DIAGNOSTIC + if (atm_stackq_head != NULL) + panic("atm_timexp: stack queue not empty"); +#endif + + /* + * Dispatch expired timers + */ + while (((tip = atm_timeq) != NULL) && (tip->ti_ticks == 0)) { + void (*func)__P((struct atm_time *)); + + /* + * Remove expired block from queue + */ + atm_timeq = tip->ti_next; + tip->ti_flag &= ~TIF_QUEUED; + + /* + * Call timeout handler (with network interrupts locked out) + */ + func = tip->ti_func; + (void) splx(s); + s = splnet(); + (*func)(tip); + (void) splx(s); + s = splimp(); + + /* + * Drain any deferred calls + */ + STACK_DRAIN(); + } + +restart: + /* + * Restart the timer + */ + (void) splx(s); + (void) timeout(atm_timexp, (void *)0, hz/ATM_HZ); + + return; +} + + +/* + * Schedule a control block timeout + * + * Place the supplied timer control block on the timer queue. The + * function (func) will be called in 't' timer ticks with the + * control block address as its only argument. There are ATM_HZ + * timer ticks per second. The ticks value stored in each block is + * a delta of the number of ticks from the previous block in the queue. + * Thus, for each tick interval, only the first block in the queue + * needs to have its tick value decremented. + * + * Arguments: + * tip pointer to timer control block + * t number of timer ticks until expiration + * func pointer to function to call at expiration + * + * Returns: + * none + * + */ +void +atm_timeout(tip, t, func) + struct atm_time *tip; + int t; + void (*func)__P((struct atm_time *)); +{ + struct atm_time *tip1, *tip2; + int s; + + + /* + * Check for double queueing error + */ + if (tip->ti_flag & TIF_QUEUED) + panic("atm_timeout: double queueing"); + + /* + * Make sure we delay at least a little bit + */ + if (t <= 0) + t = 1; + + /* + * Find out where we belong on the queue + */ + s = splimp(); + for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2->ti_ticks <= t); + tip1 = tip2, tip2 = tip1->ti_next) { + t -= tip2->ti_ticks; + } + + /* + * Place ourselves on queue and update timer deltas + */ + if (tip1 == NULL) + atm_timeq = tip; + else + tip1->ti_next = tip; + tip->ti_next = tip2; + + if (tip2) + tip2->ti_ticks -= t; + + /* + * Setup timer block + */ + tip->ti_flag |= TIF_QUEUED; + tip->ti_ticks = t; + tip->ti_func = func; + + (void) splx(s); + return; +} + + +/* + * Cancel a timeout + * + * Remove the supplied timer control block from the timer queue. + * + * Arguments: + * tip pointer to timer control block + * + * Returns: + * 0 control block successfully dequeued + * 1 control block not on timer queue + * + */ +int +atm_untimeout(tip) + struct atm_time *tip; +{ + struct atm_time *tip1, *tip2; + int s; + + /* + * Is control block queued? + */ + if ((tip->ti_flag & TIF_QUEUED) == 0) + return(1); + + /* + * Find control block on the queue + */ + s = splimp(); + for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2 != tip); + tip1 = tip2, tip2 = tip1->ti_next) { + } + + if (tip2 == NULL) { + (void) splx(s); + return (1); + } + + /* + * Remove block from queue and update timer deltas + */ + tip2 = tip->ti_next; + if (tip1 == NULL) + atm_timeq = tip2; + else + tip1->ti_next = tip2; + + if (tip2) + tip2->ti_ticks += tip->ti_ticks; + + /* + * Reset timer block + */ + tip->ti_flag &= ~TIF_QUEUED; + + (void) splx(s); + return (0); +} + + +/* + * Queue a Stack Call + * + * Queues a stack call which must be deferred to the global stack queue. + * The call parameters are stored in entries which are allocated from the + * stack queue storage pool. + * + * Arguments: + * cmd stack command + * func destination function + * token destination layer's token + * cvp pointer to connection vcc + * arg1 command argument + * arg2 command argument + * + * Returns: + * 0 call queued + * errno call not queued - reason indicated + * + */ +int +atm_stack_enq(cmd, func, token, cvp, arg1, arg2) + int cmd; + void (*func)__P((int, void *, int, int)); + void *token; + Atm_connvc *cvp; + int arg1; + int arg2; +{ + struct stackq_entry *sqp; + int s = splnet(); + + /* + * Get a new queue entry for this call + */ + sqp = (struct stackq_entry *)atm_allocate(&atm_stackq_pool); + if (sqp == NULL) { + (void) splx(s); + return (ENOMEM); + } + + /* + * Fill in new entry + */ + sqp->sq_next = NULL; + sqp->sq_cmd = cmd; + sqp->sq_func = func; + sqp->sq_token = token; + sqp->sq_arg1 = arg1; + sqp->sq_arg2 = arg2; + sqp->sq_connvc = cvp; + + /* + * Put new entry at end of queue + */ + if (atm_stackq_head == NULL) + atm_stackq_head = sqp; + else + atm_stackq_tail->sq_next = sqp; + atm_stackq_tail = sqp; + + (void) splx(s); + return (0); +} + + +/* + * Drain the Stack Queue + * + * Dequeues and processes entries from the global stack queue. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +atm_stack_drain() +{ + struct stackq_entry *sqp, *qprev, *qnext; + int s = splnet(); + int cnt; + + /* + * Loop thru entire queue until queue is empty + * (but panic rather loop forever) + */ + do { + cnt = 0; + qprev = NULL; + for (sqp = atm_stackq_head; sqp; ) { + + /* + * Got an eligible entry, do STACK_CALL stuff + */ + if (sqp->sq_cmd & STKCMD_UP) { + if (sqp->sq_connvc->cvc_downcnt) { + + /* + * Cant process now, skip it + */ + qprev = sqp; + sqp = sqp->sq_next; + continue; + } + + /* + * OK, dispatch the call + */ + sqp->sq_connvc->cvc_upcnt++; + (*sqp->sq_func)(sqp->sq_cmd, + sqp->sq_token, + sqp->sq_arg1, + sqp->sq_arg2); + sqp->sq_connvc->cvc_upcnt--; + } else { + if (sqp->sq_connvc->cvc_upcnt) { + + /* + * Cant process now, skip it + */ + qprev = sqp; + sqp = sqp->sq_next; + continue; + } + + /* + * OK, dispatch the call + */ + sqp->sq_connvc->cvc_downcnt++; + (*sqp->sq_func)(sqp->sq_cmd, + sqp->sq_token, + sqp->sq_arg1, + sqp->sq_arg2); + sqp->sq_connvc->cvc_downcnt--; + } + + /* + * Dequeue processed entry and free it + */ + cnt++; + qnext = sqp->sq_next; + if (qprev) + qprev->sq_next = qnext; + else + atm_stackq_head = qnext; + if (qnext == NULL) + atm_stackq_tail = qprev; + atm_free((caddr_t)sqp); + sqp = qnext; + } + } while (cnt > 0); + + /* + * Make sure entire queue was drained + */ + if (atm_stackq_head != NULL) + panic("atm_stack_drain: Queue not emptied"); + + (void) splx(s); +} + + +/* + * Process Interrupt Queue + * + * Processes entries on the ATM interrupt queue. This queue is used by + * device interface drivers in order to schedule events from the driver's + * lower (interrupt) half to the driver's stack services. + * + * The interrupt routines must store the stack processing function to call + * and a token (typically a driver/stack control block) at the front of the + * queued buffer. We assume that the function pointer and token values are + * both contained (and properly aligned) in the first buffer of the chain. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +atm_intr() +{ + KBuffer *m; + caddr_t cp; + atm_intr_func_t func; + void *token; + int s; + + for (; ; ) { + /* + * Get next buffer from queue + */ + s = splimp(); + IF_DEQUEUE(&atm_intrq, m); + (void) splx(s); + if (m == NULL) + break; + + /* + * Get function to call and token value + */ + KB_DATASTART(m, cp, caddr_t); + func = *(atm_intr_func_t *)cp; + cp += sizeof(func); + token = *(void **)cp; + KB_HEADADJ(m, -(sizeof(func) + sizeof(token))); + if (KB_LEN(m) == 0) { + KBuffer *m1; + KB_UNLINKHEAD(m, m1); + m = m1; + } + + /* + * Call processing function + */ + (*func)(token, m); + + /* + * Drain any deferred calls + */ + STACK_DRAIN(); + } +} + +#ifdef __FreeBSD__ +NETISR_SET(NETISR_ATM, atm_intr); +#endif + + +/* + * Print a pdu buffer chain + * + * Arguments: + * m pointer to pdu buffer chain + * msg pointer to message header string + * + * Returns: + * none + * + */ +void +atm_pdu_print(m, msg) + KBuffer *m; + char *msg; +{ + caddr_t cp; + int i; + char c = ' '; + + printf("%s:", msg); + while (m) { + KB_DATASTART(m, cp, caddr_t); + printf("%cbfr=0x%x data=0x%x len=%d: ", + c, (int)m, (int)cp, KB_LEN(m)); + c = '\t'; + if (atm_print_data) { + for (i = 0; i < KB_LEN(m); i++) { + printf("%2x ", (u_char)*cp++); + } + printf("<end_bfr>\n"); + } else { + printf("\n"); + } + m = KB_NEXT(m); + } +} + diff --git a/sys/netatm/atm_sys.h b/sys/netatm/atm_sys.h new file mode 100644 index 0000000..4cfb031 --- /dev/null +++ b/sys/netatm/atm_sys.h @@ -0,0 +1,275 @@ +/* + * + * =================================== + * 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: atm_sys.h,v 1.12 1998/05/18 19:05:57 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * General system definitions + * + */ + +#ifndef _NETATM_ATM_SYS_H +#define _NETATM_ATM_SYS_H + +/* + * Software Version + */ +#define ATM_VERSION 0x00030000 /* Version 3.0 */ +#define ATM_VERS_MAJ(v) ((v) >> 16) +#define ATM_VERS_MIN(v) ((v) & 0xffff) + + +/* + * Misc system defines + */ +#define ATM_CALLQ_MAX 100 /* Maximum length of call queue */ +#define ATM_INTRQ_MAX 1000 /* Maximum length of interrupt queue */ + + +/* + * ATM address manipulation macros + */ +#define ATM_ADDR_EQUAL(a1, a2) \ + (((a1)->address_format == (a2)->address_format) && \ + ((a1)->address_length == (a2)->address_length) && \ + (bcmp((caddr_t)(a1)->address, (caddr_t)(a2)->address, \ + (a1)->address_length) == 0)) + +#define ATM_ADDR_SEL_EQUAL(a1, s1, a2) \ + (((a1)->address_format == (a2)->address_format) && \ + ((a1)->address_length == (a2)->address_length) && \ + (((((a1)->address_format == T_ATM_ENDSYS_ADDR) || \ + ((a1)->address_format == T_ATM_NSAP_ADDR)) && \ + (bcmp((caddr_t)(a1)->address, (caddr_t)(a2)->address, \ + (a1)->address_length - 1) == 0) && \ + ((s1) == ((struct atm_addr_nsap *)(a2)->address)->aan_sel)) || \ + (((a1)->address_format != T_ATM_ENDSYS_ADDR) && \ + ((a1)->address_format != T_ATM_NSAP_ADDR) && \ + (bcmp((caddr_t)(a1)->address, (caddr_t)(a2)->address, \ + (a1)->address_length) == 0)))) + +#define ATM_ADDR_COPY(a1, a2) \ +{ \ + (a2)->address_format = (a1)->address_format; \ + (a2)->address_length = (a1)->address_length; \ + XM_COPY((caddr_t)(a1)->address, (caddr_t)(a2)->address, \ + (a1)->address_length); \ +} + +#define ATM_ADDR_SEL_COPY(a1, s1, a2) \ +{ \ + (a2)->address_format = (a1)->address_format; \ + (a2)->address_length = (a1)->address_length; \ + if (((a1)->address_format == T_ATM_ENDSYS_ADDR) || \ + ((a1)->address_format == T_ATM_NSAP_ADDR)) { \ + XM_COPY((caddr_t)(a1)->address, (caddr_t)(a2)->address, \ + (a1)->address_length - 1); \ + ((struct atm_addr_nsap *)(a2)->address)->aan_sel = (s1);\ + } else { \ + XM_COPY((caddr_t)(a1)->address, (caddr_t)(a2)->address, \ + (a1)->address_length); \ + } \ +} + + +/* + * ATM Cell Header definitions + */ + +/* + * These macros assume that the cell header (minus the HEC) + * is contained in the least-significant 32-bits of a word + */ +#define ATM_HDR_SET_VPI(vpi) (((vpi) & 0xff) << 20) +#define ATM_HDR_SET_VCI(vci) (((vci) & 0xffff) << 4) +#define ATM_HDR_SET_PT(pt) (((pt) & 0x7) << 1) +#define ATM_HDR_SET_CLP(clp) ((clp) & 0x1) +#define ATM_HDR_SET(vpi,vci,pt,clp) (ATM_HDR_SET_VPI(vpi) | \ + ATM_HDR_SET_VCI(vci) | \ + ATM_HDR_SET_PT(pt) | \ + ATM_HDR_SET_CLP(clp)) +#define ATM_HDR_GET_VPI(hdr) (((hdr) >> 20) & 0xff) +#define ATM_HDR_GET_VCI(hdr) (((hdr) >> 4) & 0xffff) +#define ATM_HDR_GET_PT(hdr) (((hdr) >> 1) & 0x7) +#define ATM_HDR_GET_CLP(hdr) ((hdr) & 0x1) + +/* + * Payload Type Identifier (3 bits) + */ +#define ATM_PT_USER_SDU0 0x0 /* User, no congestion, sdu type 0 */ +#define ATM_PT_USER_SDU1 0x1 /* User, no congestion, sdu type 1 */ +#define ATM_PT_USER_CONG_SDU0 0x2 /* User, congestion, sdu type 0 */ +#define ATM_PT_USER_CONG_SDU1 0x3 /* User, congestion, sdu type 1 */ +#define ATM_PT_NONUSER 0x4 /* User/non-user differentiator */ +#define ATM_PT_OAMF5_SEG 0x4 /* OAM F5 segment flow */ +#define ATM_PT_OAMF5_E2E 0x5 /* OAM F5 end-to-end flow */ + + +/* + * AAL (ATM Adaptation Layer) codes + */ +typedef u_char Aal_t; +#define ATM_AAL0 0 /* AAL0 - Cell service */ +#define ATM_AAL1 1 /* AAL1 */ +#define ATM_AAL2 2 /* AAL2 */ +#define ATM_AAL3_4 3 /* AAL3/4 */ +#define ATM_AAL5 5 /* AAL5 */ + + +/* + * VCC Encapsulation codes + */ +typedef u_char Encaps_t; +#define ATM_ENC_NULL 1 /* Null encapsulation */ +#define ATM_ENC_LLC 2 /* LLC encapsulation */ + + +#ifdef ATM_KERNEL +/* + * ATM timer control block. Used to schedule a timeout via atm_timeout(). + * This control block will typically be embedded in a processing-specific + * control block. + */ +struct atm_time { + u_short ti_ticks; /* Delta of ticks until timeout */ + u_char ti_flag; /* Timer flag bits (see below) */ + void (*ti_func) /* Call at timeout expiration */ + __P((struct atm_time *)); + struct atm_time *ti_next; /* Next on queue */ +}; + +/* + * Timer Flags + */ +#define TIF_QUEUED 0x01 /* Control block on timer queue */ + +#define ATM_HZ 2 /* Time ticks per second */ + + +/* + * To avoid heavy use of kmem_alloc, memory for protocol control blocks may + * be allocated from storage pools. Each control block type will have + * its own pool. Each storage pool will consist of individually allocated + * memory chunks, which will then be sub-divided into the separate control + * blocks. Each chunk will contain a header (sp_chunk) and 'n' blocks of the + * same type, plus a link field for each block. Each chunk will also contain + * a list of all free control blocks in the chunk. + * + * Each protocol must define an sp_info structure for each of its storage + * pools. This structure serves as the "root" for its particular pool. + * Protocols must not modify this structure after its first use. + */ +struct sp_info { + /* Values supplied by pool owner */ + char *si_name; /* Name of pool */ + size_t si_blksiz; /* Size of each block */ + int si_blkcnt; /* Blocks per chunk */ + int si_maxallow; /* Maximum allowable chunks */ + + /* Used by allocate/free functions - do not touch */ + struct sp_info *si_next; /* Next active storage pool */ + struct sp_chunk *si_poolh; /* Storage pool chunk head */ + struct sp_chunk *si_poolt; /* Storage pool chunk tail */ + size_t si_chunksiz; /* Size of chunk */ + int si_chunks; /* Current allocated chunks */ + int si_total; /* Total number of blocks */ + int si_free; /* Free blocks */ + int si_maxused; /* Maximum allocated chunks */ + int si_allocs; /* Total allocate calls */ + int si_fails; /* Allocate failures */ +}; + +struct sp_chunk { + struct sp_chunk *sc_next; /* Next chunk in pool */ + struct sp_info *sc_info; /* Storage pool info */ + u_int sc_magic; /* Chunk magic number */ + int sc_used; /* Allocated blocks in chunk */ + struct sp_link *sc_freeh; /* Head of free blocks in chunk */ + struct sp_link *sc_freet; /* Tail of free blocks in chunk */ +}; + +struct sp_link { + union { + struct sp_link *slu_next; /* Next block in free list */ + struct sp_chunk *slu_chunk; /* Link back to our chunk */ + } sl_u; +}; + +#define SPOOL_MAGIC 0x73d4b69c /* Storage pool magic number */ +#define SPOOL_MIN_CHUNK 2 /* Minimum number of chunks */ +#define SPOOL_ROUNDUP 16 /* Roundup for allocated chunks */ +#define SPOOL_COMPACT (300 * ATM_HZ) /* Compaction timeout value */ + +/* + * Debugging + */ +#ifdef DIAGNOSTIC +#define ATM_TIME \ + struct timeval now, delta; \ + KT_TIME(now); \ + delta.tv_sec = now.tv_sec - atm_debugtime.tv_sec; \ + delta.tv_usec = now.tv_usec - atm_debugtime.tv_usec; \ + atm_debugtime = now; \ + if (delta.tv_usec < 0) { \ + delta.tv_sec--; \ + delta.tv_usec += 1000000; \ + } \ + printf("%3d.%6d: ", delta.tv_sec, delta.tv_usec); + +#define ATM_DEBUG0(f) if (atm_debug) {ATM_TIME; printf(f);} +#define ATM_DEBUGN0(f) if (atm_debug) {printf(f);} +#define ATM_DEBUG1(f,a1) if (atm_debug) {ATM_TIME; printf(f, a1);} +#define ATM_DEBUGN1(f,a1) if (atm_debug) {printf(f, a1);} +#define ATM_DEBUG2(f,a1,a2) if (atm_debug) {ATM_TIME; printf(f, a1, a2);} +#define ATM_DEBUGN2(f,a1,a2) if (atm_debug) {printf(f, a1, a2);} +#define ATM_DEBUG3(f,a1,a2,a3) if (atm_debug) {ATM_TIME; printf(f, a1, a2, a3);} +#define ATM_DEBUGN3(f,a1,a2,a3) if (atm_debug) {printf(f, a1, a2, a3);} +#define ATM_DEBUG4(f,a1,a2,a3,a4) if (atm_debug) {ATM_TIME; printf(f, a1, a2, a3, a4);} +#define ATM_DEBUGN4(f,a1,a2,a3,a4) if (atm_debug) {printf(f, a1, a2, a3, a4);} +#define ATM_DEBUG5(f,a1,a2,a3,a4,a5) if (atm_debug) {ATM_TIME; printf(f, a1, a2, a3, a4, a5);} +#define ATM_DEBUGN5(f,a1,a2,a3,a4,a5) if (atm_debug) {printf(f, a1, a2, a3, a4, a5);} +#else +#define ATM_DEBUG0(f) +#define ATM_DEBUGN0(f) +#define ATM_DEBUG1(f,a1) +#define ATM_DEBUGN1(f,a1) +#define ATM_DEBUG2(f,a1,a2) +#define ATM_DEBUGN2(f,a1,a2) +#define ATM_DEBUG3(f,a1,a2,a3) +#define ATM_DEBUGN3(f,a1,a2,a3) +#define ATM_DEBUG4(f,a1,a2,a3,a4) +#define ATM_DEBUGN4(f,a1,a2,a3,a4) +#define ATM_DEBUG5(f,a1,a2,a3,a4,a5) +#define ATM_DEBUGN5(f,a1,a2,a3,a4,a5) +#endif + +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_SYS_H */ diff --git a/sys/netatm/atm_usrreq.c b/sys/netatm/atm_usrreq.c new file mode 100644 index 0000000..4231d1c --- /dev/null +++ b/sys/netatm/atm_usrreq.c @@ -0,0 +1,711 @@ +/* + * + * =================================== + * 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: atm_usrreq.c,v 1.7 1998/06/29 21:51:29 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM DGRAM socket protocol processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_usrreq.c,v 1.7 1998/06/29 21:51:29 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Local functions + */ +static int atm_dgram_attach __P((struct socket *, int, struct proc *)); +static int atm_dgram_control __P((struct socket *, u_long, caddr_t, + struct ifnet *, struct proc *)); +static int atm_dgram_info __P((caddr_t)); + + +/* + * New-style socket request routines + */ +#if (defined(__FreeBSD__) && (BSD >= 199506)) +struct pr_usrreqs atm_dgram_usrreqs = { + atm_proto_notsupp1, /* pru_abort */ + pru_accept_notsupp, /* pru_accept */ + atm_dgram_attach, /* pru_attach */ + atm_proto_notsupp2, /* pru_bind */ + pru_connect_notsupp, /* pru_connect */ + pru_connect2_notsupp, /* pru_connect2 */ + atm_dgram_control, /* pru_control */ + atm_proto_notsupp1, /* pru_detach */ + atm_proto_notsupp1, /* pru_disconnect */ + pru_listen_notsupp, /* pru_listen */ + atm_proto_notsupp3, /* pru_peeraddr */ + pru_rcvd_notsupp, /* pru_rcvd */ + pru_rcvoob_notsupp, /* pru_rcvoob */ + atm_proto_notsupp4, /* pru_send */ + pru_sense_null, /* pru_sense */ + atm_proto_notsupp1, /* pru_shutdown */ + atm_proto_notsupp3, /* pru_sockaddr */ +}; +#endif + + +/* + * Handy common code macros + */ +#ifdef DIAGNOSTIC +#define ATM_INTRO() \ + int s, err = 0; \ + s = splnet(); \ + /* \ + * Stack queue should have been drained \ + */ \ + if (atm_stackq_head != NULL) \ + panic("atm_usrreq: stack queue not empty"); \ + ; +#else +#define ATM_INTRO() \ + int s, err = 0; \ + s = splnet(); \ + ; +#endif + +#define ATM_OUTRO() \ +done: \ + /* \ + * Drain any deferred calls \ + */ \ + STACK_DRAIN(); \ + (void) splx(s); \ + return (err); \ + ; + +#define ATM_RETERR(errno) { \ + err = errno; \ + goto done; \ +} + + +/* + * Attach protocol to socket + * + * Arguments: + * so pointer to socket + * proto protocol identifier + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_dgram_attach(so, proto, p) + struct socket *so; + int proto; + struct proc *p; +{ + ATM_INTRO(); + + /* + * Nothing to do here for ioctl()-only sockets + */ + ATM_OUTRO(); +} + + +/* + * Process ioctl system calls + * + * Arguments: + * so pointer to socket + * cmd ioctl code + * data pointer to code specific parameter data area + * ifp pointer to ifnet structure if it's an interface ioctl + * p pointer to process + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_dgram_control(so, cmd, data, ifp, p) + struct socket *so; + u_long cmd; + caddr_t data; + struct ifnet *ifp; + struct proc *p; +{ + ATM_INTRO(); + + /* + * First, figure out which ioctl we're dealing with and + * then process it based on the sub-op code + */ + switch (cmd) { + + case AIOCCFG: { + struct atmcfgreq *acp = (struct atmcfgreq *)data; + struct atm_pif *pip; + + if (p && (suser(p->p_ucred, &p->p_acflag) != 0)) + ATM_RETERR(EPERM); + + switch (acp->acr_opcode) { + + case AIOCS_CFG_ATT: + /* + * Attach signalling manager + */ + if ((pip = atm_pifname(acp->acr_att_intf)) == NULL) + ATM_RETERR(ENXIO); + err = atm_sigmgr_attach(pip, acp->acr_att_proto); + break; + + case AIOCS_CFG_DET: + /* + * Detach signalling manager + */ + if ((pip = atm_pifname(acp->acr_det_intf)) == NULL) + ATM_RETERR(ENXIO); + err = atm_sigmgr_detach(pip); + break; + + default: + err = EOPNOTSUPP; + } + break; + } + + case AIOCADD: { + struct atmaddreq *aap = (struct atmaddreq *)data; + Atm_endpoint *epp; + + if (p && (suser(p->p_ucred, &p->p_acflag) != 0)) + ATM_RETERR(EPERM); + + switch (aap->aar_opcode) { + + case AIOCS_ADD_PVC: + /* + * Add a PVC definition + */ + + /* + * Locate requested endpoint service + */ + epp = aap->aar_pvc_sap > ENDPT_MAX ? NULL : + atm_endpoints[aap->aar_pvc_sap]; + if (epp == NULL) + ATM_RETERR(ENOPROTOOPT); + + /* + * Let endpoint service handle it from here + */ + err = (*epp->ep_ioctl)(AIOCS_ADD_PVC, data, NULL); + break; + + case AIOCS_ADD_ARP: + /* + * Add an ARP mapping + */ + epp = atm_endpoints[ENDPT_IP]; + if (epp == NULL) + ATM_RETERR(ENOPROTOOPT); + + /* + * Let IP/ATM endpoint handle this + */ + err = (*epp->ep_ioctl) (AIOCS_ADD_ARP, data, NULL); + break; + + default: + err = EOPNOTSUPP; + } + break; + } + + case AIOCDEL: { + struct atmdelreq *adp = (struct atmdelreq *)data; + struct atm_pif *pip; + struct sigmgr *smp; + Atm_endpoint *epp; + + if (p && (suser(p->p_ucred, &p->p_acflag) != 0)) + ATM_RETERR(EPERM); + + switch (adp->adr_opcode) { + + case AIOCS_DEL_PVC: + case AIOCS_DEL_SVC: + /* + * Delete a PVC or SVC + */ + + /* + * Locate appropriate sigmgr + */ + if ((pip = atm_pifname(adp->adr_pvc_intf)) == NULL) + ATM_RETERR(ENXIO); + if ((smp = pip->pif_sigmgr) == NULL) + ATM_RETERR(ENOENT); + + /* + * Let sigmgr handle it from here + */ + err = (*smp->sm_ioctl)(adp->adr_opcode, data, + (caddr_t)pip->pif_siginst); + break; + + case AIOCS_DEL_ARP: + /* + * Delete an ARP mapping + */ + epp = atm_endpoints[ENDPT_IP]; + if (epp == NULL) + ATM_RETERR(ENOPROTOOPT); + + /* + * Let IP/ATM endpoint handle this + */ + err = (*epp->ep_ioctl) (AIOCS_DEL_ARP, data, NULL); + break; + + default: + err = EOPNOTSUPP; + } + break; + } + + case AIOCSET: { + struct atmsetreq *asp = (struct atmsetreq *)data; + struct atm_pif *pip; + struct atm_nif *nip; + struct sigmgr *smp; + struct ifnet *ifp2; + + if (p && (suser(p->p_ucred, &p->p_acflag) != 0)) + ATM_RETERR(EPERM); + + switch (asp->asr_opcode) { + + case AIOCS_SET_ASV: + /* + * Set an ARP server address + */ + + /* + * Locate appropriate sigmgr + */ + if ((nip = atm_nifname(asp->asr_arp_intf)) == NULL) + ATM_RETERR(ENXIO); + pip = nip->nif_pif; + if ((smp = pip->pif_sigmgr) == NULL) + ATM_RETERR(ENOENT); + + /* + * Let sigmgr handle it from here + */ + err = (*smp->sm_ioctl)(AIOCS_SET_ASV, data, + (caddr_t)nip); + break; + + case AIOCS_SET_MAC: + /* + * Set physical interface MAC/ESI address + */ + + /* + * Locate physical interface + */ + if ((pip = atm_pifname(asp->asr_mac_intf)) == NULL) + ATM_RETERR(ENXIO); + + /* + * Interface must be detached + */ + if (pip->pif_sigmgr != NULL) + ATM_RETERR(EADDRINUSE); + + /* + * Just plunk the address into the pif + */ + KM_COPY((caddr_t)&asp->asr_mac_addr, + (caddr_t)&pip->pif_macaddr, + sizeof(struct mac_addr)); + break; + + case AIOCS_SET_NIF: + /* + * Define network interfaces + */ + if ((pip = atm_pifname(asp->asr_nif_intf)) == NULL) + ATM_RETERR(ENXIO); + + /* + * Validate interface count - logical interfaces + * are differentiated by the atm address selector. + */ + if ((asp->asr_nif_cnt <= 0) || (asp->asr_nif_cnt > 256)) + ATM_RETERR(EINVAL); + + /* + * Make sure prefix name is unique + */ + TAILQ_FOREACH(ifp2, &ifnet, if_link) { + if (!strcmp(ifp2->if_name, asp->asr_nif_pref)) { + /* + * If this is for the interface we're + * (re-)defining, let it through + */ + for (nip = pip->pif_nif; nip; + nip = nip->nif_pnext) { + if (&nip->nif_if == ifp2) + break; + } + if (nip) + continue; + ATM_RETERR(EEXIST); + } + } + + /* + * Let interface handle it from here + */ + err = (*pip->pif_ioctl)(AIOCS_SET_NIF, data, + (caddr_t)pip); + break; + + case AIOCS_SET_PRF: + /* + * Set interface NSAP Prefix + */ + + /* + * Locate appropriate sigmgr + */ + if ((pip = atm_pifname(asp->asr_prf_intf)) == NULL) + ATM_RETERR(ENXIO); + if ((smp = pip->pif_sigmgr) == NULL) + ATM_RETERR(ENOENT); + + /* + * Let sigmgr handle it from here + */ + err = (*smp->sm_ioctl)(AIOCS_SET_PRF, data, + (caddr_t)pip->pif_siginst); + break; + + default: + err = EOPNOTSUPP; + } + break; + } + + case AIOCINFO: + err = atm_dgram_info(data); + break; + + default: + err = EOPNOTSUPP; + } + + ATM_OUTRO(); +} + + +/* + * Process AIOCINFO ioctl system calls + * + * Called at splnet. + * + * Arguments: + * data pointer to AIOCINFO parameter structure + * + * Returns: + * 0 request processed + * errno error processing request - reason indicated + * + */ +static int +atm_dgram_info(data) + caddr_t data; +{ + struct atminfreq *aip = (struct atminfreq *)data; + struct atm_pif *pip; + struct atm_nif *nip; + struct sigmgr *smp; + Atm_endpoint *epp; + int len = aip->air_buf_len; + int err = 0; + + switch (aip->air_opcode) { + + case AIOCS_INF_VST: + case AIOCS_INF_CFG: + /* + * Get vendor interface information + */ + if (aip->air_vinfo_intf[0] != '\0') { + /* + * Interface specified + */ + if ((pip = atm_pifname(aip->air_vinfo_intf))) { + err = (*pip->pif_ioctl)(aip->air_opcode, data, + (caddr_t)pip); + } else { + err = ENXIO; + } + } else { + /* + * Want info for every interface + */ + for (pip = atm_interface_head; pip; + pip = pip->pif_next) { + err = (*pip->pif_ioctl)(aip->air_opcode, data, + (caddr_t)pip); + if (err) + break; + } + } + break; + + case AIOCS_INF_IPM: + /* + * Get IP Map information + */ + epp = atm_endpoints[ENDPT_IP]; + if (epp) { + err = (*epp->ep_ioctl) (AIOCS_INF_IPM, data, NULL); + } else { + err = ENOPROTOOPT; + } + break; + + case AIOCS_INF_ARP: + /* + * Get ARP table information + */ + for (pip = atm_interface_head; pip; pip = pip->pif_next) { + if (smp = pip->pif_sigmgr) { + err = (*smp->sm_ioctl)(AIOCS_INF_ARP, + data, (caddr_t)pip->pif_siginst); + } + if (err) + break; + } + break; + + case AIOCS_INF_ASV: + /* + * Get ARP server information + */ + if (aip->air_asrv_intf[0] != '\0') { + /* + * Interface specified + */ + if ((nip = atm_nifname(aip->air_asrv_intf))) { + if (smp = nip->nif_pif->pif_sigmgr) { + err = (*smp->sm_ioctl)(AIOCS_INF_ASV, + data, (caddr_t)nip); + } + } else { + err = ENXIO; + } + } else { + /* + * Want info for all arp servers + */ + for (pip = atm_interface_head; pip; + pip = pip->pif_next) { + if (smp = pip->pif_sigmgr) { + err = (*smp->sm_ioctl)(AIOCS_INF_ASV, + data, NULL); + } + if (err) + break; + } + } + break; + + case AIOCS_INF_INT: + /* + * Get physical interface info + */ + if (aip->air_int_intf[0] != '\0') { + /* + * Interface specified + */ + if ((pip = atm_pifname(aip->air_int_intf))) { + err = (*pip->pif_ioctl)(AIOCS_INF_INT, + data, (caddr_t)pip); + } else { + err = ENXIO; + } + } else { + /* + * Want info for every physical interface + */ + for (pip = atm_interface_head; pip; + pip = pip->pif_next) { + err = (*pip->pif_ioctl)(AIOCS_INF_INT, + data, (caddr_t)pip); + if (err) + break; + } + } + break; + + case AIOCS_INF_VCC: + /* + * Get VCC information + */ + if (aip->air_vcc_intf[0] != '\0') { + /* + * Interface specified + */ + if ((pip = atm_pifname(aip->air_vcc_intf))) { + if (smp = pip->pif_sigmgr) { + err = (*smp->sm_ioctl)(AIOCS_INF_VCC, + data, + (caddr_t)pip->pif_siginst); + } + } else { + err = ENXIO; + } + } else { + /* + * Want info for every interface + */ + for (pip = atm_interface_head; pip; + pip = pip->pif_next) { + if (smp = pip->pif_sigmgr) { + err = (*smp->sm_ioctl)(AIOCS_INF_VCC, + data, + (caddr_t)pip->pif_siginst); + } + if (err) + break; + } + } + break; + + case AIOCS_INF_NIF: + /* + * Get network interface info + */ + if (aip->air_int_intf[0] != '\0') { + /* + * Interface specified + */ + if ((nip = atm_nifname(aip->air_int_intf))) { + pip = nip->nif_pif; + err = (*pip->pif_ioctl)(AIOCS_INF_NIF, + data, (caddr_t)nip); + } else { + err = ENXIO; + } + } else { + /* + * Want info for every network interface + */ + for (pip = atm_interface_head; pip; + pip = pip->pif_next) { + for (nip = pip->pif_nif; nip; + nip = nip->nif_pnext) { + err = (*pip->pif_ioctl)(AIOCS_INF_NIF, + data, (caddr_t)nip); + if (err) + break; + } + if (err) + break; + } + } + break; + + case AIOCS_INF_PIS: + /* + * Get physical interface statistics + */ + if (aip->air_physt_intf[0] != '\0') { + /* + * Interface specified + */ + if ((pip = atm_pifname(aip->air_physt_intf))) { + err = (*pip->pif_ioctl)(AIOCS_INF_PIS, + data, (caddr_t)pip); + } else { + err = ENXIO; + } + } else { + /* + * Want statistics for every physical interface + */ + for (pip = atm_interface_head; pip; + pip = pip->pif_next) { + err = (*pip->pif_ioctl)(AIOCS_INF_PIS, + data, (caddr_t)pip); + if (err) + break; + } + } + break; + + case AIOCS_INF_VER: + /* + * Get ATM software version + */ + if (len < sizeof(atm_version)) { + err = ENOSPC; + break; + } + if (err = copyout((caddr_t)&atm_version, + aip->air_buf_addr, + sizeof(atm_version))) { + break; + } + aip->air_buf_addr += sizeof(atm_version); + aip->air_buf_len -= sizeof(atm_version); + break; + + default: + err = EOPNOTSUPP; + } + + /* + * Calculate returned buffer length + */ + aip->air_buf_len = len - aip->air_buf_len; + + return (err); +} + diff --git a/sys/netatm/atm_var.h b/sys/netatm/atm_var.h new file mode 100644 index 0000000..ab5f856 --- /dev/null +++ b/sys/netatm/atm_var.h @@ -0,0 +1,208 @@ +/* + * + * =================================== + * 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: atm_var.h,v 1.9 1998/05/18 19:05:52 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM system variables + * + */ + +#ifndef _NETATM_ATM_VAR_H +#define _NETATM_ATM_VAR_H + + +#ifdef ATM_KERNEL +/* + * Global variable declarations + */ + /* atm_aal5.c */ +#if (defined(__FreeBSD__) && (BSD >= 199506)) +extern struct pr_usrreqs atm_aal5_usrreqs; +#endif + + /* atm_proto.c */ +extern struct domain atmdomain; + + /* atm_subr.c */ +extern struct atm_pif *atm_interface_head; +extern struct atm_ncm *atm_netconv_head; +extern Atm_endpoint *atm_endpoints[]; +extern struct sp_info *atm_pool_head; +extern struct stackq_entry *atm_stackq_head; +extern struct stackq_entry *atm_stackq_tail; +extern struct ifqueue atm_intrq; +#ifdef sgi +extern int atm_intr_index; +#endif +extern struct atm_sock_stat atm_sock_stat; +extern int atm_init; +extern int atm_version; +extern int atm_debug; +extern struct timeval atm_debugtime; +extern int atm_dev_print; +extern int atm_print_data; +extern struct sp_info atm_attributes_pool; + + /* atm_usrreq.c */ +#if (defined(__FreeBSD__) && (BSD >= 199506)) +extern struct pr_usrreqs atm_dgram_usrreqs; +#endif + + +/* + * Global function declarations + */ + /* atm_aal5.c */ +int atm_aal5_ctloutput __P((struct socket *, struct sockopt *)); +void atm_aal5_init __P((void)); + + /* atm_cm.c */ +int atm_cm_connect __P((Atm_endpoint *, void *, Atm_attributes *, + Atm_connection **)); +int atm_cm_listen __P((Atm_endpoint *, void *, Atm_attributes *, + Atm_connection **)); +int atm_cm_addllc __P((Atm_endpoint *, void *, struct attr_llc *, + Atm_connection *, Atm_connection **)); +int atm_cm_addparty __P((Atm_connection *, int, + struct t_atm_sap *)); +int atm_cm_dropparty __P((Atm_connection *, int, + struct t_atm_cause *)); +int atm_cm_release __P((Atm_connection *, struct t_atm_cause *)); +int atm_cm_abort __P((Atm_connvc *, struct t_atm_cause *)); +int atm_cm_incoming __P((struct vccb *, Atm_attributes *)); +void atm_cm_connected __P((Atm_connvc *)); +void atm_cm_cleared __P((Atm_connvc *)); +Atm_connection *atm_cm_match __P((Atm_attributes *, Atm_connection *)); +int atm_cm_cpcs_ctl __P((int, Atm_connection *, void *)); +int atm_cm_cpcs_data __P((Atm_connection *, KBuffer *)); +int atm_cm_saal_ctl __P((int, Atm_connection *, void *)); +int atm_cm_saal_data __P((Atm_connection *, KBuffer *)); +int atm_cm_sscop_ctl __P((int, Atm_connection *, void *, void *)); +int atm_cm_sscop_data __P((Atm_connection *, KBuffer *)); +int atm_endpoint_register __P((Atm_endpoint *)); +int atm_endpoint_deregister __P((Atm_endpoint *)); + + /* atm_device.c */ +int atm_dev_inst __P((struct stack_defn **, Atm_connvc *)); +void atm_dev_lower __P((int, void *, int, int)); +void * atm_dev_alloc __P((u_int, u_int, u_int)); +void atm_dev_free __P((void *)); +#if defined(sun4m) +void * atm_dma_map __P((caddr_t, int, int)); +void atm_dma_free __P((caddr_t, int)); +#endif +KBuffer * atm_dev_compress __P((KBuffer *)); +Cmn_vcc * atm_dev_vcc_find __P((Cmn_unit *, u_int, u_int, u_int)); +void atm_dev_pdu_print __P((Cmn_unit *, Cmn_vcc *, KBuffer *, + char *)); + + /* atm_if.c */ +int atm_physif_register __P((Cmn_unit *, char *, + struct stack_defn *)); +int atm_physif_deregister __P((Cmn_unit *)); +void atm_physif_freenifs __P((struct atm_pif *)); +int atm_netconv_register __P((struct atm_ncm *)); +int atm_netconv_deregister __P((struct atm_ncm *)); +int atm_nif_attach __P((struct atm_nif *)); +void atm_nif_detach __P((struct atm_nif *)); +int atm_nif_setaddr __P((struct atm_nif *, struct ifaddr *)); +#if (defined(BSD) && (BSD >= 199103)) +int atm_ifoutput __P((struct ifnet *, KBuffer *, + struct sockaddr *, struct rtentry *)); +#else +int atm_ifoutput __P((struct ifnet *, KBuffer *, + struct sockaddr *)); +#endif +struct atm_pif * + atm_pifname __P((char *)); +struct atm_nif * + atm_nifname __P((char *)); + + /* atm_proto.c */ +#if (defined(__FreeBSD__) && (BSD >= 199506)) +int atm_proto_notsupp1 __P((struct socket *)); +int atm_proto_notsupp2 __P((struct socket *, struct sockaddr *, + struct proc *)); +int atm_proto_notsupp3 __P((struct socket *, struct sockaddr **)); +int atm_proto_notsupp4 __P((struct socket *, int, KBuffer *, + struct sockaddr *, KBuffer *, struct proc *)); +#endif + + /* atm_signal.c */ +int atm_sigmgr_register __P((struct sigmgr *)); +int atm_sigmgr_deregister __P((struct sigmgr *)); +int atm_sigmgr_attach __P((struct atm_pif *, u_char)); +int atm_sigmgr_detach __P((struct atm_pif *)); +int atm_stack_register __P((struct stack_defn *)); +int atm_stack_deregister __P((struct stack_defn *)); +int atm_create_stack __P((Atm_connvc *, struct stack_list *, + void (*)__P((int, void *, int, int)) )); + + /* atm_socket.c */ +int atm_sock_attach __P((struct socket *, u_long, u_long)); +int atm_sock_detach __P((struct socket *)); +int atm_sock_bind __P((struct socket *, struct sockaddr *)); +int atm_sock_listen __P((struct socket *, Atm_endpoint *)); +int atm_sock_connect __P((struct socket *, struct sockaddr *, + Atm_endpoint *)); +int atm_sock_disconnect __P((struct socket *)); +int atm_sock_sockaddr __P((struct socket *, struct sockaddr **)); +int atm_sock_peeraddr __P((struct socket *, struct sockaddr **)); +int atm_sock_setopt __P((struct socket *, struct sockopt *, + Atm_pcb *)); +int atm_sock_getopt __P((struct socket *, struct sockopt *, + Atm_pcb *)); +void atm_sock_connected __P((void *)); +void atm_sock_cleared __P((void *, struct t_atm_cause *)); + + /* atm_subr.c */ +void atm_initialize __P((void)); +void * atm_allocate __P((struct sp_info *)); +void atm_free __P((void *)); +void atm_release_pool __P((struct sp_info *)); +void atm_timeout __P((struct atm_time *, int, + void (*) __P((struct atm_time *)) )); +int atm_untimeout __P((struct atm_time *)); +int atm_stack_enq __P((int, void (*) __P((int, void *, int, int)), + void *, Atm_connvc *, int, int)); +void atm_stack_drain __P((void)); +void atm_intr __P((void)); +void atm_pdu_print __P((KBuffer *, char *)); + + /* atm_usrreq.c */ +#if (!(defined(__FreeBSD__) && (BSD >= 199506))) +int atm_dgram_usrreq __P((struct socket *, int, KBuffer *, + KBuffer *, KBuffer *)); +#endif + +#endif /* ATM_KERNEL */ + +#endif /* _NETATM_ATM_VAR_H */ diff --git a/sys/netatm/atm_vc.h b/sys/netatm/atm_vc.h new file mode 100644 index 0000000..1c7c7b0 --- /dev/null +++ b/sys/netatm/atm_vc.h @@ -0,0 +1,89 @@ +/* + * + * =================================== + * 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: atm_vc.h,v 1.6 1998/02/19 20:00:34 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Virtual Channel definitions + * + */ + +#ifndef _NETATM_ATM_VC_H +#define _NETATM_ATM_VC_H + + +#ifdef ATM_KERNEL +/* + * ATM Virtual Channel Connection control block. All vccb's are created + * and controlled by an ATM signalling manager. Each ATM signalling + * protocol will also have its own protocol-specific vccb format. Each + * of these protocol vccb's must have this common block at the beginning. + */ +struct vccb { + u_char vc_type; /* VCC type (see below) */ + u_char vc_proto; /* Signalling protocol */ + u_char vc_sstate; /* Signalling state (sigmgr specific) */ + u_char vc_ustate; /* User interface state (see below) */ + struct atm_pif *vc_pif; /* Physical interface */ + struct atm_nif *vc_nif; /* Network interface */ + Qelem_t vc_sigelem; /* Signalling instance vccb queue */ + struct atm_time vc_time; /* Timer controls */ + u_short vc_vpi; /* Virtual Path Identifier */ + u_short vc_vci; /* Virtual Channel Identifier */ + Atm_connvc *vc_connvc; /* CM connection VCC instance */ + long vc_ipdus; /* PDUs received from VCC */ + long vc_opdus; /* PDUs sent to VCC */ + long vc_ibytes; /* Bytes received from VCC */ + long vc_obytes; /* Bytes sent to VCC */ + long vc_ierrors; /* Errors receiving from VCC */ + long vc_oerrors; /* Errors sending to VCC */ + time_t vc_tstamp; /* State transition timestamp */ +}; +#endif /* ATM_KERNEL */ + +/* + * VCC Types + */ +#define VCC_PVC 0x01 /* PVC (Permanent Virtual Channel) */ +#define VCC_SVC 0x02 /* SVC (Switched Virtual Channel) */ +#define VCC_IN 0x04 /* Inbound VCC */ +#define VCC_OUT 0x08 /* Outbound VCC */ + +/* + * VCC Signalling-to-User Interface States + */ +#define VCCU_NULL 0 /* No state */ +#define VCCU_POPEN 1 /* Pending open completion */ +#define VCCU_OPEN 2 /* Connection is open */ +#define VCCU_CLOSED 3 /* Connection has been terminated */ +#define VCCU_ABORT 4 /* Connection being aborted */ + + +#endif /* _NETATM_ATM_VC_H */ diff --git a/sys/netatm/ipatm/ipatm.h b/sys/netatm/ipatm/ipatm.h new file mode 100644 index 0000000..b3ce348 --- /dev/null +++ b/sys/netatm/ipatm/ipatm.h @@ -0,0 +1,55 @@ +/* + * + * =================================== + * 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: ipatm.h,v 1.5 1998/03/24 20:49:49 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Protocol definitions + * + */ + +#ifndef _IPATM_IPATM_H +#define _IPATM_IPATM_H + +/* + * Protocol Variables + */ +#define IPATM_VCIDLE 15 /* VCC idle time (minutes) */ +#define IPATM_ARP_TIME (60 * ATM_HZ) /* Wait for ARP answer */ +#define IPATM_SVC_TIME (60 * ATM_HZ) /* Wait for SVC open answer */ +#define IPATM_IDLE_TIME (60 * ATM_HZ) /* VCC idle timer tick */ + +/* + * IP/ATM LLC/SNAP header + */ +#define IPATM_LLC_LEN 8 +#define IPATM_LLC_HDR {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00} + +#endif /* _IPATM_IPATM_H */ diff --git a/sys/netatm/ipatm/ipatm_event.c b/sys/netatm/ipatm/ipatm_event.c new file mode 100644 index 0000000..048edf9 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_event.c @@ -0,0 +1,454 @@ +/* + * + * =================================== + * 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: ipatm_event.c,v 1.8 1998/08/06 18:21:13 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * IP VCC event handler + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_event.c,v 1.8 1998/08/06 18:21:13 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +/* + * Process an IP VCC timeout + * + * Called when a previously scheduled ipvcc control block timer expires. + * Processing will be based on the current ipvcc state. + * + * Called at splnet. + * + * Arguments: + * tip pointer to ipvcc timer control block + * + * Returns: + * none + * + */ +void +ipatm_timeout(tip) + struct atm_time *tip; +{ + struct ipvcc *ivp; + + /* + * Back-off to ipvcc control block + */ + ivp = (struct ipvcc *) + ((caddr_t)tip - (int)(&((struct ipvcc *)0)->iv_time)); + + /* + * Process timeout based on protocol state + */ + switch (ivp->iv_state) { + + case IPVCC_PMAP: + /* + * Give up waiting for arp response + */ + (void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE); + break; + + case IPVCC_POPEN: + case IPVCC_PACCEPT: + /* + * Give up waiting for signalling manager response + */ + (void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE); + break; + + case IPVCC_ACTPENT: + /* + * Try again to get an ARP entry + */ + switch ((*ivp->iv_ipnif->inf_serv->is_arp_pvcopen)(ivp)) { + + case MAP_PROCEEDING: + /* + * Wait for answer + */ + ivp->iv_state = IPVCC_ACTIVE; + break; + + case MAP_VALID: + /* + * We've got our answer already + */ + ivp->iv_state = IPVCC_ACTIVE; + ivp->iv_flags |= IVF_MAPOK; + ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr; + break; + + case MAP_FAILED: + /* + * Try again later + */ + IPVCC_TIMER(ivp, 5 * ATM_HZ); + break; + + default: + panic("ipatm_timeout: invalid am_pvcopen return"); + } + break; + + default: + log(LOG_ERR, "ipatm: invalid timer state: ivp=0x%x, state=%d\n", + (int)ivp, ivp->iv_state); + } +} + + +/* + * Process IP VCC Connected Notification + * + * Arguments: + * toku owner's connection token (ipvcc protocol block) + * + * Returns: + * none + * + */ +void +ipatm_connected(toku) + void *toku; +{ + struct ipvcc *ivp = (struct ipvcc *)toku; + + /* + * SVC is connected + */ + if ((ivp->iv_state != IPVCC_POPEN) && + (ivp->iv_state != IPVCC_PACCEPT)) { + log(LOG_ERR, "ipatm: invalid CALL_CONNECTED state=%d\n", + ivp->iv_state); + return; + } + + /* + * Verify possible negotiated parameter values + */ + if (ivp->iv_state == IPVCC_POPEN) { + Atm_attributes *ap = &ivp->iv_conn->co_connvc->cvc_attr; + int mtu = (ivp->iv_flags & IVF_LLC) ? + ATM_NIF_MTU + IPATM_LLC_LEN : + ATM_NIF_MTU; + + /* + * Verify final MTU + */ + if (ap->aal.type == ATM_AAL5) { + if ((ap->aal.v.aal5.forward_max_SDU_size < mtu) || + (ap->aal.v.aal5.backward_max_SDU_size > mtu)) { + (void) ipatm_closevc(ivp, + T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED); + return; + } + } else { + if ((ap->aal.v.aal4.forward_max_SDU_size < mtu) || + (ap->aal.v.aal4.backward_max_SDU_size > mtu)) { + (void) ipatm_closevc(ivp, + T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED); + return; + } + } + } + + /* + * Finish up VCC activation + */ + ipatm_activate(ivp); +} + + +/* + * Process IP VCC Cleared Notification + * + * Arguments: + * toku owner's connection token (ipvcc protocol block) + * cause pointer to cause code + * + * Returns: + * none + * + */ +void +ipatm_cleared(toku, cause) + void *toku; + struct t_atm_cause *cause; +{ + struct ipvcc *ivp = (struct ipvcc *)toku; + + + /* + * VCC has been cleared, so figure out what's next + */ + ivp->iv_conn = NULL; + + switch (ivp->iv_state) { + + case IPVCC_POPEN: + /* + * Call setup failed, see if there is another + * set of vcc parameters to try + */ + ivp->iv_state = IPVCC_CLOSED; + if (ipatm_retrysvc(ivp)) { + (void) ipatm_closevc(ivp, cause->cause_value); + } + break; + + case IPVCC_PACCEPT: + case IPVCC_ACTPENT: + case IPVCC_ACTIVE: + ivp->iv_state = IPVCC_CLOSED; + (void) ipatm_closevc(ivp, cause->cause_value); + break; + } +} + + +/* + * Process an ARP Event Notification + * + * Arguments: + * ivp pointer to IP VCC control block + * event ARP event type + * + * Returns: + * none + * + */ +void +ipatm_arpnotify(ivp, event) + struct ipvcc *ivp; + int event; +{ + struct sockaddr_in sin; + struct ifnet *ifp; + + /* + * Process event + */ + switch (event) { + + case MAP_VALID: + switch (ivp->iv_state) { + + case IPVCC_PMAP: + /* + * We've got our destination, however, first we'll + * check to make sure no other VCC to our destination + * has also had it's ARP table entry completed. + * If we don't find a better VCC to use, then we'll + * go ahead and open this SVC. + */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ivp->iv_dst.s_addr; + if (ipatm_iptovc(&sin, ivp->iv_ipnif->inf_nif) != ivp) { + /* + * We found a better VCC, so use it and + * get rid of this VCC + */ + if (ivp->iv_queue) { + ifp = (struct ifnet *) + ivp->iv_ipnif->inf_nif; + (void) ipatm_ifoutput(ifp, + ivp->iv_queue, + (struct sockaddr *)&sin); + ivp->iv_queue = NULL; + } + (void) ipatm_closevc(ivp, + T_ATM_CAUSE_UNSPECIFIED_NORMAL); + + } else { + /* + * We like this VCC... + */ + ivp->iv_flags |= IVF_MAPOK; + if (ipatm_opensvc(ivp)) { + (void) ipatm_closevc(ivp, + T_ATM_CAUSE_TEMPORARY_FAILURE); + } + } + break; + + case IPVCC_POPEN: + case IPVCC_PACCEPT: + case IPVCC_ACTIVE: + /* + * Everything looks good, so accept new mapping + */ + ivp->iv_flags |= IVF_MAPOK; + ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr; + + /* + * Send queued packet + */ + if ((ivp->iv_state == IPVCC_ACTIVE) && ivp->iv_queue) { + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ivp->iv_dst.s_addr; + ifp = (struct ifnet *)ivp->iv_ipnif->inf_nif; + (void) ipatm_ifoutput(ifp, ivp->iv_queue, + (struct sockaddr *)&sin); + ivp->iv_queue = NULL; + } + break; + } + break; + + case MAP_INVALID: + switch (ivp->iv_state) { + + case IPVCC_POPEN: + case IPVCC_PACCEPT: + case IPVCC_ACTIVE: + + /* + * Mapping has gone stale, so we cant use this VCC + * until the mapping is refreshed + */ + ivp->iv_flags &= ~IVF_MAPOK; + break; + } + break; + + case MAP_FAILED: + /* + * ARP lookup failed, just trash it all + */ + (void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE); + break; + + case MAP_CHANGED: + /* + * ARP mapping has changed + */ + if (ivp->iv_flags & IVF_PVC) { + /* + * For PVCs, just reset lookup cache if needed + */ + if (last_map_ipvcc == ivp) { + last_map_ipdst = 0; + last_map_ipvcc = NULL; + } + } else { + /* + * Close SVC if it has already used this mapping + */ + switch (ivp->iv_state) { + + case IPVCC_POPEN: + case IPVCC_ACTIVE: + (void) ipatm_closevc(ivp, + T_ATM_CAUSE_UNSPECIFIED_NORMAL); + break; + } + } + break; + + default: + log(LOG_ERR, "ipatm: unknown arp event %d, ivp=0x%x\n", + event, (int)ivp); + } +} + + +/* + * Process an IP VCC idle timer tick + * + * This function is called every IPATM_IDLE_TIME seconds, in order to + * scan for idle IP VCC's. If an active VCC reaches the idle time limit, + * then it will be closed. + * + * Called at splnet. + * + * Arguments: + * tip pointer to the VCC idle timer control block + * + * Returns: + * none + * + */ +void +ipatm_itimeout(tip) + struct atm_time *tip; +{ + struct ipvcc *ivp, *inext; + struct ip_nif *inp; + + + /* + * Schedule next timeout + */ + atm_timeout(&ipatm_itimer, IPATM_IDLE_TIME, ipatm_itimeout); + + /* + * Check for disabled idle timeout + */ + if (ipatm_vcidle == 0) + return; + + /* + * Check out all IP VCCs + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp; + ivp = inext) { + + inext = Q_NEXT(ivp, struct ipvcc, iv_elem); + + /* + * Looking for active, idle SVCs + */ + if (ivp->iv_flags & (IVF_PVC | IVF_NOIDLE)) + continue; + if (ivp->iv_state != IPVCC_ACTIVE) + continue; + if (++ivp->iv_idle < ipatm_vcidle) + continue; + + /* + * OK, we found one - close the VCC + */ + (void) ipatm_closevc(ivp, + T_ATM_CAUSE_UNSPECIFIED_NORMAL); + } + } +} + diff --git a/sys/netatm/ipatm/ipatm_if.c b/sys/netatm/ipatm/ipatm_if.c new file mode 100644 index 0000000..ede2450 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_if.c @@ -0,0 +1,335 @@ +/* + * + * =================================== + * 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: ipatm_if.c,v 1.6 1998/03/24 20:51:47 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Interface Manager + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_if.c,v 1.6 1998/03/24 20:51:47 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +/* + * Local functions + */ +static void ipatm_closenif __P((struct ip_nif *)); + + +/* + * Process Network Interface status change + * + * Called whenever a network interface status change is requested. + * + * Called at splnet. + * + * Arguments: + * cmd command code + * nip pointer to atm network interface control block + * arg command specific parameter + * + * Returns: + * 0 command successful + * errno command failed - reason indicated + * + */ +int +ipatm_nifstat(cmd, nip, arg) + int cmd; + struct atm_nif *nip; + int arg; +{ + struct in_ifaddr *ia; + struct siginst *sip; + struct ip_nif *inp; + int err = 0; + + /* + * Look for corresponding IP interface + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + + /* + * Process command + */ + switch (cmd) { + + case NCM_ATTACH: + /* + * Make sure i/f isn't already attached + */ + if (inp != NULL) { + err = EEXIST; + break; + } + + /* + * Get a new interface block + */ + inp = (struct ip_nif *)atm_allocate(&ipatm_nifpool); + if (inp == NULL) { + err = ENOMEM; + break; + } + inp->inf_nif = nip; + inp->inf_state = IPNIF_ADDR; + inp->inf_arpnotify = ipatm_arpnotify; + inp->inf_ipinput = ipatm_ipinput; + inp->inf_createsvc = ipatm_createsvc; + LINK2TAIL(inp, struct ip_nif, ipatm_nif_head, inf_next); + break; + + case NCM_DETACH: + /* + * Make sure i/f is attached + */ + if (inp == NULL) { + err = ENODEV; + break; + } + + /* + * Validate interface stuff + */ + if (Q_HEAD(inp->inf_vcq, struct ipvcc)) + panic("ipatm_nifstat: ipvcc queue not empty"); + + /* + * If we're active, close all our VCCs and tell the + * interface service about the deactivation + */ + if (inp->inf_state == IPNIF_ACTIVE) { + + ipatm_closenif(inp); + + if (inp->inf_serv) + (void) (*inp->inf_serv->is_ifdact)(inp); + } + + /* + * Clean up and free block + */ + UNLINK(inp, struct ip_nif, ipatm_nif_head, inf_next); + atm_free((caddr_t)inp); + break; + + case NCM_SETADDR: + /* + * We only care about IP addresses + */ +#if (defined(BSD) && (BSD >= 199103)) + if (((struct ifaddr *)arg)->ifa_addr->sa_family != AF_INET) +#else + if (((struct ifaddr *)arg)->ifa_addr.sa_family != AF_INET) +#endif + break; + + /* + * Make sure i/f is there + */ + ia = (struct in_ifaddr *)arg; + if (inp == NULL) + panic("ipatm_nifstat: setaddr missing ip_nif"); + + /* + * Process new address + */ + switch (inp->inf_state) { + + case IPNIF_SIGMGR: + case IPNIF_ADDR: + inp->inf_addr = ia; + + /* + * If signalling manager is not set, wait for it + */ + sip = nip->nif_pif->pif_siginst; + if (sip == NULL) { + inp->inf_state = IPNIF_SIGMGR; + break; + } + + /* + * Otherwise, everything's set + */ + inp->inf_state = IPNIF_ACTIVE; + + /* + * Tell interface service we're around + */ + if (sip->si_ipserv) { + inp->inf_serv = sip->si_ipserv; + err = (*inp->inf_serv->is_ifact)(inp); + } + + /* + * Reset state if there's been a problem + */ + if (err) { + inp->inf_serv = NULL; + inp->inf_addr = NULL; + inp->inf_state = IPNIF_ADDR; + } + break; + + case IPNIF_ACTIVE: + /* + * We dont support an address change + */ + err = EEXIST; + break; + } + break; + + case NCM_SIGATTACH: + /* + * Make sure i/f is attached + */ + if (inp == NULL) { + err = ENODEV; + break; + } + + /* + * Are we waiting for the sigmgr attach?? + */ + if (inp->inf_state != IPNIF_SIGMGR) { + /* + * No, nothing else to do + */ + break; + } + + /* + * OK, everything's set + */ + inp->inf_state = IPNIF_ACTIVE; + + /* + * Tell interface service we're around + */ + sip = nip->nif_pif->pif_siginst; + if (sip->si_ipserv) { + inp->inf_serv = sip->si_ipserv; + err = (*inp->inf_serv->is_ifact)(inp); + } + + /* + * Just report any problems, since a NCM_SIGDETACH will + * be coming down immediately + */ + break; + + case NCM_SIGDETACH: + /* + * Make sure i/f is attached + */ + if (inp == NULL) { + err = ENODEV; + break; + } + + /* + * Are we currently active?? + */ + if (inp->inf_state != IPNIF_ACTIVE) { + /* + * No, nothing else to do + */ + break; + } + + /* + * Close all the IP VCCs for this interface + */ + ipatm_closenif(inp); + + /* + * Tell interface service that i/f has gone down + */ + if (inp->inf_serv) + (void) (*inp->inf_serv->is_ifdact)(inp); + + /* + * Just have to wait for another sigattach + */ + inp->inf_serv = NULL; + inp->inf_state = IPNIF_SIGMGR; + break; + + default: + log(LOG_ERR, "ipatm_nifstat: unknown command %d\n", cmd); + } + + return (err); +} + + +/* + * Close all VCCs on a Network Interface + * + * Called at splnet. + * + * Arguments: + * inp pointer to IP network interface + * + * Returns: + * none + * + */ +static void +ipatm_closenif(inp) + struct ip_nif *inp; +{ + struct ipvcc *ivp, *inext; + + /* + * Close each IP VCC on this interface + */ + for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp; ivp = inext) { + + inext = Q_NEXT(ivp, struct ipvcc, iv_elem); + + (void) ipatm_closevc(ivp, T_ATM_CAUSE_UNSPECIFIED_NORMAL); + } +} + diff --git a/sys/netatm/ipatm/ipatm_input.c b/sys/netatm/ipatm/ipatm_input.c new file mode 100644 index 0000000..ca3e3e7 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_input.c @@ -0,0 +1,210 @@ +/* + * + * =================================== + * 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: ipatm_input.c,v 1.9 1998/04/07 23:03:52 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Process stack and data input + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_input.c,v 1.9 1998/04/07 23:03:52 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +/* + * Process VCC Input Data + * + * Arguments: + * tok ipatm connection token (pointer to ipvcc) + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +void +ipatm_cpcs_data(tok, m) + void *tok; + KBuffer *m; +{ + struct ipvcc *ivp = tok; + +#ifdef DIAGNOSTIC + if (ipatm_print) { + atm_pdu_print(m, "ipatm_input"); + } +#endif + + /* + * Handle input packet + */ + if (ivp->iv_state != IPVCC_ACTIVE) { + KB_FREEALL(m); + ipatm_stat.ias_rcvstate++; + return; + } + + /* + * IP packet - reset idle timer + */ + ivp->iv_idle = 0; + + /* + * Pass packet to IP + */ + (void) ipatm_ipinput(ivp->iv_ipnif, m); +} + + +/* + * IP Input Packet Handler + * + * All IP packets received from various ATM sources will be sent here + * for final queuing to the IP layer. + * + * Arguments: + * inp pointer to packet's receiving IP network interface + * m pointer to packet buffer chain + * + * Returns: + * 0 packet successfully queued to IP layer + * else error queuing packet, buffer chain freed + * + */ +int +ipatm_ipinput(inp, m) + struct ip_nif *inp; + KBuffer *m; +{ + int s, space; + +#ifdef DIAGNOSTIC + if (ipatm_print) { + atm_pdu_print(m, "ipatm_ipinput"); + } +#endif + +#if defined(BSD) +#if BSD >= 199103 + +#ifdef DIAGNOSTIC + if (!KB_ISPKT(m)) { + panic("ipatm_ipinput: no packet header"); + } + { + int cnt = 0; + KBuffer *m0 = m; + + while (m0) { + cnt += KB_LEN(m0); + m0 = KB_NEXT(m0); + } + if (m->m_pkthdr.len != cnt) { + panic("ipatm_ipinput: packet length incorrect"); + } + } +#endif + /* + * Save the input ifnet pointer in the packet header + */ + m->m_pkthdr.rcvif = (struct ifnet *)inp->inf_nif; + +#else /* ! BSD >= 199103 */ + /* + * Stick ifnet pointer onto front of packet - hopefully + * there'll be room in the first buffer. + */ + KB_HEADROOM(m, space); + if (space < sizeof(struct ifnet *)) { + KBuffer *n; + + /* + * We have to allocate another buffer and tack it + * onto the front of the packet + */ + KB_ALLOCPKT(n, sizeof(struct ifnet *), + KB_F_NOWAIT, KB_T_HEADER); + if (n == 0) { + KB_FREEALL(m); + ipatm_stat.ias_rcvnobuf++; + return (1); + } + KB_LEN(n) = sizeof(struct ifnet *); + KB_LINKHEAD(n, m); + m = n; + } else { + /* + * Header fits, just adjust buffer controls + */ + KB_HEADADJ(m, sizeof(struct ifnet *)); + } + { + struct ifnet **p; + + KB_DATASTART(m, p, struct ifnet **); + *p = (struct ifnet *)inp->inf_nif; + } +#endif /* ! BSD >= 199103 */ + + /* + * Finally, hand packet off to IP. + * + * NB: Since we're already in the softint kernel state, we + * just call IP directly to avoid the extra unnecessary + * kernel scheduling. + */ + s = splimp(); + if (IF_QFULL(&ipintrq)) { + IF_DROP(&ipintrq); + (void) splx(s); + KB_FREEALL(m); + return (1); + } + + IF_ENQUEUE(&ipintrq, m); + (void) splx(s); +#if BSD < 199506 + ipintr(); +#else + schednetisr ( NETISR_IP ); +#endif /* BSD >= 199506 */ +#endif /* defined(BSD) */ + + return (0); +} + diff --git a/sys/netatm/ipatm/ipatm_load.c b/sys/netatm/ipatm/ipatm_load.c new file mode 100644 index 0000000..8caa635 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_load.c @@ -0,0 +1,878 @@ +/* + * + * =================================== + * 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: ipatm_load.c,v 1.12 1998/07/30 22:23:00 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Support for running as a loadable kernel module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_load.c,v 1.12 1998/07/30 22:23:00 mks Exp $"; +#endif + +#ifndef ATM_IP_MODULE +#include "opt_atm.h" +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +/* + * Global variables + */ +int ipatm_vccnt = 0; +int ipatm_vcidle = IPATM_VCIDLE; +int ipatm_print = 0; +u_long last_map_ipdst = 0; +struct ipvcc* last_map_ipvcc = NULL; + +struct ip_nif *ipatm_nif_head = NULL; + +struct ipatm_stat ipatm_stat = {0}; + +struct atm_time ipatm_itimer = {0, 0}; /* VCC idle timer */ + +Atm_endpoint ipatm_endpt = { + NULL, + ENDPT_IP, + ipatm_ioctl, + ipatm_getname, + ipatm_connected, + ipatm_cleared, + ipatm_incoming, + NULL, + NULL, + NULL, + ipatm_cpcs_data, + NULL, + NULL, + NULL, + NULL +}; + +struct sp_info ipatm_vcpool = { + "ipatm vcc pool", /* si_name */ + sizeof(struct ipvcc), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + +struct sp_info ipatm_nifpool = { + "ipatm nif pool", /* si_name */ + sizeof(struct ip_nif), /* si_blksiz */ + 5, /* si_blkcnt */ + 20 /* si_maxallow */ +}; + + +/* + * Local functions + */ +static int ipatm_start __P((void)); +static int ipatm_stop __P((void)); + + +/* + * Local variables + */ +static struct atm_ncm ipatm_ncm = { + NULL, + AF_INET, + ipatm_ifoutput, + ipatm_nifstat +}; + +static struct ipatm_listener { + Atm_attributes attr; + Atm_connection *conn; +} ipatm_listeners[] = { +{ + { NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL5 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_ANY + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_PRESENT, + T_ATM_ABSENT, + { + { + T_ATM_SIMPLE_ID, + }, + { + T_ATM_ABSENT + } + } + }, + { /* llc */ + T_ATM_PRESENT, + { + T_ATM_LLC_SHARING, + IPATM_LLC_LEN, + IPATM_LLC_HDR + } + }, + { /* called */ + T_ATM_ANY + }, + { /* calling */ + T_ATM_ANY + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ANY + }, + { /* cause */ + T_ATM_ABSENT + }, + }, + NULL +}, +{ + { NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL5 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_ANY + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_ABSENT, + T_ATM_ABSENT + }, + { /* llc */ + T_ATM_ABSENT + }, + { /* called */ + T_ATM_ANY + }, + { /* calling */ + T_ATM_ANY + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ANY + }, + { /* cause */ + T_ATM_ABSENT + }, + }, + NULL +}, +{ + { NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL3_4 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_ANY + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_ABSENT, + T_ATM_ABSENT + }, + { /* llc */ + T_ATM_ABSENT + }, + { /* called */ + T_ATM_ANY + }, + { /* calling */ + T_ATM_ANY + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ANY + }, + { /* cause */ + T_ATM_ABSENT + }, + }, + NULL +}, +}; + +static struct t_atm_cause ipatm_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_UNSPECIFIED_NORMAL, + {0, 0, 0, 0} +}; + + +/* + * Initialize ipatm processing + * + * This will be called during module loading. We'll just register + * ourselves and wait for the packets to start flying. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +ipatm_start() +{ + struct atm_pif *pip; + struct atm_nif *nip; + int err, s, i; + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: ipatm=%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); + } + + /* + * Register ourselves as a network convergence module + */ + err = atm_netconv_register(&ipatm_ncm); + if (err) + goto done; + + /* + * Register ourselves as an ATM endpoint + */ + err = atm_endpoint_register(&ipatm_endpt); + if (err) + goto done; + + /* + * Get current system configuration + */ + s = splnet(); + for (pip = atm_interface_head; pip; pip = pip->pif_next) { + /* + * Process each network interface + */ + for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) { + struct ifnet *ifp = (struct ifnet *)nip; + struct in_ifaddr *ia; + + /* + * Attach interface + */ + err = ipatm_nifstat(NCM_ATTACH, nip, 0); + if (err) { + (void) splx(s); + goto done; + } + + /* + * If IP address has been set, register it + */ + TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { + if (ia->ia_ifp == ifp) + break; + } + if (ia) { + err = ipatm_nifstat(NCM_SETADDR, nip, (int)ia); + if (err) { + (void) splx(s); + goto done; + } + } + } + } + (void) splx(s); + + /* + * Fill in union fields + */ + ipatm_aal5llc.aal.v.aal5.forward_max_SDU_size = + ATM_NIF_MTU + IPATM_LLC_LEN; + ipatm_aal5llc.aal.v.aal5.backward_max_SDU_size = + ATM_NIF_MTU + IPATM_LLC_LEN; + ipatm_aal5llc.aal.v.aal5.SSCS_type = T_ATM_NULL; + ipatm_aal5llc.blli.v.layer_2_protocol.ID.simple_ID = T_ATM_BLLI2_I8802; + + ipatm_aal5null.aal.v.aal5.forward_max_SDU_size = ATM_NIF_MTU; + ipatm_aal5null.aal.v.aal5.backward_max_SDU_size = ATM_NIF_MTU; + ipatm_aal5null.aal.v.aal5.SSCS_type = T_ATM_NULL; + + ipatm_aal4null.aal.v.aal4.forward_max_SDU_size = ATM_NIF_MTU; + ipatm_aal4null.aal.v.aal4.backward_max_SDU_size = ATM_NIF_MTU; + ipatm_aal4null.aal.v.aal4.SSCS_type = T_ATM_NULL; + ipatm_aal4null.aal.v.aal4.mid_low = 0; + ipatm_aal4null.aal.v.aal4.mid_high = 1023; + + /* + * Listen for incoming calls + */ + for (i = 0; + i < (sizeof(ipatm_listeners) / sizeof(struct ipatm_listener)); + i++) { + struct attr_aal *aalp = &ipatm_listeners[i].attr.aal; + int maxsdu = ATM_NIF_MTU; + + /* + * Fill in union fields + */ + if (ipatm_listeners[i].attr.blli.tag_l2 == T_ATM_PRESENT) { + struct t_atm_blli *bp = &ipatm_listeners[i].attr.blli.v; + + bp->layer_2_protocol.ID.simple_ID = T_ATM_BLLI2_I8802; + maxsdu += IPATM_LLC_LEN; + } + if (aalp->type == ATM_AAL5) { + aalp->v.aal5.forward_max_SDU_size = maxsdu; + aalp->v.aal5.backward_max_SDU_size = maxsdu; + aalp->v.aal5.SSCS_type = T_ATM_NULL; + } else { + aalp->v.aal4.forward_max_SDU_size = maxsdu; + aalp->v.aal4.backward_max_SDU_size = maxsdu; + aalp->v.aal4.SSCS_type = T_ATM_NULL; + aalp->v.aal4.mid_low = 0; + aalp->v.aal4.mid_high = 1023; + } + + /* + * Now start listening + */ + if (err = atm_cm_listen(&ipatm_endpt, (void *)i, + &ipatm_listeners[i].attr, + &ipatm_listeners[i].conn)) + goto done; + } + + /* + * Start background VCC idle timer + */ + atm_timeout(&ipatm_itimer, IPATM_IDLE_TIME, ipatm_itimeout); + +done: + return (err); +} + + +/* + * Halt ipatm processing + * + * This will be called just prior to unloading the module from + * memory. All IP VCCs must be terminated before the protocol can + * be shutdown. + * + * Arguments: + * none + * + * Returns: + * 0 shutdown was successful + * errno shutdown failed - reason indicated + * + */ +static int +ipatm_stop() +{ + struct ip_nif *inp; + int err = 0, i; + int s = splnet(); + + /* + * Any VCCs still open?? + */ + if (ipatm_vccnt) { + + /* Yes, can't stop now */ + err = EBUSY; + goto done; + } + + /* + * Kill VCC idle timer + */ + (void) atm_untimeout(&ipatm_itimer); + + /* + * Stop listening for incoming calls + */ + for (i = 0; + i < (sizeof(ipatm_listeners) / sizeof(struct ipatm_listener)); + i++) { + if (ipatm_listeners[i].conn != NULL) { + (void) atm_cm_release(ipatm_listeners[i].conn, + &ipatm_cause); + } + } + + /* + * Detach all our interfaces + */ + while (inp = ipatm_nif_head) { + (void) ipatm_nifstat(NCM_DETACH, inp->inf_nif, 0); + } + + /* + * De-register from system + */ + (void) atm_netconv_deregister(&ipatm_ncm); + (void) atm_endpoint_deregister(&ipatm_endpt); + + /* + * Free up our storage pools + */ + atm_release_pool(&ipatm_vcpool); + atm_release_pool(&ipatm_nifpool); + +done: + (void) splx(s); + return (err); +} + + +#ifdef ATM_IP_MODULE +/* + ******************************************************************* + * + * Loadable Module Support + * + ******************************************************************* + */ +static int ipatm_doload __P((void)); +static int ipatm_dounload __P((void)); + +/* + * 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 +ipatm_doload() +{ + int err = 0; + + /* + * Start us up + */ + err = ipatm_start(); + if (err) + /* Problems, clean up */ + (void)ipatm_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 +ipatm_dounload() +{ + int err = 0; + + /* + * OK, try to clean up our mess + */ + err = ipatm_stop(); + + return (err); +} + + +#ifdef sun +/* + * Loadable driver description + */ +struct vdldrv ipatm_drv = { + VDMAGIC_PSEUDO, /* Pseudo Driver */ + "ipatm_mod", /* name */ + NULL, /* 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 +ipatm_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 = ipatm_doload(); + if (err == 0) + /* Let vd driver know about us */ + vdp->vdd_vdtab = (struct vdlinkage *)&ipatm_drv; + break; + + case VDUNLOAD: + /* + * Module Unload + */ + err = ipatm_dounload(); + break; + + case VDSTAT: + /* + * Module Status + */ + + /* Not much to say at the moment */ + + break; + + default: + log(LOG_ERR, "ipatm_mod: Unknown vd command 0x%x\n", cmd); + err = EINVAL; + } + + return (err); +} +#endif /* sun */ + +#ifdef __FreeBSD__ + +#include <sys/exec.h> +#include <sys/sysent.h> +#include <sys/lkm.h> + +/* + * Loadable miscellaneous module description + */ +MOD_MISC(ipatm); + + +/* + * 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 +ipatm_load(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(ipatm_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 +ipatm_unload(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(ipatm_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 +ipatm_mod(lkmtp, cmd, ver) + struct lkm_table *lkmtp; + int cmd; + int ver; +{ + MOD_DISPATCH(ipatm, lkmtp, cmd, ver, + ipatm_load, ipatm_unload, lkm_nullcmd); +} +#endif /* __FreeBSD__ */ + +#else /* !ATM_IP_MODULE */ + +/* + ******************************************************************* + * + * Kernel Compiled Module Support + * + ******************************************************************* + */ +static void ipatm_doload __P((void *)); + +SYSINIT(atmipatm, SI_SUB_PROTO_END, SI_ORDER_ANY, ipatm_doload, NULL) + +/* + * Kernel initialization + * + * Arguments: + * arg Not used + * + * Returns: + * none + * + */ +static void +ipatm_doload(void *arg) +{ + int err = 0; + + /* + * Start us up + */ + err = ipatm_start(); + if (err) { + /* Problems, clean up */ + (void)ipatm_stop(); + + log(LOG_ERR, "IP over ATM unable to initialize (%d)!!\n", err); + } + return; +} +#endif /* ATM_IP_MODULE */ + diff --git a/sys/netatm/ipatm/ipatm_output.c b/sys/netatm/ipatm/ipatm_output.c new file mode 100644 index 0000000..7f02f28 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_output.c @@ -0,0 +1,216 @@ +/* + * + * =================================== + * 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: ipatm_output.c,v 1.6 1998/02/19 20:14:17 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Output IP packets across an ATM VCC + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_output.c,v 1.6 1998/02/19 20:14:17 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +/* + * Output an IP Packet + * + * All IP packets output to an ATM interface will be directed here via + * the atm_ifoutput() function. If there is an ATM VCC already setup for + * the destination IP address, then we'll just send the packet to that VCC. + * Otherwise we will have to setup a new VCC, ARPing for the corresponding + * destination ATM hardware address along the way. + * + * Arguments: + * ifp pointer to ifnet structure + * m pointer to packet buffer chain to be output + * dst pointer to packet's IP destination address + * + * Returns: + * 0 packet "output" was successful + * errno output failed - reason indicated + * + */ +int +ipatm_ifoutput(ifp, m, dst) + struct ifnet *ifp; + KBuffer *m; + struct sockaddr *dst; +{ + struct ipvcc *ivp; + int err = 0; + +#ifdef DIAGNOSTIC + if (ipatm_print) { + atm_pdu_print(m, "ipatm_ifoutput"); + } +#endif + + /* + * See if we've already got an appropriate VCC + */ + ivp = ipatm_iptovc((struct sockaddr_in *)dst, (struct atm_nif *)ifp); + if (ivp) { + + /* + * Reset idle timer + */ + ivp->iv_idle = 0; + + /* + * Can we use this VCC now?? + */ + if ((ivp->iv_state == IPVCC_ACTIVE) && + (ivp->iv_flags & IVF_MAPOK)) { + + /* + * OK, now send packet + */ + err = atm_cm_cpcs_data(ivp->iv_conn, m); + if (err) { + /* + * Output problem, drop packet + */ + KB_FREEALL(m); + } + } else { + + /* + * VCC is unavailable for data packets. Queue packet + * for now, but only maintain a queue length of one. + */ + if (ivp->iv_queue) + KB_FREEALL(ivp->iv_queue); + + ivp->iv_queue = m; + } + } else { + struct in_ifaddr *ia; +#if (defined(BSD) && (BSD < 199306)) + extern struct ifnet loif; +#endif + + /* + * No VCC to destination + */ + + /* + * Is packet for our interface address? + */ + TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { + if (ia->ia_ifp != ifp) + continue; + if (((struct sockaddr_in *)dst)->sin_addr.s_addr == + IA_SIN(ia)->sin_addr.s_addr) { + + /* + * It's for us - hand packet to loopback driver + */ + (void) if_simloop(ifp, m, dst, 0); + goto done; + } + } + + /* + * Is this a broadcast packet ?? + */ +#if (defined(BSD) && (BSD >= 199306)) + if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr, ifp)) { +#else + if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) { +#endif + struct ip_nif *inp; + int s; + + /* + * If interface server exists and provides broadcast + * services, then let it deal with this packet + */ + s = splnet(); + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == (struct atm_nif *)ifp) + break; + } + (void) splx(s); + + if ((inp == NULL) || + (inp->inf_serv == NULL) || + (inp->inf_serv->is_bcast_output == NULL)) { + KB_FREEALL(m); + err = EADDRNOTAVAIL; + goto done; + } + + err = (*inp->inf_serv->is_bcast_output)(inp, m); + goto done; + } + + /* + * How about a multicast packet ?? + */ + if (IN_MULTICAST(ntohl(SATOSIN(dst)->sin_addr.s_addr))) { + /* + * Multicast isn't currently supported + */ + KB_FREEALL(m); + err = EADDRNOTAVAIL; + goto done; + } + + /* + * Well, I guess we need to create an SVC to the destination + */ + if ((err = ipatm_createsvc(ifp, AF_INET, + (caddr_t)&((struct sockaddr_in *)dst)->sin_addr, + &ivp)) == 0) { + /* + * SVC open is proceeding, queue packet + */ + ivp->iv_queue = m; + + } else { + /* + * SVC open failed, release buffers and return + */ + KB_FREEALL(m); + } + } + +done: + return (err); +} + diff --git a/sys/netatm/ipatm/ipatm_serv.h b/sys/netatm/ipatm/ipatm_serv.h new file mode 100644 index 0000000..2046213 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_serv.h @@ -0,0 +1,114 @@ +/* + * + * =================================== + * 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: ipatm_serv.h,v 1.6 1998/02/19 20:14:21 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * IP/ATM service interface definitions + * + */ + +#ifndef _IPATM_IPATM_SERV_H +#define _IPATM_IPATM_SERV_H + + +/* + * Structures specifying VCC parameters and pointers to all of the IP + * services offered by an external IP interface service provider. + */ +struct ip_vccparm { + Aal_t ivc_aal; /* AAL type */ + Encaps_t ivc_encaps; /* VCC encapsulation */ +}; + +#define IPATM_VCCPARMS 4 /* Number of parameter lists */ + +struct ip_serv { +/* Interfaces to IP/ATM interface services */ + int (*is_ifact) /* Interface activation */ + __P((struct ip_nif *)); + int (*is_ifdact) /* Interface deactivation */ + __P((struct ip_nif *)); + int (*is_ioctl) /* Interface ioctl */ + __P((int, caddr_t, caddr_t)); + +/* Interfaces to IP/ATM ARP services */ + int (*is_arp_pvcopen) /* IP creating dynamic PVC */ + __P((struct ipvcc *)); + int (*is_arp_svcout) /* IP creating outgoing SVC */ + __P((struct ipvcc *, struct in_addr *)); + int (*is_arp_svcin) /* IP creating incoming SVC */ + __P((struct ipvcc *, Atm_addr *, Atm_addr *)); + int (*is_arp_svcact) /* IP SVC is active */ + __P((struct ipvcc *)); + void (*is_arp_close) /* IP closing VCC */ + __P((struct ipvcc *)); + +/* Interfaces to IP/ATM broadcast services */ + int (*is_bcast_output) /* IP broadcast packet output */ + __P((struct ip_nif *, KBuffer *)); + +/* Interfaces to IP/ATM multicast services */ + +/* Ordered list of parameters to try for IP/ATM VCC connections */ + struct ip_vccparm is_vccparm[IPATM_VCCPARMS]; /* List of vcc params */ +}; + + +/* + * ARP Interface + * ---------------- + */ + +/* + * Common header for IP/ATM ARP mappings. For each IP VCC created, the + * appropriate IP/ATM ARP server must assign one of these structures to + * indicate the address mapping. This is the only IP-visible ARP structure. + * The servers may embed this structure at the beginning of their + * module-specific mappings. + */ +struct arpmap { + struct in_addr am_dstip; /* Destination IP address */ + Atm_addr am_dstatm; /* Destination ATM address */ + Atm_addr am_dstatmsub; /* Destination ATM subaddress */ +}; + + +/* + * is_arp_[ps]open() return codes and ipatm_arpnotify() event types + */ +#define MAP_PROCEEDING 1 /* Lookup is proceeding (open only) */ +#define MAP_VALID 2 /* Mapping is valid */ +#define MAP_INVALID 3 /* Mapping is invalid */ +#define MAP_CHANGED 4 /* Mapping has changed */ +#define MAP_FAILED 5 /* Mapping request has failed */ + + +#endif /* _IPATM_IPATM_SERV_H */ diff --git a/sys/netatm/ipatm/ipatm_usrreq.c b/sys/netatm/ipatm/ipatm_usrreq.c new file mode 100644 index 0000000..1f1751c --- /dev/null +++ b/sys/netatm/ipatm/ipatm_usrreq.c @@ -0,0 +1,394 @@ +/* + * + * =================================== + * 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: ipatm_usrreq.c,v 1.6 1998/05/18 19:14:04 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Process user requests + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_usrreq.c,v 1.6 1998/05/18 19:14:04 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +/* + * Process IP PF_ATM ioctls + * + * Called at splnet. + * + * Arguments: + * code PF_ATM sub-operation code + * data pointer to code specific parameter data area + * arg1 pointer to code specific argument + * + * Returns: + * 0 request procesed + * errno error processing request - reason indicated + * + */ +int +ipatm_ioctl(code, data, arg1) + int code; + caddr_t data; + caddr_t arg1; +{ + struct atmaddreq *aap; + struct atmdelreq *adp; + struct atminfreq *aip; + struct air_ip_vcc_rsp aivr; + struct atm_nif *nip; + struct ip_nif *inp; + struct ipvcc *ivp; + struct vccb *vcp; + struct ipatmpvc pv; + caddr_t cp; + struct in_addr ip; + int space, err = 0; + + + switch (code) { + + case AIOCS_ADD_PVC: + /* + * Add an IP PVC + */ + aap = (struct atmaddreq *)data; + + /* + * Find the IP network interface + */ + if ((nip = atm_nifname(aap->aar_pvc_intf)) == NULL) { + err = ENXIO; + break; + } + + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + if (inp == NULL) { + err = ENXIO; + break; + } + + /* + * Validate PVC params + */ + if (aap->aar_pvc_aal == ATM_AAL5) { + if ((aap->aar_pvc_encaps != ATM_ENC_LLC) && + (aap->aar_pvc_encaps != ATM_ENC_NULL)) { + err = EINVAL; + break; + } + } else if (aap->aar_pvc_aal == ATM_AAL3_4) { + if (aap->aar_pvc_encaps != ATM_ENC_NULL) { + err = EINVAL; + break; + } + } else { + err = EINVAL; + break; + } + + if (aap->aar_pvc_flags & PVC_DYN) { + /* + * For dynamic PVC destination addressing, the + * network interface must have support for this + */ + if ((inp->inf_serv == NULL) || + (inp->inf_serv->is_arp_pvcopen == NULL)) { + err = EDESTADDRREQ; + break; + } + } else { + u_long dst = ((struct sockaddr_in *)&aap->aar_pvc_dst) + ->sin_addr.s_addr; + + if (dst == INADDR_ANY) { + err = EINVAL; + break; + } + } + + /* + * Build connection request + */ + pv.ipp_ipnif = inp; + pv.ipp_vpi = aap->aar_pvc_vpi; + pv.ipp_vci = aap->aar_pvc_vci; + pv.ipp_encaps = aap->aar_pvc_encaps; + pv.ipp_aal = aap->aar_pvc_aal; + if (aap->aar_pvc_flags & PVC_DYN) { + pv.ipp_dst.sin_addr.s_addr = INADDR_ANY; + } else + pv.ipp_dst = *(struct sockaddr_in *)&aap->aar_pvc_dst; + + /* + * Open a new VCC + */ + err = ipatm_openpvc(&pv, &ivp); + break; + + case AIOCS_ADD_ARP: + /* + * Add an ARP mapping + */ + aap = (struct atmaddreq *)data; + + /* + * Validate IP address + */ + if (aap->aar_arp_dst.sa_family != AF_INET) { + err = EAFNOSUPPORT; + break; + } + ip = SATOSIN(&aap->aar_arp_dst)->sin_addr; + + if (aap->aar_arp_intf[0] == '\0') { + /* + * Find the IP network interface associated with + * the supplied IP address + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (ipatm_chknif(ip, inp) == 0) + break; + } + if (inp == NULL) { + err = EADDRNOTAVAIL; + break; + } + } else { + /* + * Find the specified IP network interface + */ + if ((nip = atm_nifname(aap->aar_arp_intf)) == NULL) { + err = ENXIO; + break; + } + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + if (inp == NULL) { + err = ENXIO; + break; + } + } + + if ((ip.s_addr == INADDR_ANY) || +#if (defined(BSD) && (BSD >= 199306)) + in_broadcast(ip, &inp->inf_nif->nif_if) || +#else + in_broadcast(ip) || +#endif + IN_MULTICAST(ntohl(ip.s_addr))) { + err = EADDRNOTAVAIL; + break; + } + + /* + * Notify the responsible ARP service + */ + err = (*inp->inf_serv->is_ioctl)(code, data, inp->inf_isintf); + break; + + case AIOCS_DEL_ARP: + /* + * Delete an ARP mapping + */ + adp = (struct atmdelreq *)data; + + /* + * Validate IP address + */ + if (adp->adr_arp_dst.sa_family != AF_INET) { + err = EAFNOSUPPORT; + break; + } + ip = SATOSIN(&adp->adr_arp_dst)->sin_addr; + + if (adp->adr_arp_intf[0] == '\0') { + /* + * Find the IP network interface associated with + * the supplied IP address + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (ipatm_chknif(ip, inp) == 0) + break; + } + if (inp == NULL) { + err = EADDRNOTAVAIL; + break; + } + } else { + /* + * Find the specified IP network interface + */ + if ((nip = atm_nifname(adp->adr_arp_intf)) == NULL) { + err = ENXIO; + break; + } + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + if (inp == NULL) { + err = ENXIO; + break; + } + } + + if ((ip.s_addr == INADDR_ANY) || +#if (defined(BSD) && (BSD >= 199306)) + in_broadcast(ip, &inp->inf_nif->nif_if) || +#else + in_broadcast(ip) || +#endif + IN_MULTICAST(ntohl(ip.s_addr))) { + err = EADDRNOTAVAIL; + break; + } + + /* + * Notify the responsible ARP service + */ + err = (*inp->inf_serv->is_ioctl)(code, data, inp->inf_isintf); + break; + + case AIOCS_INF_IPM: + /* + * Get IP VCC information + */ + aip = (struct atminfreq *)data; + + if (aip->air_ip_addr.sa_family != AF_INET) + break; + ip = SATOSIN(&aip->air_ip_addr)->sin_addr; + + cp = aip->air_buf_addr; + space = aip->air_buf_len; + + /* + * Loop through all our interfaces + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + /* + * Check out each VCC + */ + for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp; + ivp = Q_NEXT(ivp, struct ipvcc, iv_elem)) { + + if ((ip.s_addr != INADDR_ANY) && + (ip.s_addr != ivp->iv_dst.s_addr)) + continue; + + /* + * Make sure there's room in user buffer + */ + if (space < sizeof(aivr)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + KM_ZERO((caddr_t)&aivr, sizeof(aivr)); + SATOSIN(&aivr.aip_dst_addr)->sin_family = + AF_INET; + SATOSIN(&aivr.aip_dst_addr)->sin_addr.s_addr = + ivp->iv_dst.s_addr; + (void) sprintf(aivr.aip_intf, "%s%d", + inp->inf_nif->nif_if.if_name, + inp->inf_nif->nif_if.if_unit); + if ((ivp->iv_conn) && + (ivp->iv_conn->co_connvc) && + (vcp = ivp->iv_conn->co_connvc->cvc_vcc)) { + aivr.aip_vpi = vcp->vc_vpi; + aivr.aip_vci = vcp->vc_vci; + aivr.aip_sig_proto = vcp->vc_proto; + } + aivr.aip_flags = ivp->iv_flags; + aivr.aip_state = ivp->iv_state; + + /* + * Copy data to user buffer and + * update buffer controls + */ + err = copyout((caddr_t)&aivr, cp, sizeof(aivr)); + if (err) + break; + cp += sizeof(aivr); + space -= sizeof(aivr); + } + if (err) + break; + } + + /* + * Update buffer pointer/count + */ + aip->air_buf_addr = cp; + aip->air_buf_len = space; + break; + + default: + err = EOPNOTSUPP; + } + + return (err); +} + + +/* + * Get Connection's Application/Owner Name + * + * Arguments: + * tok ipatm connection token (pointer to ipvcc) + * + * Returns: + * addr pointer to string containing our name + * + */ +caddr_t +ipatm_getname(tok) + void *tok; +{ + return ("IP"); +} + diff --git a/sys/netatm/ipatm/ipatm_var.h b/sys/netatm/ipatm/ipatm_var.h new file mode 100644 index 0000000..6048284 --- /dev/null +++ b/sys/netatm/ipatm/ipatm_var.h @@ -0,0 +1,215 @@ +/* + * + * =================================== + * 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: ipatm_var.h,v 1.8 1998/03/24 20:56:57 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Protocol control blocks + * + */ + +#ifndef _IPATM_IPATM_VAR_H +#define _IPATM_IPATM_VAR_H + +#ifdef ATM_KERNEL +/* + * Structure containing information for each VCC, both SVC and PVC, which + * supports IP traffic. + */ +struct ipvcc { + Qelem_t iv_elem; /* ip_nif queueing links */ + u_short iv_flags; /* VCC flags (see below) */ + u_char iv_state; /* VCC state (see below) */ + Atm_connection *iv_conn; /* Connection manager token */ + struct in_addr iv_dst; /* Peer's IP address */ + struct ip_nif *iv_ipnif; /* IP network interface */ + struct atm_time iv_time; /* Timer controls */ + short iv_idle; /* VCC idle timer */ + u_char iv_parmx; /* Index into provider's vcc params */ + KBuffer *iv_queue; /* Packet waiting for VCC */ + struct arpmap *iv_arpent; /* ARP entry for VCC */ + struct ipvcc *iv_arpnext; /* ARP link field */ + Atm_connection *iv_arpconn; /* ARP connection manager token */ +}; +#define iv_forw iv_elem.q_forw +#define iv_back iv_elem.q_back +#endif /* ATM_KERNEL */ + +/* + * VCC Flags + */ +#define IVF_PVC 0x0001 /* PVC */ +#define IVF_SVC 0x0002 /* SVC */ +#define IVF_LLC 0x0004 /* VCC uses LLC/SNAP encapsulation */ +#define IVF_MAPOK 0x0008 /* VCC ARP mapping is valid */ +#define IVF_NOIDLE 0x0010 /* Do not idle-timeout this VCC */ + +/* + * VCC States + */ +#define IPVCC_FREE 0 /* No VCC associated with entry */ +#define IPVCC_PMAP 1 /* SVC waiting for ARP mapping */ +#define IPVCC_POPEN 2 /* Pending SVC open completion */ +#define IPVCC_PACCEPT 3 /* Pending SVC accept completion */ +#define IPVCC_ACTPENT 4 /* PVC open - waiting for ARP entry */ +#define IPVCC_ACTIVE 5 /* VCC open - available */ +#define IPVCC_CLOSED 6 /* VCC has been closed */ + + +#ifdef ATM_KERNEL +/* + * Structure containing IP-specific information for each ATM network + * interface in the system. + */ +struct ip_nif { + struct ip_nif *inf_next; /* Next on interface chain */ + struct atm_nif *inf_nif; /* ATM network interface */ + u_short inf_state; /* Interface state (see below) */ + struct in_ifaddr *inf_addr; /* Interface's IP address */ + Queue_t inf_vcq; /* VCC connections queue */ + struct ip_serv *inf_serv; /* Interface service provider */ + +/* For use by IP interface service provider (ie signalling manager) */ + caddr_t inf_isintf; /* Interface control block */ + +/* IP/ATM provided interface services */ + void (*inf_arpnotify)/* ARP event notification */ + __P((struct ipvcc *, int)); + int (*inf_ipinput) /* IP packet input */ + __P((struct ip_nif *, KBuffer *)); + int (*inf_createsvc)/* Create an IP SVC */ + __P((struct ifnet *, u_short, caddr_t, + struct ipvcc **)); +}; + +/* + * Network Interface States + */ +#define IPNIF_ADDR 1 /* Waiting for i/f address */ +#define IPNIF_SIGMGR 2 /* Waiting for sigmgr attach */ +#define IPNIF_ACTIVE 3 /* Active */ + + +/* + * Global IP/ATM Statistics + */ +struct ipatm_stat { + u_long ias_rcvstate; /* Packets received, bad vcc state */ + u_long ias_rcvnobuf; /* Packets received, no buf avail */ +}; + + +/* + * Structure to pass parameters for ipatm_openpvc() + */ +struct ipatmpvc { + struct ip_nif *ipp_ipnif; /* PVC's IP network interface */ + u_short ipp_vpi; /* VPI value */ + u_short ipp_vci; /* VCI value */ + Aal_t ipp_aal; /* AAL type */ + Encaps_t ipp_encaps; /* VCC encapsulation */ + struct sockaddr_in ipp_dst; /* Destination's IP address */ +}; + + +/* + * Timer macros + */ +#define IPVCC_TIMER(s, t) atm_timeout(&(s)->iv_time, (t), ipatm_timeout) +#define IPVCC_CANCEL(s) atm_untimeout(&(s)->iv_time) + +/* + * Misc useful macros + */ +#define SATOSIN(sa) ((struct sockaddr_in *)(sa)) + + +/* + * Global function declarations + */ + /* ipatm_event.c */ +void ipatm_timeout __P((struct atm_time *)); +void ipatm_connected __P((void *)); +void ipatm_cleared __P((void *, struct t_atm_cause *)); +void ipatm_arpnotify __P((struct ipvcc *, int)); +void ipatm_itimeout __P((struct atm_time *)); + + /* ipatm_if.c */ +int ipatm_nifstat __P((int, struct atm_nif *, int)); + + /* ipatm_input.c */ +void ipatm_cpcs_data __P((void *, KBuffer *)); +int ipatm_ipinput __P((struct ip_nif *, KBuffer *)); + + /* ipatm_load.c */ + + /* ipatm_output.c */ +int ipatm_ifoutput __P((struct ifnet *, KBuffer *, + struct sockaddr *)); + + /* ipatm_usrreq.c */ +int ipatm_ioctl __P((int, caddr_t, caddr_t)); +caddr_t ipatm_getname __P((void *)); + + /* ipatm_vcm.c */ +int ipatm_openpvc __P((struct ipatmpvc *, struct ipvcc **)); +int ipatm_createsvc __P((struct ifnet *, u_short, caddr_t, + struct ipvcc **)); +int ipatm_opensvc __P((struct ipvcc *)); +int ipatm_retrysvc __P((struct ipvcc *)); +void ipatm_activate __P((struct ipvcc *)); +int ipatm_incoming __P((void *, Atm_connection *, Atm_attributes *, + void **)); +int ipatm_closevc __P((struct ipvcc *, int)); +int ipatm_chknif __P((struct in_addr, struct ip_nif *)); +struct ipvcc *ipatm_iptovc __P((struct sockaddr_in *, struct atm_nif *)); + + +/* + * External variables + */ +extern int ipatm_vccnt; +extern int ipatm_vcidle; +extern int ipatm_print; +extern u_long last_map_ipdst; +extern struct ipvcc *last_map_ipvcc; +extern struct ip_nif *ipatm_nif_head; +extern struct sp_info ipatm_vcpool; +extern struct sp_info ipatm_nifpool; +extern struct ipatm_stat ipatm_stat; +extern struct atm_time ipatm_itimer; +extern Atm_endpoint ipatm_endpt; +extern Atm_attributes ipatm_aal5llc; +extern Atm_attributes ipatm_aal5null; +extern Atm_attributes ipatm_aal4null; + +#endif /* ATM_KERNEL */ + +#endif /* _IPATM_IPATM_VAR_H */ diff --git a/sys/netatm/ipatm/ipatm_vcm.c b/sys/netatm/ipatm/ipatm_vcm.c new file mode 100644 index 0000000..5d6a7fc --- /dev/null +++ b/sys/netatm/ipatm/ipatm_vcm.c @@ -0,0 +1,1245 @@ +/* + * + * =================================== + * 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: ipatm_vcm.c,v 1.13 1998/08/06 18:21:14 mks Exp $ + * + */ + +/* + * IP Over ATM Support + * ------------------- + * + * Virtual Channel Manager + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: ipatm_vcm.c,v 1.13 1998/08/06 18:21:14 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm.h> +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> + + +Atm_attributes ipatm_aal5llc = { + NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL5 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_PRESENT, + { + T_ATM_CLASS_X, + T_ATM_NULL, + T_ATM_NULL, + T_NO, + T_ATM_1_TO_1 + } + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_PRESENT, + T_ATM_ABSENT, + { + { + T_ATM_SIMPLE_ID, + }, + { + T_ATM_ABSENT + } + } + }, + { /* llc */ + T_ATM_PRESENT, + { + T_ATM_LLC_SHARING, + IPATM_LLC_LEN, + IPATM_LLC_HDR + } + }, + { /* called */ + T_ATM_PRESENT, + }, + { /* calling */ + T_ATM_ABSENT + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ABSENT + }, + { /* cause */ + T_ATM_ABSENT + } +}; + +Atm_attributes ipatm_aal5null = { + NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + sizeof(struct ifnet *), /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL5 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_PRESENT, + { + T_ATM_CLASS_X, + T_ATM_NULL, + T_ATM_NULL, + T_NO, + T_ATM_1_TO_1 + } + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_ABSENT, + T_ATM_ABSENT + }, + { /* llc */ + T_ATM_ABSENT + }, + { /* called */ + T_ATM_PRESENT, + }, + { /* calling */ + T_ATM_ABSENT + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ABSENT + }, + { /* cause */ + T_ATM_ABSENT + } +}; + +Atm_attributes ipatm_aal4null = { + NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + sizeof(struct ifnet *), /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL3_4 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_PRESENT, + { + T_ATM_CLASS_X, + T_ATM_NULL, + T_ATM_NULL, + T_NO, + T_ATM_1_TO_1 + } + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_ABSENT, + T_ATM_ABSENT + }, + { /* llc */ + T_ATM_ABSENT + }, + { /* called */ + T_ATM_PRESENT, + }, + { /* calling */ + T_ATM_ABSENT + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ABSENT + }, + { /* cause */ + T_ATM_ABSENT + } +}; + +static struct t_atm_cause ipatm_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + 0, + {0, 0, 0, 0} +}; + + +/* + * Open an IP PVC + * + * This function will perform all actions necessary to activate a + * PVC for IP usage. In particular, it will allocate control blocks, + * open the PVC, initialize PVC stack, and initiate whatever ARP + * procedures are required. + * + * Arguments: + * pvp pointer to PVC parameter structure + * sivp address to return pointer to IP PVC control block + * + * Returns: + * 0 PVC was successfully opened + * errno open failed - reason indicated + * + */ +int +ipatm_openpvc(pvp, sivp) + struct ipatmpvc *pvp; + struct ipvcc **sivp; +{ + struct ipvcc *ivp; + Atm_attributes *ap; + Atm_addr_pvc *pvcp; + struct atm_nif *nip; + struct ip_nif *inp; + int s, err = 0; + + inp = pvp->ipp_ipnif; + nip = inp->inf_nif; + + /* + * Make sure interface is ready to go + */ + if (inp->inf_state != IPNIF_ACTIVE) { + err = ENETDOWN; + goto done; + } + + /* + * Validate fixed destination IP address + */ + if (pvp->ipp_dst.sin_addr.s_addr != INADDR_ANY) { +#if (defined(BSD) && (BSD >= 199306)) + if (in_broadcast(pvp->ipp_dst.sin_addr, &nip->nif_if) || +#else + if (in_broadcast(pvp->ipp_dst.sin_addr) || +#endif + IN_MULTICAST(ntohl(pvp->ipp_dst.sin_addr.s_addr)) || + ipatm_chknif(pvp->ipp_dst.sin_addr, inp)) { + err = EINVAL; + goto done; + } + } + + /* + * Allocate IP VCC block + */ + ivp = (struct ipvcc *)atm_allocate(&ipatm_vcpool); + if (ivp == NULL) { + err = ENOMEM; + goto done; + } + + /* + * Initialize the PVC + */ + ivp->iv_flags = IVF_PVC; + if (pvp->ipp_encaps == ATM_ENC_LLC) + ivp->iv_flags |= IVF_LLC; + + /* + * Fill out connection attributes + */ + if (pvp->ipp_aal == ATM_AAL5) { + if (pvp->ipp_encaps == ATM_ENC_LLC) + ap = &ipatm_aal5llc; + else + ap = &ipatm_aal5null; + } else { + ap = &ipatm_aal4null; + } + + ap->nif = nip; + ap->traffic.v.forward.PCR_all_traffic = nip->nif_pif->pif_pcr; + ap->traffic.v.backward.PCR_all_traffic = nip->nif_pif->pif_pcr; + ap->called.addr.address_format = T_ATM_PVC_ADDR; + ap->called.addr.address_length = sizeof(Atm_addr_pvc); + pvcp = (Atm_addr_pvc *)ap->called.addr.address; + ATM_PVC_SET_VPI(pvcp, pvp->ipp_vpi); + ATM_PVC_SET_VCI(pvcp, pvp->ipp_vci); + ap->called.subaddr.address_format = T_ATM_ABSENT; + ap->called.subaddr.address_length = 0; + + /* + * Create PVC + */ + err = atm_cm_connect(&ipatm_endpt, ivp, ap, &ivp->iv_conn); + if (err) { + atm_free((caddr_t)ivp); + goto done; + } + + /* + * Save PVC information and link in VCC + */ + /* ivp->iv_ = ap->headout; */ + + /* + * Queue VCC onto its network interface + */ + s = splnet(); + ipatm_vccnt++; + ENQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq); + ivp->iv_ipnif = inp; + (void) splx(s); + + /* + * Set destination IP address and IPVCC state + */ + if (pvp->ipp_dst.sin_addr.s_addr == INADDR_ANY) { + /* + * Initiate ARP processing + */ + switch ((*inp->inf_serv->is_arp_pvcopen)(ivp)) { + + case MAP_PROCEEDING: + /* + * Wait for answer + */ + ivp->iv_state = IPVCC_ACTIVE; + break; + + case MAP_VALID: + /* + * We've got our answer already + */ + ivp->iv_state = IPVCC_ACTIVE; + ivp->iv_flags |= IVF_MAPOK; + ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr; + break; + + case MAP_FAILED: + /* + * Try again later + */ + ivp->iv_state = IPVCC_ACTPENT; + IPVCC_TIMER(ivp, 1 * ATM_HZ); + break; + + default: + panic("ipatm_openpvc: invalid arp_pvcopen return"); + } + + } else { + /* + * Use configured IP destination + */ + ivp->iv_dst.s_addr = pvp->ipp_dst.sin_addr.s_addr; + ivp->iv_state = IPVCC_ACTIVE; + ivp->iv_flags |= IVF_MAPOK; + } + +done: + if (err) + *sivp = NULL; + else + *sivp = ivp; + return (err); +} + + +/* + * Create an IP SVC + * + * This function will initiate the creation of an IP SVC. The IP VCC + * control block will be initialized and, if required, we will initiate + * ARP processing in order to resolve the destination's ATM address. Once + * the destination ATM address is known, ipatm_opensvc() will be called. + * + * Arguments: + * ifp pointer to destination ifnet structure + * daf destination address family type + * dst pointer to destination address + * sivp address to return pointer to IP SVC control block + * + * Returns: + * 0 SVC creation was successfully initiated + * errno creation failed - reason indicated + * + */ +int +ipatm_createsvc(ifp, daf, dst, sivp) + struct ifnet *ifp; + u_short daf; + caddr_t dst; + struct ipvcc **sivp; +{ + struct atm_nif *nip = (struct atm_nif *)ifp; + struct ip_nif *inp; + struct ipvcc *ivp; + struct in_addr *ip; + Atm_addr *atm; + int s, err = 0; + + /* + * Get IP interface and make sure its ready + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + if (inp == NULL) { + err = ENXIO; + goto done; + } + if (inp->inf_state != IPNIF_ACTIVE) { + err = ENETDOWN; + goto done; + } + + /* + * Validate destination address + */ + if (daf == AF_INET) { + /* + * Destination is IP address + */ + ip = (struct in_addr *)dst; + atm = NULL; + if (ip->s_addr == INADDR_ANY) { + err = EADDRNOTAVAIL; + goto done; + } + } else if (daf == AF_ATM) { + /* + * Destination is ATM address + */ + atm = (Atm_addr *)dst; + ip = NULL; + if (atm->address_format == T_ATM_ABSENT) { + err = EINVAL; + goto done; + } + } else { + err = EINVAL; + goto done; + } + + /* + * Make sure we have services provider and ARP support + */ + if ((inp->inf_serv == NULL) || + (inp->inf_serv->is_arp_svcout == NULL)) { + err = ENETDOWN; + goto done; + } + + /* + * Allocate IP VCC + */ + ivp = (struct ipvcc *)atm_allocate(&ipatm_vcpool); + if (ivp == NULL) { + err = ENOMEM; + goto done; + } + + /* + * Initialize SVC + */ + ivp->iv_flags = IVF_SVC; + ivp->iv_ipnif = inp; + + /* + * Get destination ATM address + */ + if (daf == AF_INET) { + /* + * ARP is the way... + */ + ivp->iv_dst.s_addr = ip->s_addr; + + switch ((*inp->inf_serv->is_arp_svcout)(ivp, ip)) { + + case MAP_PROCEEDING: + /* + * Wait for answer + */ + ivp->iv_state = IPVCC_PMAP; + IPVCC_TIMER(ivp, IPATM_ARP_TIME); + break; + + case MAP_VALID: + /* + * We've got our answer already, so open SVC + */ + ivp->iv_flags |= IVF_MAPOK; + err = ipatm_opensvc(ivp); + if (err) { + (*inp->inf_serv->is_arp_close)(ivp); + atm_free((caddr_t)ivp); + goto done; + } + break; + + case MAP_FAILED: + /* + * So sorry...come again + */ + atm_free((caddr_t)ivp); + err = ENETDOWN; + goto done; + + default: + panic("ipatm_createsvc: invalid arp_svcout return"); + } + } else { + /* + * We were given the ATM address, so open the SVC + * + * Create temporary arp map entry so that opensvc() works. + * Caller must set up a permanent entry immediately! (yuk) + */ + struct arpmap map; + + ATM_ADDR_COPY(atm, &map.am_dstatm); + map.am_dstatmsub.address_format = T_ATM_ABSENT; + map.am_dstatmsub.address_length = 0; + ivp->iv_arpent = ↦ + err = ipatm_opensvc(ivp); + if (err) { + atm_free((caddr_t)ivp); + goto done; + } + ivp->iv_arpent = NULL; + } + + /* + * Queue VCC onto its network interface + */ + s = splnet(); + ipatm_vccnt++; + ENQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq); + (void) splx(s); + +done: + if (err) + *sivp = NULL; + else + *sivp = ivp; + return (err); +} + + +/* + * Open an IP SVC + * + * This function will continue the IP SVC creation process. Here, we + * will issue an SVC open to the signalling manager and then wait for + * the final SVC setup results. + * + * Arguments: + * ivp pointer to IP SVC to open + * + * Returns: + * 0 SVC open was successfully initiated + * errno open failed - reason indicated + * + */ +int +ipatm_opensvc(ivp) + struct ipvcc *ivp; +{ + struct ip_nif *inp = ivp->iv_ipnif; + Atm_attributes *ap; + int err = 0, i; + + /* + * Cancel possible arp timeout + */ + IPVCC_CANCEL(ivp); + + /* + * Fill out connection attributes + */ + i = ivp->iv_parmx; + if (inp->inf_serv->is_vccparm[i].ivc_aal == ATM_AAL5) { + if (inp->inf_serv->is_vccparm[i].ivc_encaps == ATM_ENC_LLC) { + ap = &ipatm_aal5llc; + ivp->iv_flags |= IVF_LLC; + } else { + ap = &ipatm_aal5null; + ivp->iv_flags &= ~IVF_LLC; + } + } else { + ap = &ipatm_aal4null; + ivp->iv_flags &= ~IVF_LLC; + } + + ap->nif = inp->inf_nif; + ap->traffic.v.forward.PCR_all_traffic = inp->inf_nif->nif_pif->pif_pcr; + ap->traffic.v.backward.PCR_all_traffic = inp->inf_nif->nif_pif->pif_pcr; + + ATM_ADDR_COPY(&ivp->iv_arpent->am_dstatm, &ap->called.addr); + ATM_ADDR_COPY(&ivp->iv_arpent->am_dstatmsub, &ap->called.subaddr); + + /* + * Initiate SVC open + */ + err = atm_cm_connect(&ipatm_endpt, ivp, ap, &ivp->iv_conn); + switch (err) { + + case EINPROGRESS: + /* + * Call is progressing + */ + /* ivp->iv_ = ap->headout; */ + + /* + * Now we just wait for a CALL_CONNECTED event + */ + ivp->iv_state = IPVCC_POPEN; + IPVCC_TIMER(ivp, IPATM_SVC_TIME); + err = 0; + break; + + case 0: + /* + * We've been hooked up with a shared VCC + */ + /* ivp->iv_ = ap->headout; */ + ipatm_activate(ivp); + break; + } + + return (err); +} + + +/* + * Retry an IP SVC Open + * + * This function will attempt to retry a failed SVC open request. The IP + * interface service provider specifies a list of possible VCC parameters + * for IP to use. We will try each set of parameters in turn until either + * an open succeeds or we reach the end of the list. + * + * Arguments: + * ivp pointer to IP SVC + * + * Returns: + * 0 SVC (re)open was successfully initiated + * else retry failed + * + */ +int +ipatm_retrysvc(ivp) + struct ipvcc *ivp; +{ + struct ip_nif *inp = ivp->iv_ipnif; + + /* + * If there isn't another set of vcc parameters to try, return + */ + if ((++ivp->iv_parmx >= IPATM_VCCPARMS) || + (inp->inf_serv->is_vccparm[ivp->iv_parmx].ivc_aal == 0)) + return (1); + + /* + * Okay, now initiate open with a new set of parameters + */ + return (ipatm_opensvc(ivp)); +} + + +/* + * Finish IP SVC Activation + * + * Arguments: + * ivp pointer to IP SVC + * + * Returns: + * none + * + */ +void +ipatm_activate(ivp) + struct ipvcc *ivp; +{ + + /* + * Connection is now active + */ + ivp->iv_state = IPVCC_ACTIVE; + IPVCC_CANCEL(ivp); + + /* + * Tell ARP module that connection is active + */ + if ((*ivp->iv_ipnif->inf_serv->is_arp_svcact)(ivp)) { + (void) ipatm_closevc(ivp, T_ATM_CAUSE_TEMPORARY_FAILURE); + return; + } + + /* + * Send any queued packet + */ + if ((ivp->iv_flags & IVF_MAPOK) && ivp->iv_queue) { + struct sockaddr_in sin; + struct ifnet *ifp; + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ivp->iv_dst.s_addr; + ifp = (struct ifnet *)ivp->iv_ipnif->inf_nif; + (void) ipatm_ifoutput(ifp, ivp->iv_queue, + (struct sockaddr *)&sin); + ivp->iv_queue = NULL; + } +} + + +/* + * Process Incoming Calls + * + * This function will receive control when an incoming call has been matched + * to one of our registered listen parameter blocks. Assuming the call passes + * acceptance criteria and all required resources are available, we will + * create an IP SVC and notify the connection manager of our decision. We + * will then await notification of the final SVC setup results. If any + * problems are encountered, we will just tell the connection manager to + * reject the call. + * + * Called at splnet. + * + * Arguments: + * tok owner's matched listening token + * cop pointer to incoming call's connection block + * ap pointer to incoming call's attributes + * tokp pointer to location to store our connection token + * + * Returns: + * 0 call is accepted + * errno call rejected - reason indicated + * + */ +int +ipatm_incoming(tok, cop, ap, tokp) + void *tok; + Atm_connection *cop; + Atm_attributes *ap; + void **tokp; +{ + struct atm_nif *nip = ap->nif; + struct ip_nif *inp; + struct ipvcc *ivp = NULL; + int err, cause; + int usellc = 0, mtu = ATM_NIF_MTU; + + /* + * Get IP interface and make sure its ready + */ + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + if ((inp == NULL) || (inp->inf_state != IPNIF_ACTIVE)) { + err = ENETUNREACH; + cause = T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE; + goto reject; + } + + /* + * Make sure we have services provider and ARP support + */ + if ((inp->inf_serv == NULL) || + (inp->inf_serv->is_arp_svcin == NULL)) { + err = ENETUNREACH; + cause = T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE; + goto reject; + } + + /* + * Check for LLC encapsulation + */ + if ((ap->blli.tag_l2 == T_ATM_PRESENT) && + (ap->blli.v.layer_2_protocol.ID_type == T_ATM_SIMPLE_ID) && + (ap->blli.v.layer_2_protocol.ID.simple_ID == T_ATM_BLLI2_I8802)) { + usellc = 1; + mtu += IPATM_LLC_LEN; + } + + /* + * Verify requested MTU + */ + if (ap->aal.type == ATM_AAL5) { + if ((ap->aal.v.aal5.forward_max_SDU_size > mtu) || + (ap->aal.v.aal5.backward_max_SDU_size < mtu)) { + err = ENETUNREACH; + cause = T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED; + goto reject; + } + } else { + if ((ap->aal.v.aal4.forward_max_SDU_size > mtu) || + (ap->aal.v.aal4.backward_max_SDU_size < mtu)) { + err = ENETUNREACH; + cause = T_ATM_CAUSE_AAL_PARAMETERS_NOT_SUPPORTED; + goto reject; + } + } + + /* + * Allocate IP VCC + */ + ivp = (struct ipvcc *)atm_allocate(&ipatm_vcpool); + if (ivp == NULL) { + err = ENOMEM; + cause = T_ATM_CAUSE_UNSPECIFIED_RESOURCE_UNAVAILABLE; + goto reject; + } + + /* + * Initialize SVC + */ + ivp->iv_flags = IVF_SVC; + ivp->iv_ipnif = inp; + if (usellc) + ivp->iv_flags |= IVF_LLC; + + /* + * Lookup ARP entry for destination + */ + switch ((*inp->inf_serv->is_arp_svcin) + (ivp, &ap->calling.addr, &ap->calling.subaddr)) { + + case MAP_PROCEEDING: + /* + * We'll be (hopefully) notified later + */ + break; + + case MAP_VALID: + /* + * We've got our answer already + */ + ivp->iv_flags |= IVF_MAPOK; + ivp->iv_dst.s_addr = ivp->iv_arpent->am_dstip.s_addr; + break; + + case MAP_FAILED: + /* + * So sorry...come again + */ + err = ENETUNREACH; + cause = T_ATM_CAUSE_SERVICE_OR_OPTION_UNAVAILABLE; + goto reject; + + default: + panic("ipatm_incoming: invalid arp_svcin return"); + } + + /* + * Accept SVC connection + */ + ivp->iv_state = IPVCC_PACCEPT; + + /* + * Save VCC information + */ + ivp->iv_conn = cop; + *tokp = ivp; + /* ivp->iv_ = ap->headout; */ + + /* + * Queue VCC onto its network interface + */ + ipatm_vccnt++; + ENQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq); + + /* + * Wait for a CALL_CONNECTED event + */ + IPVCC_TIMER(ivp, IPATM_SVC_TIME); + + return (0); + +reject: + /* + * Clean up after call failure + */ + if (ivp) { + (*inp->inf_serv->is_arp_close)(ivp); + atm_free((caddr_t)ivp); + } + ap->cause.tag = T_ATM_PRESENT; + ap->cause.v = ipatm_cause; + ap->cause.v.cause_value = cause; + return (err); +} + + +/* + * Close an IP VCC + * + * This function will close an IP VCC (PVC or SVC), including notifying + * the signalling and ARP subsystems of the VCC's demise and cleaning + * up memory after ourselves. + * + * Arguments: + * ivp pointer to VCC + * code cause code + * + * Returns: + * 0 VCC successfully closed + * errno close failed - reason indicated + * + */ +int +ipatm_closevc(ivp, code) + struct ipvcc *ivp; + int code; +{ + struct ip_nif *inp = ivp->iv_ipnif; + int s, err; + + /* + * Make sure VCC hasn't been through here already + */ + switch (ivp->iv_state) { + + case IPVCC_FREE: + return (EALREADY); + } + + /* + * Reset lookup cache + */ + if (last_map_ipvcc == ivp) { + last_map_ipvcc = NULL; + last_map_ipdst = 0; + } + + /* + * Tell ARP about SVCs and dynamic PVCs + */ + if (inp->inf_serv && + ((ivp->iv_flags & IVF_SVC) || inp->inf_serv->is_arp_pvcopen)) { + (*inp->inf_serv->is_arp_close)(ivp); + } + + /* + * Free queued packets + */ + if (ivp->iv_queue) + KB_FREEALL(ivp->iv_queue); + + /* + * Cancel any timers + */ + IPVCC_CANCEL(ivp); + + /* + * Close VCC + */ + switch (ivp->iv_state) { + + case IPVCC_PMAP: + break; + + case IPVCC_POPEN: + case IPVCC_PACCEPT: + case IPVCC_ACTPENT: + case IPVCC_ACTIVE: + ipatm_cause.cause_value = code; + err = atm_cm_release(ivp->iv_conn, &ipatm_cause); + if (err) { + log(LOG_ERR, + "ipatm_closevc: release fail: err=%d\n", err); + } + break; + + case IPVCC_CLOSED: + break; + + default: + log(LOG_ERR, + "ipatm_closevc: unknown state: ivp=0x%x, state=%d\n", + (int)ivp, ivp->iv_state); + } + + /* + * Remove VCC from network i/f + */ + s = splnet(); + DEQUEUE(ivp, struct ipvcc, iv_elem, inp->inf_vcq); + + /* + * Reset state just to be sure + */ + ivp->iv_state = IPVCC_FREE; + + /* + * If ARP module is done with VCC too, then free it + */ + if (ivp->iv_arpconn == NULL) + atm_free((caddr_t)ivp); + ipatm_vccnt--; + (void) splx(s); + + return (0); +} + + +/* + * Check if IP address is valid on a Network Interface + * + * Checks whether the supplied IP address is allowed to be assigned to + * the supplied IP network interface. + * + * Arguments: + * in IP address + * inp pointer to IP network interface + * + * Returns: + * 0 - OK to assign + * 1 - not valid to assign + * + */ +int +ipatm_chknif(in, inp) + struct in_addr in; + struct ip_nif *inp; +{ + struct in_ifaddr *ia; + u_long i; + + /* + * Make sure there's an interface requested + */ + if (inp == NULL) + return (1); + + /* + * Make sure we have an IP address + */ + i = ntohl(in.s_addr); + if (i == 0) + return (1); + + /* + * Make sure an interface address is set + */ + ia = inp->inf_addr; + if (ia == NULL) + return (1); + + /* + * Make sure we're on the right subnet + */ + if ((i & ia->ia_subnetmask) != ia->ia_subnet) + return (1); + + return (0); +} + + +/* + * Map an IP Address to an IP VCC + * + * Given a destination IP address, this function will return a pointer + * to the appropriate output IP VCC to which to send the packet. + * This is currently implemented using a one-behind cache containing the + * last successful mapping result. If the cache lookup fails, then a + * simple linear search of all IP VCCs on the destination network interface + * is performed. This is obviously an area to look at for performance + * improvements. + * + * Arguments: + * dst pointer to destination IP address + * nip pointer to destination network interface + * + * Returns: + * addr pointer to located IP VCC + * 0 no such mapping exists + * + */ +struct ipvcc * +ipatm_iptovc(dst, nip) + struct sockaddr_in *dst; + struct atm_nif *nip; +{ + struct ip_nif *inp; + struct ipvcc *ivp; + u_long dstip = dst->sin_addr.s_addr; + int s; + + /* + * Look in cache first + */ + if (last_map_ipdst == dstip) + return (last_map_ipvcc); + + /* + * Oh well, we've got to search for it...first find the interface + */ + s = splnet(); + for (inp = ipatm_nif_head; inp; inp = inp->inf_next) { + if (inp->inf_nif == nip) + break; + } + if (inp == NULL) { + (void) splx(s); + return (NULL); + } + + /* + * Now home in on the VCC + */ + for (ivp = Q_HEAD(inp->inf_vcq, struct ipvcc); ivp; + ivp = Q_NEXT(ivp, struct ipvcc, iv_elem)) { + if (ivp->iv_dst.s_addr == dstip) + break; + } + + /* + * Update lookup cache + */ + if (ivp) { + last_map_ipdst = dstip; + last_map_ipvcc = ivp; + } + (void) splx(s); + + return (ivp); +} + diff --git a/sys/netatm/kern_include.h b/sys/netatm/kern_include.h new file mode 100644 index 0000000..9134206 --- /dev/null +++ b/sys/netatm/kern_include.h @@ -0,0 +1,112 @@ +/* + * + * =================================== + * 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: kern_include.h,v 1.7 1998/07/23 21:44:40 root Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * Common kernel module includes + * + */ + +#ifndef _NETATM_KERN_INCLUDE_H +#define _NETATM_KERN_INCLUDE_H + +/* + * Note that we're compiling kernel code + */ +#define ATM_KERNEL + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/sockio.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/syslog.h> + +#ifdef sun +#include <machine/cpu.h> +#include <machine/mmu.h> +#include <machine/psl.h> +#include <sun/openprom.h> +#include <sun/vddrv.h> +#include <sundev/mbvar.h> +#endif + +#ifdef __FreeBSD__ +#include <machine/clock.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#endif + +/* + * Networking support + */ +#include <net/if.h> +#if (defined(BSD) && (BSD >= 199103)) +#include <net/if_types.h> +#include <net/if_dl.h> +#endif +#include <net/netisr.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> + +/* + * Porting fluff + */ +#include <netatm/port.h> + +/* + * ATM core services + */ +#include <netatm/queue.h> +#include <netatm/atm.h> +#include <netatm/atm_sys.h> +#include <netatm/atm_sap.h> +#include <netatm/atm_cm.h> +#include <netatm/atm_if.h> +#include <netatm/atm_vc.h> +#include <netatm/atm_ioctl.h> +#include <netatm/atm_sigmgr.h> +#include <netatm/atm_stack.h> +#include <netatm/atm_pcb.h> +#include <netatm/atm_var.h> + +#endif /* _NETATM_KERN_INCLUDE_H */ diff --git a/sys/netatm/port.h b/sys/netatm/port.h new file mode 100644 index 0000000..fac693a --- /dev/null +++ b/sys/netatm/port.h @@ -0,0 +1,564 @@ +/* + * + * =================================== + * 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: port.h,v 1.7 1998/03/24 20:25:28 mks Exp $ + * + */ + +/* + * System Configuration + * -------------------- + * + * Porting aides + * + */ + +#ifndef _NETATM_PORT_H +#define _NETATM_PORT_H + +/* + * Try to ensure that this system is supported + */ +#if (defined(BSD) && (BSD >= 199103)) + + /* 4.3 BSD Net2 based */ + +#elif defined(sun) + + /* SunOS4.x */ + +#else + + /* Ooops */ + #error "Undefined/unsupported system type" + +#endif + + +/* + * Kernel memory management + * + * KM_ALLOC(size, type, flags) + * Returns an allocated kernel memory chunk of size bytes. + * KM_FREE(addr, size, type) + * Free a kernel memory chunk of size bytes. + * KM_CMP(b1, b2, len) + * Compares len bytes of data from b1 against b2. + * KM_COPY(from, to, len) + * Copies len bytes of data from from to to. + * KM_ZERO(addr, len) + * Zeros len bytes of data from addr. + * + */ +#ifdef ATM_KERNEL +#if (defined(BSD) && (BSD >= 199103)) +#include <sys/malloc.h> +#define KM_ALLOC(size, type, flags) malloc((size), (type), (flags)) +#define KM_FREE(addr, size, type) free((addr), (type)) +#elif defined(sun) +#include <sys/kmem_alloc.h> +#define KM_ALLOC(size, type, flags) kmem_alloc(size) +#define KM_FREE(addr, size, type) kmem_free((addr), (size)) +#endif + +#if defined(BSD) +#define KM_CMP(b1, b2, len) bcmp((void *)(b1), (void *)(b2),\ + (len)) +#define KM_COPY(from, to, len) bcopy((void *)(from), (void *)(to),\ + (len)) +#define KM_ZERO(addr, len) bzero((void *)(addr), (len)) +#endif +#define XM_COPY(f, t, l) KM_COPY((f), (t), (l)) + +#else + +/* + * User-space memory management + * + * UM_ALLOC(size) Returns an allocated kernel memory chunk of size bytes. + * UM_FREE(addr) Free a kernel memory chunk of size bytes. + * UM_COPY(from, to, len) + * Copies len bytes of data from from to to. + * UM_ZERO(addr, len) Zeros len bytes of data from addr. + * + */ +#if (defined(BSD) && (BSD >= 199103)) +#define UM_ALLOC(size) malloc((size_t)(size)) +#define UM_FREE(addr) free((void *)(addr)) +#define UM_COPY(from, to, len) bcopy((void *)(from), (void *)(to),\ + (size_t)(len)) +#define UM_ZERO(addr, len) bzero((void *)(addr), (size_t)(len)) +#elif defined(sun) +#define UM_ALLOC(size) malloc(size) +#define UM_FREE(addr) free((char *)(addr)) +#define UM_COPY(from, to, len) bcopy((char *)(from), (char *)(to), (len)) +#define UM_ZERO(addr, len) bzero((char *)(addr), (len)) + +#endif +#define XM_COPY(f, t, l) UM_COPY((f), (t), (l)) + +#endif /* ATM_KERNEL */ + + +#ifdef ATM_KERNEL +/* + * Kernel buffers + * + * KBuffer Typedef for a kernel buffer. + * + * KB_NEXT(bfr) Access next buffer in chain (r/w). + * KB_LEN(bfr) Access length of data in this buffer (r/w). + * KB_QNEXT(bfr) Access next buffer in queue (r/w). + * + * KB_ALLOC(bfr, size, flags, type) + * Allocates a new kernel buffer of at least size bytes. + * KB_ALLOCPKT(bfr, size, flags, type) + * Allocates a new kernel packet header buffer of at + * least size bytes. + * KB_ALLOCEXT(bfr, size, flags, type) + * Allocates a new kernel buffer with external storage + * of at least size bytes. + * KB_FREEONE(bfr, nxt) Free buffer bfr and set next buffer in chain in nxt. + * KB_FREEALL(bfr) Free bfr's entire buffer chain. + * KB_COPY(bfr, off, len, new, flags) + * Copy len bytes of user data from buffer bfr starting at + * byte offset off and return new buffer chain in new. + * If len is KB_COPYALL, copy until end of chain. + * KB_COPYDATA(bfr, off, len, datap) + * Copy data from buffer bfr starting at byte offset off + * for len bytes into the data area pointed to by datap. + * Returns the number of bytes not copied to datap. + * KB_PULLUP(bfr, n, new) + * Get at least the first n bytes of data in the buffer + * chain headed by bfr contiguous in the first buffer. + * Returns the (potentially new) head of the chain in new. + * On failure the chain is freed and NULL is returned. + * KB_LINKHEAD(new, head) + * Link the kernel buffer new at the head of the buffer + * chain headed by head. If both new and head are + * packet header buffers, new will become the packet + * header for the chain. + * KB_LINK(new, prev) + * Link the kernel buffer new into the buffer chain + * after the buffer prev. + * KB_UNLINKHEAD(head, next) + * Unlink the kernel buffer from the head of the buffer + * chain headed by head. The buffer head will be freed + * and the new chain head will be placed in next. + * KB_UNLINK(old, prev, next) + * Unlink the kernel buffer old with previous buffer prev + * from its buffer chain. The following buffer in the + * chain will be placed in next and the buffer old will + * be freed. + * KB_ISPKT(bfr) Tests whether bfr is a packet header buffer. + * KB_ISEXT(bfr) Tests whether bfr has external storage. + * KB_BFRSTART(bfr, x, t) + * Sets x (cast to type t) to point to the start of the + * buffer space in bfr. + * KB_BFREND(bfr, x, t) + * Sets x (cast to type t) to point one byte past the end + * of the buffer space in bfr. + * KB_BFRLEN(bfr) Returns length of buffer space in bfr. + * KB_DATASTART(bfr, x, t) + * Sets x (cast to type t) to point to the start of the + * buffer data contained in bfr. + * KB_DATAEND(bfr, x, t) + * Sets x (cast to type t) to point one byte past the end + * of the buffer data contained in bfr. + * KB_HEADSET(bfr, n) Sets the start address for buffer data in buffer bfr to + * n bytes from the beginning of the buffer space. + * KB_HEADMOVE(bfr, n) Adjust buffer data controls to move data down (n > 0) + * or up (n < 0) n bytes in the buffer bfr. + * KB_HEADADJ(bfr, n) Adjust buffer data controls to add (n > 0) or subtract + * (n < 0) n bytes of data to/from the beginning of bfr. + * KB_TAILADJ(bfr, n) Adjust buffer data controls to add (n > 0) or subtract + * (n < 0) n bytes of data to/from the end of bfr. + * KB_TAILALIGN(bfr, n) Set buffer data controls to place an object of size n + * at the end of bfr, longword aligned. + * KB_HEADROOM(bfr, n) Set n to the amount of buffer space available before + * the start of data in bfr. + * KB_TAILROOM(bfr, n) Set n to the amount of buffer space available after + * the end of data in bfr. + * KB_PLENGET(bfr, n) Set n to bfr's packet length. + * KB_PLENSET(bfr, n) Set bfr's packet length to n. + * KB_PLENADJ(bfr, n) Adjust total packet length by n bytes. + * + */ +#if defined(BSD) +#include <sys/mbuf.h> +typedef struct mbuf KBuffer; + +#define KB_F_WAIT M_WAIT +#define KB_F_NOWAIT M_DONTWAIT + +#define KB_T_HEADER MT_HEADER +#define KB_T_DATA MT_DATA + +#define KB_COPYALL M_COPYALL + +#if BSD >= 199103 + +#define KB_NEXT(bfr) (bfr)->m_next +#define KB_LEN(bfr) (bfr)->m_len +#define KB_QNEXT(bfr) (bfr)->m_nextpkt +#define KB_ALLOC(bfr, size, flags, type) { \ + if ((size) <= MLEN) { \ + MGET((bfr), (flags), (type)); \ + } else \ + (bfr) = NULL; \ +} +#define KB_ALLOCPKT(bfr, size, flags, type) { \ + if ((size) <= MHLEN) { \ + MGETHDR((bfr), (flags), (type)); \ + } else \ + (bfr) = NULL; \ +} +#define KB_ALLOCEXT(bfr, size, flags, type) { \ + if ((size) <= MCLBYTES) { \ + MGET((bfr), (flags), (type)); \ + if ((bfr) != NULL) { \ + MCLGET((bfr), (flags)); \ + if (((bfr)->m_flags & M_EXT) == 0) { \ + m_freem((bfr)); \ + (bfr) = NULL; \ + } \ + } \ + } else \ + (bfr) = NULL; \ +} +#define KB_FREEONE(bfr, nxt) { \ + (nxt) = m_free(bfr); \ +} +#define KB_FREEALL(bfr) { \ + m_freem(bfr); \ +} +#define KB_COPY(bfr, off, len, new, flags) { \ + (new) = m_copym((bfr), (off), (len), (flags)); \ +} +#define KB_COPYDATA(bfr, off, len, datap) \ + (m_copydata((bfr), (off), (len), (datap)), 0) +#define KB_PULLUP(bfr, n, new) { \ + (new) = m_pullup((bfr), (n)); \ +} +#define KB_LINKHEAD(new, head) { \ + if ((head) && KB_ISPKT(new) && KB_ISPKT(head)) {\ + M_COPY_PKTHDR((new), (head)); \ + (head)->m_flags &= ~M_PKTHDR; \ + } \ + (new)->m_next = (head); \ +} +#define KB_LINK(new, prev) { \ + (new)->m_next = (prev)->m_next; \ + (prev)->m_next = (new); \ +} +#define KB_UNLINKHEAD(head, next) { \ + MFREE((head), (next)); \ +} +#define KB_UNLINK(old, prev, next) { \ + MFREE((old), (next)); \ + (prev)->m_next = (next); \ +} +#define KB_ISPKT(bfr) (((bfr)->m_flags & M_PKTHDR) != 0) +#define KB_ISEXT(bfr) (((bfr)->m_flags & M_EXT) != 0) +#define KB_BFRSTART(bfr, x, t) { \ + if ((bfr)->m_flags & M_EXT) \ + (x) = (t)((bfr)->m_ext.ext_buf); \ + else if ((bfr)->m_flags & M_PKTHDR) \ + (x) = (t)(&(bfr)->m_pktdat); \ + else \ + (x) = (t)((bfr)->m_dat); \ +} +#define KB_BFREND(bfr, x, t) { \ + if ((bfr)->m_flags & M_EXT) \ + (x) = (t)((bfr)->m_ext.ext_buf + (bfr)->m_ext.ext_size);\ + else if ((bfr)->m_flags & M_PKTHDR) \ + (x) = (t)(&(bfr)->m_pktdat + MHLEN); \ + else \ + (x) = (t)((bfr)->m_dat + MLEN); \ +} +#define KB_BFRLEN(bfr) \ + (((bfr)->m_flags & M_EXT) ? (bfr)->m_ext.ext_size : \ + (((bfr)->m_flags & M_PKTHDR) ? MHLEN : MLEN)) +#define KB_DATASTART(bfr, x, t) { \ + (x) = mtod((bfr), t); \ +} +#define KB_DATAEND(bfr, x, t) { \ + (x) = (t)(mtod((bfr), caddr_t) + (bfr)->m_len); \ +} +#define KB_HEADSET(bfr, n) { \ + if ((bfr)->m_flags & M_EXT) \ + (bfr)->m_data = (bfr)->m_ext.ext_buf + (n); \ + else if ((bfr)->m_flags & M_PKTHDR) \ + (bfr)->m_data = (bfr)->m_pktdat + (n); \ + else \ + (bfr)->m_data = (bfr)->m_dat + (n); \ +} +#define KB_HEADMOVE(bfr, n) { \ + (bfr)->m_data += (n); \ +} +#define KB_HEADADJ(bfr, n) { \ + (bfr)->m_len += (n); \ + (bfr)->m_data -= (n); \ +} +#define KB_TAILADJ(bfr, n) { \ + (bfr)->m_len += (n); \ +} +#define KB_TAILALIGN(bfr, n) { \ + (bfr)->m_len = (n); \ + if ((bfr)->m_flags & M_EXT) \ + (bfr)->m_data = (caddr_t)(((u_int)(bfr)->m_ext.ext_buf \ + + (bfr)->m_ext.ext_size - (n)) & ~(sizeof(long) - 1));\ + else \ + (bfr)->m_data = (caddr_t)(((u_int)(bfr)->m_dat + MLEN - (n)) \ + & ~(sizeof(long) - 1)); \ +} +#define KB_HEADROOM(bfr, n) { \ + /* n = M_LEADINGSPACE(bfr) XXX */ \ + (n) = ((bfr)->m_flags & M_EXT ? (bfr)->m_data - (bfr)->m_ext.ext_buf : \ + (bfr)->m_flags & M_PKTHDR ? (bfr)->m_data - (bfr)->m_pktdat : \ + (bfr)->m_data - (bfr)->m_dat); \ +} +#define KB_TAILROOM(bfr, n) { \ + (n) = M_TRAILINGSPACE(bfr); \ +} +#define KB_PLENGET(bfr, n) { \ + (n) = (bfr)->m_pkthdr.len; \ +} +#define KB_PLENSET(bfr, n) { \ + (bfr)->m_pkthdr.len = (n); \ +} +#define KB_PLENADJ(bfr, n) { \ + (bfr)->m_pkthdr.len += (n); \ +} + + +#else /* ! BSD >= 199103 */ + + +#define KB_NEXT(bfr) (bfr)->m_next +#define KB_LEN(bfr) (bfr)->m_len +#define KB_QNEXT(bfr) (bfr)->m_act +#define KB_ALLOC(bfr, size, flags, type) { \ + if ((size) <= MLEN) { \ + MGET((bfr), (flags), (type)); \ + } else \ + (bfr) = NULL; \ +} +#define KB_ALLOCPKT(bfr, size, flags, type) { \ + if ((size) <= MLEN) { \ + MGET((bfr), (flags), (type)); \ + } else \ + (bfr) = NULL; \ +} +#define KB_ALLOCEXT(bfr, size, flags, type) { \ + if ((size) <= MCLBYTES) { \ + MGET((bfr), (flags), (type)); \ + if ((bfr) != NULL) { \ + MCLGET(bfr); \ + if ((bfr)->m_len != MCLBYTES) { \ + m_freem((bfr)); \ + (bfr) = NULL; \ + } \ + } \ + } else \ + (bfr) = NULL; \ +} +#define KB_FREEONE(bfr, nxt) { \ + (nxt) = m_free(bfr); \ +} +#define KB_FREEALL(bfr) { \ + m_freem(bfr); \ +} +#define KB_COPY(bfr, off, len, new, flags) { \ + (new) = m_copy((bfr), (off), (len)); \ +} +#define KB_COPYDATA(bfr, off, len, datap) \ + m_cpytoc((bfr), (off), (len), (datap)) +#define KB_PULLUP(bfr, n, new) { \ + (new) = m_pullup((bfr), (n)); \ +} +#define KB_LINKHEAD(new, head) { \ + (new)->m_next = (head); \ +} +#define KB_LINK(new, prev) { \ + (new)->m_next = (prev)->m_next; \ + (prev)->m_next = (new); \ +} +#define KB_UNLINKHEAD(head, next) { \ + MFREE((head), (next)); \ +} +#define KB_UNLINK(old, prev, next) { \ + MFREE((old), (next)); \ + (prev)->m_next = (next); \ +} +#define KB_ISPKT(bfr) (0) +#define KB_ISEXT(bfr) M_HASCL(bfr) +#define KB_BFRSTART(bfr, x, t) { \ + if (M_HASCL(bfr)) { \ + if ((bfr)->m_cltype == MCL_STATIC) \ + (x) = (t)(mtod((bfr), int) & ~(MCLBYTES - 1)); \ + else \ + (x) = (t)NULL; \ + } else \ + (x) = (t)((bfr)->m_dat); \ +} +#define KB_BFREND(bfr, x, t) { \ + if (M_HASCL(bfr)) { \ + if ((bfr)->m_cltype == MCL_STATIC) \ + (x) = (t)((mtod((bfr), int) & ~(MCLBYTES - 1)) \ + + MCLBYTES); \ + else \ + (x) = (t)NULL; \ + } else \ + (x) = (t)((bfr)->m_dat + MLEN); \ +} +#define KB_BFRLEN(bfr) \ + (M_HASCL(bfr) ? (((bfr)->m_cltype == MCL_STATIC) ? MCLBYTES : 0) : MLEN) +#define KB_DATASTART(bfr, x, t) { \ + (x) = mtod((bfr), t); \ +} +#define KB_DATAEND(bfr, x, t) { \ + (x) = (t)(mtod((bfr), caddr_t) + (bfr)->m_len); \ +} +#define KB_HEADSET(bfr, n) { \ + if (M_HASCL(bfr)) { \ + /* Assume cluster buffer is empty XXX */\ + (bfr)->m_off += (n); \ + } else \ + (bfr)->m_off = MMINOFF + (n); \ +} +#define KB_HEADMOVE(bfr, n) { \ + (bfr)->m_off += (n); \ +} +#define KB_HEADADJ(bfr, n) { \ + (bfr)->m_len += (n); \ + (bfr)->m_off -= (n); \ +} +#define KB_TAILADJ(bfr, n) { \ + (bfr)->m_len += (n); \ +} +#define KB_TAILALIGN(bfr, n) { \ + (bfr)->m_len = (n); \ + if (M_HASCL(bfr)) { \ + if ((bfr)->m_cltype == MCL_STATIC) \ + (bfr)->m_off = (int)(((mtod((bfr), int) \ + & ~(MCLBYTES - 1)) + MCLBYTES - (n)) \ + & ~(sizeof(long) - 1)) - (int)(bfr); \ + /* Out of luck for loaned buffers */ \ + } else \ + (bfr)->m_off = (MMAXOFF - (n)) & ~(sizeof(long) - 1); \ +} +#define KB_HEADROOM(bfr, n) { \ + if (M_HASCL(bfr)) { \ + if ((bfr)->m_cltype == MCL_STATIC) \ + (n) = mtod((bfr), int) & (MCLBYTES - 1); \ + else \ + (n) = 0; \ + } else \ + (n) = (bfr)->m_off - MMINOFF; \ +} +#define KB_TAILROOM(bfr, n) { \ + if (M_HASCL(bfr)) { \ + if ((bfr)->m_cltype == MCL_STATIC) \ + (n) = MCLBYTES - ((mtod((bfr), int) + (bfr)->m_len) \ + & (MCLBYTES - 1)); \ + else \ + (n) = 0; \ + } else \ + (n) = MMAXOFF - ((bfr)->m_off + (bfr)->m_len); \ +} +#define KB_PLENGET(bfr, n) { \ + struct mbuf *zz; \ + for ((n) = 0, zz = (bfr); zz; zz = zz->m_next) \ + (n) += zz->m_len; \ +} +#define KB_PLENSET(bfr, n) { \ +} +#define KB_PLENADJ(bfr, n) { \ +} + + +#endif /* ! BSD >= 199103 */ + +#endif /* defined(BSD) */ + + +/* + * Kernel time + * + * KTimeout_ret Typedef for timeout() function return + * + * KT_TIME(t) Sets t to the current time. + * + */ +#if (defined(BSD) && (BSD >= 199306)) +typedef void KTimeout_ret; +#else +typedef int KTimeout_ret; +#endif +#if (defined(BSD) && (BSD >= 199103)) +#define KT_TIME(t) microtime(&t) +#elif defined(sun) +#define KT_TIME(t) uniqtime(&t) +#else +#define KT_TIME(t) ((t) = time) +#endif + +#endif /* ATM_KERNEL */ + +#ifndef NTOHL +#if BYTE_ORDER == BIG_ENDIAN +#define NTOHL(x) (x) +#define NTOHS(x) (x) +#define HTONL(x) (x) +#define HTONS(x) (x) +#else +#define NTOHL(x) (x) = ntohl((u_long)(x)) +#define NTOHS(x) (x) = ntohs((u_short)(x)) +#define HTONL(x) (x) = htonl((u_long)(x)) +#define HTONS(x) (x) = htons((u_short)(x)) +#endif +#endif /* NTOHL */ + +#ifndef MAX +#define MAX(a,b) max((a),(b)) +#endif +#ifndef MIN +#define MIN(a,b) min((a),(b)) +#endif + +#if (!(defined(BSD) && (BSD >= 199306))) +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ +typedef char int8_t; +typedef unsigned char u_int8_t; +typedef short int16_t; +typedef unsigned short u_int16_t; +typedef int int32_t; +typedef unsigned int u_int32_t; +#endif +#endif + +#endif /* _NETATM_PORT_H */ diff --git a/sys/netatm/queue.h b/sys/netatm/queue.h new file mode 100644 index 0000000..8a8febf --- /dev/null +++ b/sys/netatm/queue.h @@ -0,0 +1,213 @@ +/* + * + * =================================== + * 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: queue.h,v 1.2 1997/05/06 22:14:38 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * General queueing/linking definitions + * + */ + +#ifndef _NETATM_QUEUE_H +#define _NETATM_QUEUE_H + +/* + * Structure defining the queue controls for a doubly linked queue + */ +struct q_queue { + caddr_t q_head; /* Head of queue */ + caddr_t q_tail; /* Tail of queue */ +}; +typedef struct q_queue Queue_t; + +/* + * Structure defining the queue elements of a doubly linked queue + */ +struct q_elem { + caddr_t q_forw; /* Forward link */ + caddr_t q_back; /* Backward link */ +}; +typedef struct q_elem Qelem_t; + +/* + * Macro to add a control block onto the tail of a doubly linked queue + * e = control block to add + * t = control block structure type + * el = name of control block's q_elem field + * q = pointer to queue controls + */ +#define ENQUEUE(e,t,el,q) \ +{ \ + (e)->el.q_forw = NULL; \ + (e)->el.q_back = (q).q_tail; \ + if ((q).q_head == NULL) { \ + (q).q_head = (caddr_t)(e); \ + (q).q_tail = (caddr_t)(e); \ + } else { \ + ((t *)(q).q_tail)->el.q_forw = (caddr_t)(e); \ + (q).q_tail = (caddr_t)(e); \ + } \ +} + +/* + * Macro to remove a control block from a doubly linked queue + * e = control block to remove + * t = control block structure type + * el = name of control block's q_elem field + * q = pointer to queue controls + */ +#define DEQUEUE(e,t,el,q) \ +{ \ + /* Ensure control block is on queue */ \ + if ((e)->el.q_forw || (q).q_tail == (caddr_t)(e)) { \ + if ((e)->el.q_forw) \ + ((t *)(e)->el.q_forw)->el.q_back = (e)->el.q_back;\ + else \ + (q).q_tail = (e)->el.q_back; \ + if ((e)->el.q_back) \ + ((t *)(e)->el.q_back)->el.q_forw = (e)->el.q_forw;\ + else \ + (q).q_head = (e)->el.q_forw; \ + } \ + (e)->el.q_back = (e)->el.q_forw = NULL; \ +} + +/* + * Macro to return the head of a doubly linked queue + * q = pointer to queue controls + * t = control block structure type + */ +#define Q_HEAD(q,t) ((t *)(q).q_head) + +/* + * Macro to return the next control block of a doubly linked queue + * e = current control block + * t = control block structure type + * el = name of control block's q_elem field + */ +#define Q_NEXT(e,t,el) ((t *)(e)->el.q_forw) + + +/* + * Macro to add a control block onto the head of a singly linked chain + * u = control block to add + * t = structure type + * h = head of chain + * l = name of link field + */ +#define LINK2HEAD(u,t,h,l) \ +{ \ + (u)->l = (h); \ + (h) = (u); \ +} + +/* + * Macro to add a control block onto the tail of a singly linked chain + * u = control block to add + * t = structure type + * h = head of chain + * l = name of link field + */ +#define LINK2TAIL(u,t,h,l) \ +{ \ + (u)->l = (t *)NULL; \ + /* Check for empty chain */ \ + if ((h) == (t *)NULL) { \ + (h) = (u); \ + } else { \ + t *tp; \ + /* Loop until we find the end of chain */ \ + for (tp = (h); tp->l != (t *)NULL; tp = tp->l) \ + ; \ + tp->l = (u); \ + } \ +} + +/* + * Macro to remove a control block from a singly linked chain + * u = control block to unlink + * t = structure type + * h = head of chain + * l = name of link field + */ +#define UNLINK(u,t,h,l) \ +{ \ + /* Check for control block at head of chain */ \ + if ((u) == (h)) { \ + (h) = (u)->l; \ + } else { \ + t *tp; \ + /* Loop until we find the control block */ \ + for (tp = (h); tp != (t *)NULL; tp = tp->l) { \ + if (tp->l == (u)) \ + break; \ + } \ + if (tp) { \ + /* Remove it from chain */ \ + tp->l = (u)->l; \ + } \ + } \ + (u)->l = (t *)NULL; \ +} + +/* + * Macro to remove a control block from a singly linked chain and return + * an indication of whether the block was found + * u = control block to unlink + * t = structure type + * h = head of chain + * l = name of link field + * f = flag; 1 => control block found on chain; else 0 + */ +#define UNLINKF(u,t,h,l,f) \ +{ \ + /* Check for control block at head of chain */ \ + if ((u) == (h)) { \ + (h) = (u)->l; \ + (f) = 1; \ + } else { \ + t *tp; \ + /* Loop until we find the control block */ \ + for (tp = (h); tp != (t *)NULL; tp = tp->l) { \ + if (tp->l == (u)) \ + break; \ + } \ + if (tp) { \ + /* Remove it from chain */ \ + tp->l = (u)->l; \ + (f) = 1; \ + } else \ + /* It wasn't on the chain */ \ + (f) = 0; \ + } \ + (u)->l = (t *)NULL; \ +} + +#endif /* _NETATM_QUEUE_H */ diff --git a/sys/netatm/sigpvc/sigpvc.h b/sys/netatm/sigpvc/sigpvc.h new file mode 100644 index 0000000..893a682 --- /dev/null +++ b/sys/netatm/sigpvc/sigpvc.h @@ -0,0 +1,47 @@ +/* + * + * =================================== + * 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: sigpvc.h,v 1.2 1997/05/06 22:15:43 mks Exp $ + * + */ + +/* + * PVC-only Signalling Manager + * --------------------------- + * + * Protocol definitions + * + */ + +#ifndef _SIGPVC_SIGPVC_H +#define _SIGPVC_SIGPVC_H + +/* + * Protocol Variables + */ +#define SIGPVC_DOWN_DELAY (15 * ATM_HZ) /* Delay til i/f marked down */ +#define SIGPVC_UP_DELAY (5 * ATM_HZ) /* Delay til i/f marked up */ + +#endif /* _SIGPVC_SIGPVC_H */ diff --git a/sys/netatm/sigpvc/sigpvc_if.c b/sys/netatm/sigpvc/sigpvc_if.c new file mode 100644 index 0000000..4208f29 --- /dev/null +++ b/sys/netatm/sigpvc/sigpvc_if.c @@ -0,0 +1,953 @@ +/* + * + * =================================== + * 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: sigpvc_if.c,v 1.13 1998/07/30 22:32:42 mks Exp $ + * + */ + +/* + * PVC-only Signalling Manager + * --------------------------- + * + * External interfaces to SigPVC manager. Includes support for + * running as a loadable kernel module. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sigpvc_if.c,v 1.13 1998/07/30 22:32:42 mks Exp $"; +#endif + +#ifndef ATM_SIGPVC_MODULE +#include "opt_atm.h" +#endif + +#include <netatm/kern_include.h> + +#include <netatm/sigpvc/sigpvc.h> +#include <netatm/sigpvc/sigpvc_var.h> + + +/* + * Global variables + */ +struct sp_info sigpvc_vcpool = { + "sigpvc vcc pool", /* si_name */ + sizeof(struct sigpvc_vccb), /* si_blksiz */ + 10, /* si_blkcnt */ + 50 /* si_maxallow */ +}; + +/* + * Local functions + */ +static int sigpvc_start __P((void)); +static int sigpvc_stop __P((void)); +static int sigpvc_attach __P((struct sigmgr *, struct atm_pif *)); +static int sigpvc_detach __P((struct atm_pif *)); +static int sigpvc_setup __P((Atm_connvc *, int *)); +static int sigpvc_release __P((struct vccb *, int *)); +static int sigpvc_free __P((struct vccb *)); +static int sigpvc_ioctl __P((int, caddr_t, caddr_t)); + +/* + * Local variables + */ +static int sigpvc_registered = 0; +static struct sigmgr sigpvc_mgr = { + NULL, + ATM_SIG_PVC, + NULL, + sigpvc_attach, + sigpvc_detach, + sigpvc_setup, + NULL, + NULL, + sigpvc_release, + sigpvc_free, + sigpvc_ioctl +}; + +static struct attr_cause sigpvc_cause = { + T_ATM_PRESENT, + { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_UNSPECIFIED_NORMAL, + {0, 0, 0, 0} + } +}; + + +/* + * Initialize sigpvc processing + * + * This will be called during module loading. We'll just register + * the sigpvc protocol descriptor and wait for a SigPVC ATM interface + * to come online. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +sigpvc_start() +{ + int err = 0; + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: sigpvc=%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); + } + + /* + * Register ourselves with system + */ + err = atm_sigmgr_register(&sigpvc_mgr); + if (err == 0) + sigpvc_registered = 1; + + return (err); +} + + +/* + * Halt sigpvc processing + * + * This should be called just prior to unloading the module from + * memory. All sigpvc interfaces must be deregistered before the + * protocol can be shutdown. + * + * Arguments: + * none + * + * Returns: + * 0 shutdown was successful + * errno shutdown failed - reason indicated + * + */ +static int +sigpvc_stop() +{ + int err = 0; + int s = splnet(); + + /* + * Is protocol even setup? + */ + if (sigpvc_registered) { + + /* + * Any protocol instances still registered?? + */ + if (sigpvc_mgr.sm_prinst) { + + /* Yes, can't stop now */ + err = EBUSY; + goto done; + } + + /* + * De-register from system + */ + err = atm_sigmgr_deregister(&sigpvc_mgr); + sigpvc_registered = 0; + + /* + * Free up our vccb storage pool + */ + atm_release_pool(&sigpvc_vcpool); + } else + err = ENXIO; + +done: + (void) splx(s); + return (err); +} + + +/* + * Attach a SigPVC-controlled interface + * + * Each ATM physical interface must be attached with the signalling manager for + * the interface's signalling protocol (via the atm_sigmgr_attach function). + * This function will handle the attachment for SigPVC-controlled interfaces. + * A new sigpvc protocol instance will be created and then we'll just sit + * around waiting for connection requests. + * + * Function must be called at splnet. + * + * Arguments: + * smp pointer to sigpvc signalling manager control block + * pip pointer to atm physical interface control block + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +static int +sigpvc_attach(smp, pip) + struct sigmgr *smp; + struct atm_pif *pip; +{ + int err = 0; + struct sigpvc *pvp = NULL; + + /* + * Allocate sigpvc protocol instance control block + */ + pvp = (struct sigpvc *) + KM_ALLOC(sizeof(struct sigpvc), M_DEVBUF, M_NOWAIT); + if (pvp == NULL) { + err = ENOMEM; + goto done; + } + KM_ZERO(pvp, sizeof(struct sigpvc)); + + /* + * Link instance into manager's chain + */ + LINK2TAIL((struct siginst *)pvp, struct siginst, + smp->sm_prinst, si_next); + + /* + * Finally, set state and link in interface + */ + pvp->pv_pif = pip; + pvp->pv_state = SIGPVC_ACTIVE; + pip->pif_sigmgr = smp; + pip->pif_siginst = (struct siginst *)pvp; + +done: + /* + * Reset our work if attach fails + */ + if (err) { + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + if (pvp) { + UNLINK((struct siginst *)pvp, struct siginst, + smp->sm_prinst, si_next); + KM_FREE(pvp, sizeof(struct sigpvc), M_DEVBUF); + } + } + + return (err); +} + + +/* + * Detach a SigPVC-controlled interface + * + * Each ATM physical interface may be detached from its signalling manager + * (via the atm_sigmgr_detach function). This function will handle the + * detachment for all SigPVC-controlled interfaces. All circuits will be + * immediately terminated. + * + * Function must be called at splnet. + * + * Arguments: + * pip pointer to atm physical interface control block + * + * Returns: + * 0 detach successful + * errno detach failed - reason indicated + * + */ +static int +sigpvc_detach(pip) + struct atm_pif *pip; +{ + struct sigpvc *pvp; + struct vccb *vcp, *vnext; + + /* + * Get SigPVC protocol instance + */ + pvp = (struct sigpvc *)pip->pif_siginst; + + /* + * Terminate all of our VCCs + */ + for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; vcp = vnext){ + u_char oustate; + + vnext = Q_NEXT(vcp, struct vccb, vc_sigelem); + + /* + * Close VCC and notify owner + */ + oustate = vcp->vc_ustate; + sigpvc_close_vcc(vcp); + if (oustate == VCCU_OPEN) { + vcp->vc_connvc->cvc_attr.cause = sigpvc_cause; + atm_cm_cleared(vcp->vc_connvc); + } + } + + /* + * If there are no vcc's queued, then get rid of the protocol + * instance. + */ + if (Q_HEAD(pvp->pv_vccq, struct vccb) == NULL) { + struct sigmgr *smp = pip->pif_sigmgr; + + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + UNLINK((struct siginst *)pvp, struct siginst, smp->sm_prinst, + si_next); + KM_FREE(pvp, sizeof(struct sigpvc), M_DEVBUF); + } else { + + /* + * Otherwise, set new state indicating detach in progress. + * The protocol instance will be freed during sigpvc_free + * processing for the last queued vcc. + */ + pvp->pv_state = SIGPVC_DETACH; + } + + return (0); +} + + +/* + * Open a SigPVC ATM Connection + * + * All service user requests to open a VC connection (via atm_open_connection) + * over an ATM interface attached to the SigPVC signalling manager are handled + * here. Only PVC requests are allowed. + * + * Function will be called at splnet. + * + * Arguments: + * cvp pointer to CM's connection VCC + * errp location to store an error code if CALL_FAILED is returned + * + * Returns: + * CALL_PROCEEDING - connection establishment is in progress + * CALL_FAILED - connection establishment failed + * CALL_CONNECTED - connection has been successfully established + * + */ +static int +sigpvc_setup(cvp, errp) + Atm_connvc *cvp; + int *errp; +{ + struct sigpvc *pvp = + (struct sigpvc *)cvp->cvc_attr.nif->nif_pif->pif_siginst; + int ret; + + /* + * See what signalling has to say + */ + switch (pvp->pv_state) { + + case SIGPVC_ACTIVE: + break; + + default: + *errp = ENXIO; + ret = CALL_FAILED; + goto done; + } + + /* + * Open requested type of connection + */ + switch (cvp->cvc_attr.called.addr.address_format) { + + case T_ATM_PVC_ADDR: + /* + * Create a PVC + */ + ret = sigpvc_create_pvc(pvp, cvp, errp); + break; + + default: + *errp = EPROTONOSUPPORT; + ret = CALL_FAILED; + } + +done: + return (ret); +} + + +/* + * Close a SigPVC ATM Connection + * + * All service user requests to terminate a previously open VC connection + * (via the atm_close_connection function), which is running over an interface + * attached to the SigPVC signalling manager, are handled here. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * errp location to store an error code if CALL_FAILED is returned + * + * Returns: + * CALL_PROCEEDING - connection termination is in progress + * CALL_FAILED - connection termination failed + * CALL_CLEARED - connection has been successfully terminated + * + */ +static int +sigpvc_release(vcp, errp) + struct vccb *vcp; + int *errp; +{ + + /* + * Make sure VCC is open + */ + if ((vcp->vc_sstate == VCCS_NULL) || (vcp->vc_sstate == VCCS_FREE) || + (vcp->vc_ustate == VCCU_NULL) || (vcp->vc_ustate == VCCU_CLOSED)) { + *errp = EALREADY; + return (CALL_FAILED); + } + + /* + * Not much else to do except close the vccb + */ + sigpvc_close_vcc(vcp); + + return (CALL_CLEARED); +} + + +/* + * Free SigPVC ATM Connection Resources + * + * All service user requests to free the resources of a closed VCC connection + * (via the atm_free_connection function), which is running over an interface + * attached to the SigPVC signalling manager, are handled here. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VCC control block + * + * Returns: + * 0 connection free was successful + * errno connection free failed - reason indicated + * + */ +static int +sigpvc_free(vcp) + struct vccb *vcp; +{ + struct atm_pif *pip = vcp->vc_pif; + struct sigpvc *pvp = (struct sigpvc *)pip->pif_siginst; + + /* + * Make sure VCC has been closed + */ + if ((vcp->vc_ustate != VCCU_CLOSED) || (vcp->vc_sstate != VCCS_FREE)) + return (EEXIST); + + /* + * Remove vccb from protocol queue + */ + DEQUEUE(vcp, struct vccb, vc_sigelem, pvp->pv_vccq); + + /* + * Free vccb storage + */ + vcp->vc_ustate = VCCU_NULL; + vcp->vc_sstate = VCCS_NULL; + atm_free((caddr_t)vcp); + + /* + * If we're detaching and this was the last vcc queued, + * get rid of the protocol instance + */ + if ((pvp->pv_state == SIGPVC_DETACH) && + (Q_HEAD(pvp->pv_vccq, struct vccb) == NULL)) { + struct sigmgr *smp = pip->pif_sigmgr; + + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + UNLINK((struct siginst *)pvp, struct siginst, smp->sm_prinst, + si_next); + KM_FREE(pvp, sizeof(struct sigpvc), M_DEVBUF); + } + + return (0); +} + + +/* + * Process Signalling Manager PF_ATM ioctls + * + * Function will be called at splnet. + * + * Arguments: + * code PF_ATM sub-operation code + * data pointer to code specific parameter data area + * arg1 pointer to code specific argument + * + * Returns: + * 0 request procesed + * errno error processing request - reason indicated + * + */ +static int +sigpvc_ioctl(code, data, arg1) + int code; + caddr_t data; + caddr_t arg1; +{ + struct atmdelreq *adp; + struct atminfreq *aip; + struct air_vcc_rsp avr; + struct sigpvc *pvp; + struct vccb *vcp; + Atm_connection *cop; + caddr_t cp; + u_int vpi, vci; + int i, space, err = 0; + + + switch (code) { + + case AIOCS_DEL_PVC: + /* + * Delete a PVC + */ + adp = (struct atmdelreq *)data; + pvp = (struct sigpvc *)arg1; + + /* + * Find requested VCC + */ + vpi = adp->adr_pvc_vpi; + vci = adp->adr_pvc_vci; + for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; + vcp = Q_NEXT(vcp, struct vccb, vc_sigelem)) { + if ((vcp->vc_vpi == vpi) && (vcp->vc_vci == vci)) + break; + } + if (vcp == NULL) + return (ENOENT); + + /* + * Schedule VCC termination + */ + err = atm_cm_abort(vcp->vc_connvc, &sigpvc_cause.v); + break; + + case AIOCS_DEL_SVC: + /* + * Delete a SVC + */ + err = ENOENT; + break; + + case AIOCS_INF_VCC: + /* + * Get VCC information + */ + aip = (struct atminfreq *)data; + pvp = (struct sigpvc *)arg1; + + cp = aip->air_buf_addr; + space = aip->air_buf_len; + + /* + * Get info for all VCCs on interface + */ + for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; + vcp = Q_NEXT(vcp, struct vccb, vc_sigelem)) { + /* + * Make sure there's room in user buffer + */ + if (space < sizeof(avr)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + (void) sprintf(avr.avp_intf, "%s%d", + pvp->pv_pif->pif_name, pvp->pv_pif->pif_unit); + avr.avp_vpi = vcp->vc_vpi; + avr.avp_vci = vcp->vc_vci; + avr.avp_type = vcp->vc_type; + avr.avp_sig_proto = ATM_SIG_PVC; + avr.avp_aal = vcp->vc_connvc->cvc_attr.aal.type; + cop = vcp->vc_connvc->cvc_conn; + if (cop) + avr.avp_encaps = cop->co_mpx; + else + avr.avp_encaps = 0; + KM_ZERO(avr.avp_owners, sizeof(avr.avp_owners)); + for (i = 0; cop && i < sizeof(avr.avp_owners); + cop = cop->co_next, + i += T_ATM_APP_NAME_LEN+1) { + strncpy(&avr.avp_owners[i], + cop->co_endpt->ep_getname(cop->co_toku), + T_ATM_APP_NAME_LEN); + } + avr.avp_state = vcp->vc_sstate; + avr.avp_daddr.address_format = T_ATM_ABSENT; + avr.avp_dsubaddr.address_format = T_ATM_ABSENT; + avr.avp_ipdus = vcp->vc_ipdus; + avr.avp_opdus = vcp->vc_opdus; + avr.avp_ibytes = vcp->vc_ibytes; + avr.avp_obytes = vcp->vc_obytes; + avr.avp_ierrors = vcp->vc_ierrors; + avr.avp_oerrors = vcp->vc_oerrors; + avr.avp_tstamp = vcp->vc_tstamp; + + /* + * Copy data to user buffer and update buffer info + */ + if (err = copyout((caddr_t)&avr, cp, sizeof(avr))) + break; + cp += sizeof(avr); + space -= sizeof(avr); + } + + /* + * Update buffer pointer/count + */ + aip->air_buf_addr = cp; + aip->air_buf_len = space; + break; + + case AIOCS_INF_ARP: + /* + * Get ARP table information + */ + /* We don't maintain any ARP information */ + break; + + default: + err = EOPNOTSUPP; + } + + return (err); +} + + +#ifdef ATM_SIGPVC_MODULE +/* + ******************************************************************* + * + * Loadable Module Support + * + ******************************************************************* + */ +static int sigpvc_doload __P((void)); +static int sigpvc_dounload __P((void)); + +/* + * 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 +sigpvc_doload() +{ + int err = 0; + + /* + * Start us up + */ + err = sigpvc_start(); + if (err) + /* Problems, clean up */ + (void)sigpvc_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 +sigpvc_dounload() +{ + int err = 0; + + /* + * OK, try to clean up our mess + */ + err = sigpvc_stop(); + + return (err); +} + + +#ifdef sun +/* + * Loadable driver description + */ +struct vdldrv sigpvc_drv = { + VDMAGIC_PSEUDO, /* Pseudo Driver */ + "sigpvc_mod", /* name */ + NULL, /* 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 +sigpvc_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 = sigpvc_doload(); + if (err == 0) + /* Let vd driver know about us */ + vdp->vdd_vdtab = (struct vdlinkage *)&sigpvc_drv; + break; + + case VDUNLOAD: + /* + * Module Unload + */ + err = sigpvc_dounload(); + break; + + case VDSTAT: + /* + * Module Status + */ + + /* Not much to say at the moment */ + + break; + + default: + log(LOG_ERR, "sigpvc_mod: Unknown vd command 0x%x\n", cmd); + err = EINVAL; + } + + return (err); +} +#endif /* sun */ + +#ifdef __FreeBSD__ + +#include <sys/exec.h> +#include <sys/sysent.h> +#include <sys/lkm.h> + +/* + * Loadable miscellaneous module description + */ +MOD_MISC(sigpvc); + + +/* + * 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 +sigpvc_load(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(sigpvc_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 +sigpvc_unload(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(sigpvc_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 +sigpvc_mod(lkmtp, cmd, ver) + struct lkm_table *lkmtp; + int cmd; + int ver; +{ + MOD_DISPATCH(sigpvc, lkmtp, cmd, ver, + sigpvc_load, sigpvc_unload, lkm_nullcmd); +} +#endif /* __FreeBSD__ */ + +#else /* !ATM_SIGPVC_MODULE */ + +/* + ******************************************************************* + * + * Kernel Compiled Module Support + * + ******************************************************************* + */ +static void sigpvc_doload __P((void *)); + +SYSINIT(atmsigpvc, SI_SUB_PROTO_END, SI_ORDER_ANY, sigpvc_doload, NULL) + +/* + * Kernel initialization + * + * Arguments: + * arg Not used + * + * Returns: + * none + * + */ +static void +sigpvc_doload(void *arg) +{ + int err = 0; + + /* + * Start us up + */ + err = sigpvc_start(); + if (err) { + /* Problems, clean up */ + (void)sigpvc_stop(); + + log(LOG_ERR, "ATM SIGPVC unable to initialize (%d)!!\n", err); + } + return; +} +#endif /* ATM_SIGPVC_MODULE */ + diff --git a/sys/netatm/sigpvc/sigpvc_subr.c b/sys/netatm/sigpvc/sigpvc_subr.c new file mode 100644 index 0000000..00f92ec --- /dev/null +++ b/sys/netatm/sigpvc/sigpvc_subr.c @@ -0,0 +1,181 @@ +/* + * + * =================================== + * 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: sigpvc_subr.c,v 1.7 1998/06/29 21:52:25 mks Exp $ + * + */ + +/* + * PVC-only Signalling Manager + * --------------------------- + * + * Subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sigpvc_subr.c,v 1.7 1998/06/29 21:52:25 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/sigpvc/sigpvc.h> +#include <netatm/sigpvc/sigpvc_var.h> + +extern struct sp_info sigpvc_vcpool; + +/* + * Create a SigPVC Permanent Virtual Channel + * + * This function will construct a vccb for a "sigpvc-controlled" PVC + * and create the service stack requested by the user. + * + * Must be called at splnet. + * + * Arguments: + * pvp pointer to sigpvc protocol instance + * cvp pointer to CM's connection VCC + * errp location to store an error code if CALL_FAILED is returned + * + * Returns: + * CALL_FAILED - pvc creation failed + * CALL_CONNECTED - pvc has been successfully created + * + */ +int +sigpvc_create_pvc(pvp, cvp, errp) + struct sigpvc *pvp; + Atm_connvc *cvp; + int *errp; +{ + Atm_addr_pvc *pp; + struct vccb *vcp; + u_int vpi, vci; + + pp = (Atm_addr_pvc *)cvp->cvc_attr.called.addr.address; + vpi = ATM_PVC_GET_VPI(pp); + vci = ATM_PVC_GET_VCI(pp); + + /* + * Verify requested VPI,VCI + */ + if ((vpi > pvp->pv_pif->pif_maxvpi) || + (vci == 0) || (vci > pvp->pv_pif->pif_maxvci)) { + *errp = ERANGE; + return (CALL_FAILED); + } + + for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; + vcp = Q_NEXT(vcp, struct vccb, vc_sigelem)) { + + if ((vcp->vc_vpi == vpi) && + (vcp->vc_vci == vci)) { + *errp = EADDRINUSE; + return (CALL_FAILED); + } + } + + /* + * Verify network interface + */ + if (cvp->cvc_attr.nif) { + if (cvp->cvc_attr.nif->nif_pif != pvp->pv_pif) { + *errp = EINVAL; + return (CALL_FAILED); + } + } + + /* + * Allocate control block for PVC + */ + vcp = (struct vccb *)atm_allocate(&sigpvc_vcpool); + if (vcp == NULL) { + *errp = ENOMEM; + return (CALL_FAILED); + } + + /* + * Fill in VCCB + */ + vcp->vc_type = VCC_PVC | VCC_IN | VCC_OUT; + vcp->vc_proto = ATM_SIG_PVC; + vcp->vc_sstate = VCCS_ACTIVE; + vcp->vc_ustate = VCCU_OPEN; + vcp->vc_pif = pvp->pv_pif; + vcp->vc_nif = cvp->cvc_attr.nif; + vcp->vc_vpi = vpi; + vcp->vc_vci = vci; + vcp->vc_connvc = cvp; + + /* + * Put VCCB on sigpvc queue + */ + ENQUEUE(vcp, struct vccb, vc_sigelem, pvp->pv_vccq); + + /* + * Pass back VCCB to connection manager + */ + cvp->cvc_vcc = vcp; + + /* + * PVC is ready to go! + */ + return (CALL_CONNECTED); +} + +/* + * Close a SigPVC VCC + * + * Clean up vccb, note that it's closing and wait for its freeing. + * + * Arguments: + * vcp pointer to connection's VCC control block + * + * Returns: + * none + * + */ +void +sigpvc_close_vcc(vcp) + struct vccb *vcp; +{ + + /* + * Sanity check (actually design-flaw check) + */ + if (vcp->vc_connvc->cvc_upcnt || vcp->vc_connvc->cvc_downcnt) + panic("sigpvc_close_vcc: stack call"); + + /* + * Set state variables + */ + vcp->vc_ustate = VCCU_CLOSED; + vcp->vc_sstate = VCCS_FREE; + + /* + * Wait for user to free resources + */ +} + diff --git a/sys/netatm/sigpvc/sigpvc_var.h b/sys/netatm/sigpvc/sigpvc_var.h new file mode 100644 index 0000000..24f92c0 --- /dev/null +++ b/sys/netatm/sigpvc/sigpvc_var.h @@ -0,0 +1,95 @@ +/* + * + * =================================== + * 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: sigpvc_var.h,v 1.5 1998/02/19 20:16:28 mks Exp $ + * + */ + +/* + * PVC-only Signalling Manager + * --------------------------- + * + * Protocol control blocks + * + */ + +#ifndef _SIGPVC_SIGPVC_VAR_H +#define _SIGPVC_SIGPVC_VAR_H + +#ifdef ATM_KERNEL +/* + * Structure containing state information for each SigPVC protocol instance. + * There will be one instance for each ATM device interface using the SigPVC + * signalling manager. + */ +struct sigpvc { + struct siginst pv_inst; /* Common header */ +}; +#define pv_next pv_inst.si_next +#define pv_pif pv_inst.si_pif +#define pv_addr pv_inst.si_addr +#define pv_vccq pv_inst.si_vccq +#define pv_state pv_inst.si_state +#endif /* ATM_KERNEL */ + +/* + * SigPVC Protocol States + */ +#define SIGPVC_ACTIVE 1 /* Active */ +#define SIGPVC_DETACH 2 /* Detach in progress */ + + +#ifdef ATM_KERNEL +/* + * SigPVC Virtual Channel Connection control block. All information regarding + * the state of a SigPVC controlled VCC will be recorded here. There will be + * one SigPVC VCC control block for each SigPVC-controlled VCC. + */ +struct sigpvc_vccb { + struct vccb vcp_hdr; /* Generic vccb */ +}; +#endif /* ATM_KERNEL */ + +/* + * SigPVC VCC Signalling Protocol States + */ +#define VCCS_NULL 0 /* No state */ +#define VCCS_ACTIVE 1 /* Active */ +#define VCCS_FREE 2 /* Waiting for user to free resources */ + + +#ifdef ATM_KERNEL +/* + * Global function declarations + */ + /* sigpvc_if.c */ + + /* sigpvc_subr.c */ +int sigpvc_create_pvc __P((struct sigpvc *, Atm_connvc *, int *)); +void sigpvc_close_vcc __P((struct vccb *)); + +#endif /* ATM_KERNEL */ + +#endif /* _SIGPVC_SIGPVC_VAR_H */ diff --git a/sys/netatm/spans/spans_arp.c b/sys/netatm/spans/spans_arp.c new file mode 100644 index 0000000..5a2443a --- /dev/null +++ b/sys/netatm/spans/spans_arp.c @@ -0,0 +1,1133 @@ +/* + * + * =================================== + * 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: spans_arp.c,v 1.9 1998/06/29 22:03:12 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS CLS - ARP support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_arp.c,v 1.9 1998/06/29 22:03:12 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> +#include <netatm/spans/spans_cls.h> + + +/* + * Global variables + */ +struct spansarp *spansarp_arptab[SPANSARP_HASHSIZ] = {NULL}; + + +/* + * Local functions + */ +static int spansarp_request __P((struct spansarp *)); +static void spansarp_aging __P((struct atm_time *)); +static void spansarp_retry __P((struct atm_time *)); + +/* + * Local variables + */ +static struct atm_time spansarp_timer = {0, 0}; /* Aging timer */ +static struct atm_time spansarp_rtimer = {0, 0}; /* Retry timer */ + +static struct spansarp *spansarp_retry_head = NULL; /* Retry chain */ + +static struct sp_info spansarp_pool = { + "spans arp pool", /* si_name */ + sizeof(struct spansarp), /* si_blksiz */ + 10, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + + +/* + * Process a new outgoing SVC requiring SPANS ARP support + * + * This function is called by an endpoint wishing to resolve a destination + * IP address to an ATM address in order to open an SVC to that destination. + * If a valid mapping is already in our cache, then we just tell the caller + * about it and that's that. Otherwise, we have to allocate a new arp entry + * and issue a query for the mapping. + * + * Arguments: + * ivp pointer to SVC's IPVCC control block + * dst pointer to destination IP address + * + * Returns: + * MAP_VALID - Got the answer, returned via iv_arpent field. + * MAP_PROCEEDING - OK so far, querying for peer's mapping + * MAP_FAILED - error, unable to allocate resources + * + */ +int +spansarp_svcout(ivp, dst) + struct ipvcc *ivp; + struct in_addr *dst; +{ + struct spanscls *clp; + struct spansarp *sap; + int s; + + ivp->iv_arpent = NULL; + + /* + * Lookup destination address + */ + s = splnet(); + SPANSARP_LOOKUP(dst->s_addr, sap); + + if (sap) { + /* + * Link this vcc to entry queue + */ + LINK2TAIL(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); + + /* + * If entry is valid, we're done + */ + if (sap->sa_flags & SAF_VALID) { + ivp->iv_arpent = (struct arpmap *)sap; + (void) splx(s); + return (MAP_VALID); + } + + /* + * We're already looking for this address + */ + (void) splx(s); + return (MAP_PROCEEDING); + } + + /* + * Need a new arp entry - first, find the cls instance + * corresponding to the requestor's IP interface. + */ + for (clp = spanscls_head; clp; clp = clp->cls_next) { + if (clp->cls_ipnif == ivp->iv_ipnif) + break; + } + if (clp == NULL) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Now get the new arp entry + */ + sap = (struct spansarp *)atm_allocate(&spansarp_pool); + if (sap == NULL) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Get entry set up + */ + sap->sa_dstip.s_addr = dst->s_addr; + sap->sa_dstatm.address_format = T_ATM_ABSENT; + sap->sa_dstatm.address_length = 0; + sap->sa_dstatmsub.address_format = T_ATM_ABSENT; + sap->sa_dstatmsub.address_length = 0; + sap->sa_cls = clp; + sap->sa_origin = SAO_LOOKUP; + + /* + * Link ipvcc to arp entry for later notification + */ + LINK2TAIL(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); + + /* + * Add arp entry to table + */ + SPANSARP_ADD(sap); + + /* + * Add arp entry to retry list and start retry timer if needed + */ + LINK2TAIL(sap, struct spansarp, spansarp_retry_head, sa_rnext); + if ((spansarp_rtimer.ti_flag & TIF_QUEUED) == 0) + atm_timeout(&spansarp_rtimer, SPANSARP_RETRY, spansarp_retry); + + /* + * Issue arp request for this address + */ + (void) spansarp_request(sap); + + (void) splx(s); + return (MAP_PROCEEDING); +} + + +/* + * Process a new incoming SVC requiring SPANS ARP support + * + * This function is called by an endpoint wishing to resolve a destination + * ATM address to its IP address for an incoming call in order to allow a + * bi-directional flow of IP packets on the SVC. + * + * SPANS ARP does not provide reverse mapping facilities and only supports + * uni-directional SVCs. Thus, we lie a little to IP and always return a + * MAP_PROCEEDING indication, but we will never later notify IP of a + * MAP_VALID condition. + * + * Arguments: + * ivp pointer to SVC's IPVCC control block + * dst pointer to destination ATM address + * dstsub pointer to destination ATM subaddress + * + * Returns: + * MAP_VALID - Got the answer, returned via iv_arpent field. + * MAP_PROCEEDING - OK so far, querying for peer's mapping + * MAP_FAILED - error, unable to allocate resources + * + */ +int +spansarp_svcin(ivp, dst, dstsub) + struct ipvcc *ivp; + Atm_addr *dst; + Atm_addr *dstsub; +{ + /* + * Clear ARP entry field + */ + ivp->iv_arpent = NULL; + + return (MAP_PROCEEDING); +} + + +/* + * SPANS ARP SVC activation notification + * + * This function is called when a previously opened SVC has successfully + * been connected. + * + * Arguments: + * ivp pointer to SVC's IPVCC control block + * + * Returns: + * 0 activation processing successful + * errno activation failed - reason indicated + * + */ +int +spansarp_svcactive(ivp) + struct ipvcc *ivp; +{ + struct spansarp *sap; + int s = splnet(); + + /* + * Find an entry for the destination address + */ + SPANSARP_LOOKUP(ivp->iv_dst.s_addr, sap); + if (sap) { + /* + * IP is finished with entry, so remove IP VCC from chain + */ + UNLINK(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); + ivp->iv_arpent = NULL; + + /* + * This seems like a reasonable reason to refresh the entry + */ + sap->sa_reftime = 0; + } + + (void) splx(s); + return (0); +} + + +/* + * SPANS ARP supported VCC is closing + * + * This function is called just prior to a user closing a VCC which + * supports SPANS ARP. We'll sever our links to the VCC and then + * figure out how much more cleanup we need to do for now. + * + * Arguments: + * ivp pointer to VCC's IPVCC control block + * + * Returns: + * none + * + */ +void +spansarp_vcclose(ivp) + struct ipvcc *ivp; +{ + struct spansarp *sap; + int s = splnet(); + + /* + * Get spansarp entry + */ + SPANSARP_LOOKUP(ivp->iv_dst.s_addr, sap); + if (sap == NULL) { + (void) splx(s); + return; + } + + /* + * Remove IP VCC from chain + */ + UNLINK(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); + ivp->iv_arpent = NULL; + + /* + * If entry is currently valid or in use, not much else for us to do + */ + if ((sap->sa_flags & (SAF_VALID | SAF_LOCKED)) || + (sap->sa_origin >= SAO_PERM)) { + (void) splx(s); + return; + } + + /* + * If there are still other VCCs waiting, exit + */ + if (sap->sa_ivp) { + (void) splx(s); + return; + } + + /* + * Noone else waiting, so remove entry from the retry chain + */ + UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); + + /* + * Free entry + */ + SPANSARP_DELETE(sap); + atm_free((caddr_t)sap); + (void) splx(s); +} + + +/* + * Process module unloading notification + * + * Called whenever the spans module is about to be unloaded. All signalling + * instances will have been previously detached. All spansarp resources + * must be freed now. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +spansarp_stop() +{ + int i; + + /* + * Make sure the arp table is empty + */ + for (i = 0; i < SPANSARP_HASHSIZ; i++) { + if (spansarp_arptab[i] != NULL) + panic("spansarp_stop: arp table not empty"); + } + + /* + * Cancel timers + */ + (void) atm_untimeout(&spansarp_timer); + (void) atm_untimeout(&spansarp_rtimer); + + /* + * Free our storage pools + */ + atm_release_pool(&spansarp_pool); +} + + +/* + * Process IP Network Interface Activation + * + * Called whenever an IP network interface becomes active. + * + * Called at splnet. + * + * Arguments: + * clp pointer to CLS interface + * + * Returns: + * none + * + */ +void +spansarp_ipact(clp) + struct spanscls *clp; +{ + /* + * Make sure aging timer is running + */ + if ((spansarp_timer.ti_flag & TIF_QUEUED) == 0) + atm_timeout(&spansarp_timer, SPANSARP_AGING, spansarp_aging); +} + + +/* + * Process IP Network Interface Deactivation + * + * Called whenever an IP network interface becomes inactive. + * + * Called at splnet. + * + * Arguments: + * clp pointer to CLS interface + * + * Returns: + * none + * + */ +void +spansarp_ipdact(clp) + struct spanscls *clp; +{ + struct spanscls *clp2; + struct spansarp *sap, *snext; + int i; + + /* + * Delete all interface entries + */ + for (i = 0; i < SPANSARP_HASHSIZ; i++) { + for (sap = spansarp_arptab[i]; sap; sap = snext) { + snext = sap->sa_next; + + /* + * Clean up entries for this interface + */ + if (sap->sa_cls != clp) + continue; + + /* + * All VCCs better be gone by now + */ + if (sap->sa_ivp) + panic("spansarp_ipdact: entry not empty"); + + /* + * Remove entry from the retry chain + */ + UNLINK(sap, struct spansarp, + spansarp_retry_head, sa_rnext); + + /* + * Delete entry from arp table + */ + SPANSARP_DELETE(sap); + atm_free((caddr_t)sap); + } + } + + /* + * Stop aging timer if this is the last active interface + */ + for (clp2 = spanscls_head; clp2; clp2 = clp2->cls_next) { + if ((clp != clp2) && (clp2->cls_ipnif)) + break; + } + if (clp2 == NULL) + (void) atm_untimeout(&spansarp_timer); +} + + +/* + * Issue a SPANS ARP request packet + * + * Arguments: + * sap pointer to arp table entry + * + * Returns: + * 0 packet was successfully sent + * else unable to send packet + * + */ +static int +spansarp_request(sap) + struct spansarp *sap; +{ + struct spanscls *clp; + struct spans *spp; + struct spanscls_hdr *chp; + struct spansarp_hdr *ahp; + KBuffer *m; + struct ip_nif *inp; + int err; + + clp = sap->sa_cls; + spp = clp->cls_spans; + inp = clp->cls_ipnif; + + /* + * Make sure CLS VCC is open and that we know our addresses + */ + if (clp->cls_state != CLS_OPEN) + return (1); + if (spp->sp_addr.address_format != T_ATM_SPANS_ADDR) + return (1); + if (inp == NULL) + return (1); + + /* + * Get a buffer for pdu + */ + KB_ALLOCPKT(m, ARP_PACKET_LEN, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) + return (1); + + /* + * Place pdu at end of buffer + */ + KB_PLENSET(m, ARP_PACKET_LEN); + KB_TAILALIGN(m, ARP_PACKET_LEN); + KB_DATASTART(m, chp, struct spanscls_hdr *); + ahp = (struct spansarp_hdr *)(chp + 1); + + /* + * Build headers + */ + spans_addr_copy(&spans_bcastaddr, &chp->ch_dst); + spans_addr_copy(spp->sp_addr.address, &chp->ch_src); + *(u_int *)&chp->ch_proto = *(u_int *)&spanscls_hdr.ch_proto; + *(u_int *)&chp->ch_dsap = *(u_int *)&spanscls_hdr.ch_dsap; + *(u_short *)&chp->ch_oui[1] = *(u_short *)&spanscls_hdr.ch_oui[1]; + chp->ch_pid = htons(ETHERTYPE_ARP); + + + /* + * Build ARP packet + */ + ahp->ah_hrd = htons(ARP_SPANS); + ahp->ah_pro = htons(ETHERTYPE_IP); + ahp->ah_hln = sizeof(spans_addr); + ahp->ah_pln = sizeof(struct in_addr); + ahp->ah_op = htons(ARP_REQUEST); + spans_addr_copy(spp->sp_addr.address, &ahp->ah_sha); + KM_COPY(&(IA_SIN(inp->inf_addr)->sin_addr), ahp->ah_spa, + sizeof(struct in_addr)); + KM_COPY(&sap->sa_dstip, ahp->ah_tpa, sizeof(struct in_addr)); + + /* + * Now, send the pdu via the CLS service + */ + err = atm_cm_cpcs_data(clp->cls_conn, m); + if (err) { + KB_FREEALL(m); + return (1); + } + + return (0); +} + + +/* + * Process a SPANS ARP input packet + * + * Arguments: + * clp pointer to interface CLS control block + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +void +spansarp_input(clp, m) + struct spanscls *clp; + KBuffer *m; +{ + struct spans *spp = clp->cls_spans; + struct spanscls_hdr *chp; + struct spansarp_hdr *ahp; + struct spansarp *sap; + struct ip_nif *inp = clp->cls_ipnif; + struct in_addr in_me, in_src, in_targ; + int s, err; + + /* + * Make sure IP interface has been activated + */ + if (inp == NULL) + goto free; + + /* + * Get the packet together + */ + if (KB_LEN(m) < ARP_PACKET_LEN) { + KB_PULLUP(m, ARP_PACKET_LEN, m); + if (m == 0) + return; + } + KB_DATASTART(m, chp, struct spanscls_hdr *); + ahp = (struct spansarp_hdr *)(chp + 1); + + KM_COPY(ahp->ah_spa, &in_src, sizeof(struct in_addr)); + KM_COPY(ahp->ah_tpa, &in_targ, sizeof(struct in_addr)); + KM_COPY(&(IA_SIN(inp->inf_addr)->sin_addr), &in_me, + sizeof(struct in_addr)); + + /* + * Initial packet verification + */ + if ((ahp->ah_hrd != htons(ARP_SPANS)) || + (ahp->ah_pro != htons(ETHERTYPE_IP))) + goto free; + + /* + * Validate source addresses + * can't be from broadcast + * can't be from me + */ + if (!spans_addr_cmp(&ahp->ah_sha, &spans_bcastaddr)) + goto free; +#if (defined(BSD) && (BSD >= 199306)) + if (in_broadcast(in_src, &inp->inf_nif->nif_if)) +#else + if (in_broadcast(in_src)) +#endif + goto free; + if (!spans_addr_cmp(&ahp->ah_sha, spp->sp_addr.address)) + goto free; + if (in_src.s_addr == in_me.s_addr) { + log(LOG_ERR, + "duplicate IP address sent from spans address %s\n", + spans_addr_print(&ahp->ah_sha)); + in_targ = in_me; + goto chkop; + } + + /* + * Update arp table with source address info + */ + s = splnet(); + SPANSARP_LOOKUP(in_src.s_addr, sap); + if (sap) { + /* + * Found an entry for the source, but don't + * update permanent entries + */ + if (sap->sa_origin != SAO_PERM) { + + /* + * Update the entry + */ + sap->sa_dstatm.address_format = T_ATM_SPANS_ADDR; + sap->sa_dstatm.address_length = sizeof(spans_addr); + spans_addr_copy(&ahp->ah_sha, sap->sa_dstatm.address); + sap->sa_cls = clp; + sap->sa_reftime = 0; + if ((sap->sa_flags & SAF_VALID) == 0) { + /* + * Newly valid entry, notify waiting users + */ + struct ipvcc *ivp, *inext; + + sap->sa_flags |= SAF_VALID; + for (ivp = sap->sa_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + + ivp->iv_arpent = (struct arpmap *)sap; + (*inp->inf_arpnotify)(ivp, MAP_VALID); + } + + /* + * Remove ourselves from the retry chain + */ + UNLINK(sap, struct spansarp, + spansarp_retry_head, sa_rnext); + } + } + + } else if (in_targ.s_addr == in_me.s_addr) { + /* + * Source unknown and we're the target - add new entry + */ + sap = (struct spansarp *)atm_allocate(&spansarp_pool); + if (sap) { + sap->sa_dstip.s_addr = in_src.s_addr; + sap->sa_dstatm.address_format = T_ATM_SPANS_ADDR; + sap->sa_dstatm.address_length = sizeof(spans_addr); + spans_addr_copy(&ahp->ah_sha, sap->sa_dstatm.address); + sap->sa_dstatmsub.address_format = T_ATM_ABSENT; + sap->sa_dstatmsub.address_length = 0; + sap->sa_cls = clp; + sap->sa_flags = SAF_VALID; + sap->sa_origin = SAO_LOOKUP; + SPANSARP_ADD(sap); + } + } + (void) splx(s); + +chkop: + /* + * If this is a request for our address, send a reply + */ + if (ntohs(ahp->ah_op) != ARP_REQUEST) + goto free; + if (in_targ.s_addr != in_me.s_addr) + goto free; + + spans_addr_copy(&chp->ch_src, &chp->ch_dst); + spans_addr_copy(spp->sp_addr.address, &chp->ch_src); + ahp->ah_op = htons(ARP_REPLY); + spans_addr_copy(&ahp->ah_sha, &ahp->ah_tha); + spans_addr_copy(spp->sp_addr.address, &ahp->ah_sha); + KM_COPY(ahp->ah_spa, ahp->ah_tpa, sizeof(struct in_addr)); + KM_COPY(&in_me, ahp->ah_spa, sizeof(struct in_addr)); + + err = atm_cm_cpcs_data(clp->cls_conn, m); + if (err) + goto free; + return; + +free: + KB_FREEALL(m); +} + + +/* + * Process a SPANS ARP aging timer tick + * + * This function is called every SPANSARP_AGING seconds, in order to age + * all the arp table entries. + * + * Called at splnet. + * + * Arguments: + * tip pointer to spansarp aging timer control block + * + * Returns: + * none + * + */ +static void +spansarp_aging(tip) + struct atm_time *tip; +{ + struct spansarp *sap, *snext; + struct ipvcc *ivp, *inext; + int i; + + + /* + * Schedule next timeout + */ + atm_timeout(&spansarp_timer, SPANSARP_AGING, spansarp_aging); + + /* + * Run through arp table bumping each entry's aging timer. + */ + for (i = 0; i < SPANSARP_HASHSIZ; i++) { + for (sap = spansarp_arptab[i]; sap; sap = snext) { + snext = sap->sa_next; + + /* + * Permanent (manually installed) entries aren't aged + */ + if (sap->sa_origin == SAO_PERM) + continue; + + /* + * See if entry is valid and over-aged + */ + if ((sap->sa_flags & SAF_VALID) == 0) + continue; + if (++sap->sa_reftime < SPANSARP_MAXAGE) + continue; + + /* + * Entry is now invalid, tell IP/ATM about it + */ + sap->sa_flags |= SAF_LOCKED; + for (ivp = sap->sa_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*ivp->iv_ipnif->inf_arpnotify) + (ivp, MAP_INVALID); + } + sap->sa_flags &= ~(SAF_LOCKED | SAF_VALID); + + if (sap->sa_ivp != NULL) { + /* + * Somebody still cares, so add the arp + * entry to the retry list. + */ + LINK2TAIL(sap, struct spansarp, + spansarp_retry_head, sa_rnext); + if ((spansarp_rtimer.ti_flag & TIF_QUEUED) == 0) + atm_timeout(&spansarp_rtimer, + SPANSARP_RETRY, spansarp_retry); + + /* + * Issue arp request for this address + */ + (void) spansarp_request(sap); + + } else { + /* + * Delete unused entry + */ + SPANSARP_DELETE(sap); + atm_free((caddr_t)sap); + } + } + } +} + + +/* + * Process a SPANS ARP retry timer tick + * + * This function is called every SPANSARP_RETRY seconds, in order to retry + * awaiting arp resolution requests. We will retry requests indefinitely, + * assuming that IP will set a timeout to close the VCC(s) requesting the + * failing address resolution. + * + * Called at splnet. + * + * Arguments: + * tip pointer to spansarp retry timer control block + * + * Returns: + * none + * + */ +static void +spansarp_retry(tip) + struct atm_time *tip; +{ + struct spansarp *sap; + + + /* + * See if there's work to do + */ + if (spansarp_retry_head == NULL) { + return; + } + + /* + * Schedule next timeout + */ + atm_timeout(&spansarp_rtimer, SPANSARP_RETRY, spansarp_retry); + + /* + * Run through retry chain, (re)issuing arp requests. + */ + for (sap = spansarp_retry_head; sap; sap = sap->sa_next) { + + /* + * Send another arp request + */ + (void) spansarp_request(sap); + } +} + + +/* + * SPANS ARP IOCTL support + * + * Function will be called at splnet. + * + * Arguments: + * code PF_ATM sub-operation code + * data pointer to code specific parameter data area + * arg1 pointer to code specific argument + * + * Returns: + * 0 request procesed + * errno error processing request - reason indicated + * + */ +int +spansarp_ioctl(code, data, arg1) + int code; + caddr_t data; + caddr_t arg1; +{ + struct atmaddreq *aap; + struct atmdelreq *adp; + struct atminfreq *aip; + struct spans *spp; + struct spanscls *clp; + struct spansarp *sap; + struct air_arp_rsp aar; + struct ip_nif *inp; + struct ipvcc *ivp, *inext; + struct in_addr ip; + u_long dst; + int err = 0, i, buf_len; + caddr_t buf_addr; + + + switch (code) { + + case AIOCS_ADD_ARP: + /* + * Add a permanent ARP mapping + */ + aap = (struct atmaddreq *)data; + clp = (struct spanscls *)arg1; + inp = clp->cls_ipnif; + if ((aap->aar_arp_addr.address_format != T_ATM_SPANS_ADDR) || + (aap->aar_arp_origin != ARP_ORIG_PERM)) { + err = EINVAL; + break; + } + ip = SATOSIN(&aap->aar_arp_dst)->sin_addr; + + /* + * See if we already have an entry for this IP address + */ + SPANSARP_LOOKUP(ip.s_addr, sap); + if (sap == NULL) { + /* + * No, get a new arp entry + */ + sap = (struct spansarp *)atm_allocate(&spansarp_pool); + if (sap == NULL) { + err = ENOMEM; + break; + } + + /* + * Get entry set up + */ + sap->sa_dstip = ip; + ATM_ADDR_COPY(&aap->aar_arp_addr, &sap->sa_dstatm); + sap->sa_dstatmsub.address_format = T_ATM_ABSENT; + sap->sa_dstatmsub.address_length = 0; + sap->sa_cls = clp; + sap->sa_flags |= SAF_VALID; + sap->sa_origin = SAO_PERM; + + /* + * Add entry to table + */ + SPANSARP_ADD(sap); + break; + + } + + /* + * See if we're attempting to change the ATM address for + * this cached entry + */ + if ((sap->sa_dstatm.address_format != T_ATM_ABSENT) && + (!ATM_ADDR_EQUAL(&aap->aar_arp_addr, &sap->sa_dstatm) || + (clp != sap->sa_cls))) { + + /* + * Yes, notify IP/ATM that a mapping change has + * occurred. IP/ATM will close any VCC's which + * aren't waiting for this map. + */ + sap->sa_flags |= SAF_LOCKED; + for (ivp = sap->sa_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_CHANGED); + } + sap->sa_flags &= ~SAF_LOCKED; + } + + /* + * Update the cached entry with the new data + */ + ATM_ADDR_COPY(&aap->aar_arp_addr, &sap->sa_dstatm); + sap->sa_cls = clp; + + /* + * If this entry isn't valid, notify anyone who might + * be interested + */ + if ((sap->sa_flags & SAF_VALID) == 0) { + + sap->sa_flags |= SAF_LOCKED; + for (ivp = sap->sa_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_VALID); + } + sap->sa_flags &= ~SAF_LOCKED; + } + + /* + * Remove this entry from the retry chain + */ + UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); + + /* + * Mark the entry as permanent + */ + sap->sa_flags |= SAF_VALID; + sap->sa_origin = SAO_PERM; + break; + + case AIOCS_DEL_ARP: + /* + * Delete an ARP mapping + */ + adp = (struct atmdelreq *)data; + clp = (struct spanscls *)arg1; + ip = SATOSIN(&adp->adr_arp_dst)->sin_addr; + + /* + * Now find the entry to be deleted + */ + SPANSARP_LOOKUP(ip.s_addr, sap); + if (sap == NULL) { + err = ENOENT; + break; + } + + /* + * Notify all VCCs using this entry that they must finish + * up now. + */ + sap->sa_flags |= SAF_LOCKED; + for (ivp = sap->sa_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_FAILED); + } + + /* + * Now free up the entry + */ + UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); + SPANSARP_DELETE(sap); + atm_free((caddr_t)sap); + break; + + case AIOCS_INF_ARP: + /* + * Get ARP table information + */ + aip = (struct atminfreq *)data; + spp = (struct spans *)arg1; + + if (aip->air_arp_addr.sa_family != AF_INET) + break; + dst = SATOSIN(&aip->air_arp_addr)->sin_addr.s_addr; + + buf_addr = aip->air_buf_addr; + buf_len = aip->air_buf_len; + + if ((clp = spp->sp_cls) == NULL) + break; + + /* + * Run through entire arp table + */ + for (i = 0; i < SPANSARP_HASHSIZ; i++) { + for (sap = spansarp_arptab[i]; sap; + sap = sap->sa_next) { + /* + * We only want entries learned + * from the supplied interface. + */ + if (sap->sa_cls != clp) + continue; + if ((dst != INADDR_ANY) && + (dst != sap->sa_dstip.s_addr)) + continue; + + /* + * Make sure there's room in the user's buffer + */ + if (buf_len < sizeof(aar)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + SATOSIN(&aar.aap_arp_addr)->sin_family = + AF_INET; + SATOSIN(&aar.aap_arp_addr)->sin_addr.s_addr = + sap->sa_dstip.s_addr; + (void) sprintf(aar.aap_intf, "%s%d", + clp->cls_ipnif->inf_nif->nif_if.if_name, + clp->cls_ipnif->inf_nif->nif_if.if_unit + ); + aar.aap_flags = sap->sa_flags; + aar.aap_origin = sap->sa_origin; + if (sap->sa_flags & SAF_VALID) + aar.aap_age = SPANSARP_MAXAGE - + sap->sa_reftime; + else + aar.aap_age = 0; + ATM_ADDR_COPY(&sap->sa_dstatm, &aar.aap_addr); + ATM_ADDR_COPY(&sap->sa_dstatmsub, + &aar.aap_subaddr); + + /* + * Copy the response into the user's buffer + */ + if (err = copyout((caddr_t)&aar, buf_addr, + sizeof(aar))) + break; + buf_addr += sizeof(aar); + buf_len -= sizeof(aar); + } + if (err) + break; + } + + /* + * Update the buffer pointer and length + */ + aip->air_buf_addr = buf_addr; + aip->air_buf_len = buf_len; + break; + + default: + err = EOPNOTSUPP; + } + + return (err); +} + diff --git a/sys/netatm/spans/spans_cls.c b/sys/netatm/spans/spans_cls.c new file mode 100644 index 0000000..f496311 --- /dev/null +++ b/sys/netatm/spans/spans_cls.c @@ -0,0 +1,848 @@ +/* + * + * =================================== + * 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: spans_cls.c,v 1.11 1998/06/29 22:04:29 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS Connectionless Datagram Service (CLS) module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_cls.c,v 1.11 1998/06/29 22:04:29 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> +#include <netatm/spans/spans_cls.h> + + +/* + * Global variables + */ +int spanscls_print = 0; + +struct spanscls *spanscls_head = NULL; + +struct spans_addr spans_bcastaddr = { + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } +}; + +struct spanscls_hdr spanscls_hdr = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* dst */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* src */ + 0x00, 0x00, 0, + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0 /* LLC SNAP */ +}; + + +/* + * Local functions + */ +static int spanscls_ipact __P((struct ip_nif *)); +static int spanscls_ipdact __P((struct ip_nif *)); +static int spanscls_bcast_output __P((struct ip_nif *, KBuffer *)); +static void spanscls_cpcs_data __P((void *, KBuffer *)); +static void spanscls_connected __P((void *)); +static void spanscls_cleared __P((void *, struct t_atm_cause *)); +static caddr_t spanscls_getname __P((void *)); +static void spanscls_pdu_print __P((struct spanscls *, KBuffer *, + char *)); + +/* + * Local variables + */ +static struct sp_info spanscls_pool = { + "spans cls pool", /* si_name */ + sizeof(struct spanscls), /* si_blksiz */ + 2, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + +static struct ip_serv spanscls_ipserv = { + spanscls_ipact, + spanscls_ipdact, + spansarp_ioctl, + NULL, + spansarp_svcout, + spansarp_svcin, + spansarp_svcactive, + spansarp_vcclose, + spanscls_bcast_output, + { + {ATM_AAL5, ATM_ENC_NULL}, + {ATM_AAL3_4, ATM_ENC_NULL} + } +}; + +static u_char spanscls_bridged[] = { + 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0x03, 0x00, 0x80, 0xc2 /* LLC SNAP */ +}; + +static Atm_endpoint spanscls_endpt = { + NULL, + ENDPT_SPANS_CLS, + NULL, + spanscls_getname, + spanscls_connected, + spanscls_cleared, + NULL, + NULL, + NULL, + NULL, + spanscls_cpcs_data, + NULL, + NULL, + NULL, + NULL +}; + +static Atm_attributes spanscls_attr = { + NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, + ATM_AAL3_4 + }, + { /* traffic */ + T_ATM_PRESENT, + { + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + { + T_ATM_ABSENT, + 0, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_NO + }, + T_YES + }, + }, + { /* bearer */ + T_ATM_PRESENT, + { + T_ATM_CLASS_X, + T_ATM_NULL, + T_ATM_NULL, + T_NO, + T_ATM_1_TO_1 + } + }, + { /* bhli */ + T_ATM_ABSENT + }, + { /* blli */ + T_ATM_ABSENT, + T_ATM_ABSENT + }, + { /* llc */ + T_ATM_ABSENT + }, + { /* called */ + T_ATM_PRESENT, + }, + { /* calling */ + T_ATM_ABSENT + }, + { /* qos */ + T_ATM_PRESENT, + { + T_ATM_NETWORK_CODING, + { + T_ATM_QOS_CLASS_0, + }, + { + T_ATM_QOS_CLASS_0 + } + } + }, + { /* transit */ + T_ATM_ABSENT + }, + { /* cause */ + T_ATM_ABSENT + } +}; + +static struct t_atm_cause spanscls_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_UNSPECIFIED_NORMAL, + {0, 0, 0, 0} +}; + + +/* + * Process module loading + * + * Called whenever the spans module is initializing. + * + * Arguments: + * none + * + * Returns: + * 0 initialization successful + * errno initialization failed - reason indicated + * + */ +int +spanscls_start() +{ + int err; + + /* + * Fill in union fields + */ + spanscls_attr.aal.v.aal4.forward_max_SDU_size = ATM_NIF_MTU; + spanscls_attr.aal.v.aal4.backward_max_SDU_size = ATM_NIF_MTU; + spanscls_attr.aal.v.aal4.SSCS_type = T_ATM_NULL; + spanscls_attr.aal.v.aal4.mid_low = 0; + spanscls_attr.aal.v.aal4.mid_high = 1023; + + /* + * Register our endpoint + */ + err = atm_endpoint_register(&spanscls_endpt); + + return (err); +} + + +/* + * Process module unloading notification + * + * Called whenever the spans module is about to be unloaded. All signalling + * instances will have been previously detached. All spanscls resources + * must be freed now. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +spanscls_stop() +{ + int s = splnet(); + + /* + * Tell ARP to stop + */ + spansarp_stop(); + + /* + * Nothing should be left here... + */ + if (spanscls_head) { + panic("spanscls_stop: bad state"); + } + (void) splx(s); + + /* + * De-register ourselves + */ + (void) atm_endpoint_deregister(&spanscls_endpt); + + /* + * Free our storage pools + */ + atm_release_pool(&spanscls_pool); +} + + +/* + * Process signalling interface attach + * + * This function is called whenever a physical interface has been attached + * to spans. We will open the CLS PVC and await further events. + * + * Called at splnet. + * + * Arguments: + * spp pointer to spans signalling protocol instance + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +int +spanscls_attach(spp) + struct spans *spp; +{ + struct spanscls *clp; + Atm_addr_pvc *pvcp; + int err; + + /* + * Get a new cls control block + */ + clp = (struct spanscls *)atm_allocate(&spanscls_pool); + if (clp == NULL) + return (ENOMEM); + + /* + * Initialize some stuff + */ + clp->cls_state = CLS_CLOSED; + clp->cls_spans = spp; + spp->sp_ipserv = &spanscls_ipserv; + + /* + * Fill out connection attributes + */ + spanscls_attr.nif = spp->sp_pif->pif_nif; + spanscls_attr.traffic.v.forward.PCR_all_traffic = spp->sp_pif->pif_pcr; + spanscls_attr.traffic.v.backward.PCR_all_traffic = spp->sp_pif->pif_pcr; + spanscls_attr.called.addr.address_format = T_ATM_PVC_ADDR; + spanscls_attr.called.addr.address_length = sizeof(Atm_addr_pvc); + pvcp = (Atm_addr_pvc *)spanscls_attr.called.addr.address; + ATM_PVC_SET_VPI(pvcp, SPANS_CLS_VPI); + ATM_PVC_SET_VCI(pvcp, SPANS_CLS_VCI); + spanscls_attr.called.subaddr.address_format = T_ATM_ABSENT; + spanscls_attr.called.subaddr.address_length = 0; + + /* + * Create SPANS Connectionless Service (CLS) PVC + */ + err = atm_cm_connect(&spanscls_endpt, clp, &spanscls_attr, + &clp->cls_conn); + if (err) { + atm_free((caddr_t)clp); + return (err); + } + + /* + * Set new state and link instance + */ + clp->cls_state = CLS_OPEN; + LINK2TAIL(clp, struct spanscls, spanscls_head, cls_next); + spp->sp_cls = clp; + + return (0); +} + + +/* + * Process signalling interface detach + * + * This function is called whenever a physical interface has been detached + * from spans. We will close the CLS PVC and clean up everything. + * + * Called at splnet. + * + * Arguments: + * spp pointer to spans signalling protocol instance + * + * Returns: + * none + * + */ +void +spanscls_detach(spp) + struct spans *spp; +{ + struct spanscls *clp; + + /* + * Get our control block + */ + clp = spp->sp_cls; + if (clp == NULL) + return; + + /* + * Just checking up on things... + */ + if (clp->cls_ipnif) + panic("spanscls_detach: IP interface still active"); + + /* + * Close CLS PVC + */ + spanscls_closevc(clp, &spanscls_cause); + + /* + * Sever links and free server block, if possible + */ + clp->cls_spans = NULL; + spp->sp_cls = NULL; + if (clp->cls_state == CLS_CLOSED) { + UNLINK(clp, struct spanscls, spanscls_head, cls_next); + atm_free((caddr_t)clp); + } +} + + +/* + * Process IP Network Interface Activation + * + * Called whenever an IP network interface becomes active. + * + * Called at splnet. + * + * Arguments: + * inp pointer to IP network interface + * + * Returns: + * 0 command successful + * errno command failed - reason indicated + * + */ +static int +spanscls_ipact(inp) + struct ip_nif *inp; +{ + struct spans *spp; + struct spanscls *clp; + + /* + * Get corresponding cls instance + */ + spp = (struct spans *)inp->inf_nif->nif_pif->pif_siginst; + if ((spp == NULL) || ((clp = spp->sp_cls) == NULL)) + return (ENXIO); + + /* + * Make sure it's not already activated + */ + if (clp->cls_ipnif) + return (EEXIST); + + /* + * Set two-way links with IP world + */ + clp->cls_ipnif = inp; + inp->inf_isintf = (caddr_t)clp; + + /* + * Tell arp about new interface + */ + spansarp_ipact(clp); + + return (0); +} + + +/* + * Process IP Network Interface Deactivation + * + * Called whenever an IP network interface becomes inactive. + * + * Called at splnet. + * + * Arguments: + * inp pointer to IP network interface + * + * Returns: + * 0 command successful + * errno command failed - reason indicated + * + */ +static int +spanscls_ipdact(inp) + struct ip_nif *inp; +{ + struct spanscls *clp; + + /* + * Get cls instance and make sure it's been activated + */ + clp = (struct spanscls *)inp->inf_isintf; + if ((clp == NULL) || (clp->cls_ipnif == NULL)) + return (ENXIO); + + /* + * Let arp know about this + */ + spansarp_ipdact(clp); + + /* + * Clear IP interface pointer + */ + clp->cls_ipnif = NULL; + return (0); +} + + +/* + * Output IP Broadcast Packet + * + * Called whenever an IP broadcast packet is sent to this interface. + * + * Arguments: + * inp pointer to IP network interface + * m pointer to packet buffer chain + * + * Returns: + * 0 packet sent successfully + * errno send failed - reason indicated + * + */ +static int +spanscls_bcast_output(inp, m) + struct ip_nif *inp; + KBuffer *m; +{ + struct spans *spp; + struct spanscls *clp; + struct spanscls_hdr *chp; + int err, space; + + /* + * Get cls instance and make sure it's been activated + */ + clp = (struct spanscls *)inp->inf_isintf; + if ((clp == NULL) || (clp->cls_ipnif == NULL)) { + KB_FREEALL(m); + return (ENETDOWN); + } + + /* + * Make sure that we know our addresses + */ + spp = clp->cls_spans; + if (spp->sp_addr.address_format != T_ATM_SPANS_ADDR) { + KB_FREEALL(m); + return (ENETDOWN); + } + + /* + * See if there's room to add CLS header to front of packet. + */ + KB_HEADROOM(m, space); + if (space < sizeof(struct spanscls_hdr)) { + KBuffer *n; + + /* + * We have to allocate another buffer and tack it + * onto the front of the packet + */ + KB_ALLOCPKT(n, sizeof(struct spanscls_hdr), + KB_F_NOWAIT, KB_T_HEADER); + if (n == 0) { + KB_FREEALL(m); + return (ENOBUFS); + } + KB_TAILALIGN(n, sizeof(struct spanscls_hdr)); + KB_LINKHEAD(n, m); + m = n; + } else { + /* + * Header fits, just adjust buffer controls + */ + KB_HEADADJ(m, sizeof(struct spanscls_hdr)); + } + + /* + * Now, build the CLS header + */ + KB_DATASTART(m, chp, struct spanscls_hdr *); + spans_addr_copy(&spans_bcastaddr, &chp->ch_dst); + spans_addr_copy(spp->sp_addr.address, &chp->ch_src); + *(u_int *)&chp->ch_proto = *(u_int *)&spanscls_hdr.ch_proto; + *(u_int *)&chp->ch_dsap = *(u_int *)&spanscls_hdr.ch_dsap; + *(u_short *)&chp->ch_oui[1] = *(u_short *)&spanscls_hdr.ch_oui[1]; + chp->ch_pid = htons(ETHERTYPE_IP); + +#ifdef DIAGNOSTIC + if (spanscls_print) + spanscls_pdu_print(clp, m, "output"); +#endif + + /* + * Finally, send the pdu via the CLS service + */ + err = atm_cm_cpcs_data(clp->cls_conn, m); + if (err) { + KB_FREEALL(m); + return (ENOBUFS); + } + + return (0); +} + + +/* + * Process VCC Input Data + * + * All input packets received from CLS VCC lower layers are processed here. + * + * Arguments: + * tok connection token (pointer to CLS VCC control block) + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +spanscls_cpcs_data(tok, m) + void *tok; + KBuffer *m; +{ + struct spanscls *clp = tok; + struct spans *spp = clp->cls_spans; + struct spanscls_hdr *chp; + struct ip_nif *inp; + + /* + * Make sure we're ready + */ + if ((clp->cls_state != CLS_OPEN) || (spp->sp_state != SPANS_ACTIVE)) { + KB_FREEALL(m); + return; + } + +#ifdef DIAGNOSTIC + if (spanscls_print) + spanscls_pdu_print(clp, m, "input"); +#endif + + /* + * Get CLS header into buffer + */ + if (KB_LEN(m) < sizeof(struct spanscls_hdr)) { + KB_PULLUP(m, sizeof(struct spanscls_hdr), m); + if (m == 0) + return; + } + KB_DATASTART(m, chp, struct spanscls_hdr *); + + /* + * Verify packet information + */ + if ((*(u_int *)&chp->ch_proto != *(u_int *)&spanscls_hdr.ch_proto) || + (*(u_int *)&chp->ch_dsap != *(u_int *)&spanscls_hdr.ch_dsap) || + (*(u_short *)&chp->ch_oui[1] != + *(u_short *)&spanscls_hdr.ch_oui[1])) { + + /* + * Check for bridged PDU + */ + if (bcmp((char *)&chp->ch_proto, (char *)spanscls_bridged, + sizeof(spanscls_bridged))) { + log(LOG_ERR, "spanscls_input: bad format\n"); +#ifdef DIAGNOSTIC + spanscls_pdu_print(clp, m, "input error"); +#endif + } + + KB_FREEALL(m); + return; + } + + /* + * Make sure packet is for us + */ + if (spans_addr_cmp(&chp->ch_dst, spp->sp_addr.address) && + spans_addr_cmp(&chp->ch_dst, &spans_bcastaddr)) { + KB_FREEALL(m); + return; + } + + /* + * Do protocol processing + */ + switch (ntohs(chp->ch_pid)) { + + case ETHERTYPE_IP: + /* + * Drop CLS header + */ + KB_HEADADJ(m, -sizeof(struct spanscls_hdr)); + KB_PLENADJ(m, -sizeof(struct spanscls_hdr)); + + /* + * Packet is ready for input to IP + */ + if (inp = clp->cls_ipnif) + (void) (*inp->inf_ipinput)(inp, m); + else + KB_FREEALL(m); + break; + + case ETHERTYPE_ARP: + spansarp_input(clp, m); + break; + + default: + log(LOG_ERR, "spanscls_input: unknown protocol 0x%x\n", + chp->ch_pid); + KB_FREEALL(m); + return; + } +} + + +/* + * Close a SPANS CLS VCC + * + * This function will close a SPANS CLS VCC. + * + * Arguments: + * clp pointer to CLS instance + * cause pointer to cause code + * + * Returns: + * none + * + */ +void +spanscls_closevc(clp, cause) + struct spanscls *clp; + struct t_atm_cause *cause; +{ + int err; + + /* + * Close VCC + */ + if (clp->cls_conn) { + err = atm_cm_release(clp->cls_conn, cause); + if (err) { + log(LOG_ERR, "spanscls_closevc: release err=%d\n", err); + } + clp->cls_conn = NULL; + } + + clp->cls_state = CLS_CLOSED; +} + + +/* + * Process CLS VCC Connected Notification + * + * Arguments: + * toku user's connection token (spanscls protocol block) + * + * Returns: + * none + * + */ +static void +spanscls_connected(toku) + void *toku; +{ + /* + * We should never get one of these + */ + log(LOG_ERR, "spanscls: unexpected connected event\n"); +} + + +/* + * Process CLS VCC Cleared Notification + * + * Arguments: + * toku user's connection token (spanscls protocol block) + * cause pointer to cause code + * + * Returns: + * none + * + */ +static void +spanscls_cleared(toku, cause) + void *toku; + struct t_atm_cause *cause; +{ + struct spanscls *clp = (struct spanscls *)toku; + + /* + * CLS VCC has been closed, so clean up our side + */ + clp->cls_conn = NULL; + spanscls_closevc(clp, cause); +} + + +/* + * Get Connection's Application/Owner Name + * + * Arguments: + * tok spanscls connection token + * + * Returns: + * addr pointer to string containing our name + * + */ +static caddr_t +spanscls_getname(tok) + void *tok; +{ + return ("SPANSCLS"); +} + + +/* + * Print a SPANS CLS PDU + * + * Arguments: + * clp pointer to cls instance + * m pointer to pdu buffer chain + * msg pointer to message string + * + * Returns: + * none + * + */ +static void +spanscls_pdu_print(clp, m, msg) + struct spanscls *clp; + KBuffer *m; + char *msg; +{ + char buf[128]; + + sprintf(buf, "spanscls %s:\n", msg); + atm_pdu_print(m, buf); +} + diff --git a/sys/netatm/spans/spans_cls.h b/sys/netatm/spans/spans_cls.h new file mode 100644 index 0000000..d2cacac --- /dev/null +++ b/sys/netatm/spans/spans_cls.h @@ -0,0 +1,188 @@ +/* + * + * =================================== + * 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: spans_cls.h,v 1.6 1998/05/18 19:11:17 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS Connectionless Datagram Service (CLS) control blocks + * + */ + +#ifndef _SPANS_SPANSCLS_H +#define _SPANS_SPANSCLS_H + +/* + * Protocol constants + */ +#define SPANSARP_AGING (60 * ATM_HZ) /* ARP aging timer */ +#define SPANSARP_RETRY (3 * ATM_HZ) /* ARP retry timer */ +#define SPANSARP_MAXAGE 20 /* Max ARP entry age (minutes)*/ +#define SPANSARP_HASHSIZ 19 /* Hash table size */ + + +/* + * SPANS CLS protocol structure. There will be one such structure for + * each SPANS signalling instance. + */ +struct spanscls { + struct spanscls *cls_next; /* Next attached cls instance */ + u_char cls_state; /* Protocol state (see below) */ + struct spans *cls_spans; /* Spans signalling instance */ + Atm_connection *cls_conn; /* Connection manager token */ + struct ip_nif *cls_ipnif; /* IP network interface */ +}; + +/* + * SPANS CLS Protocol States + */ +#define CLS_CLOSED 1 /* CLS PVC is closed */ +#define CLS_OPEN 2 /* CLS PVC is open */ + + +/* + * Structure for SPANS ARP mappings. Each of these structures will contain + * IP address to SPANS hardware address mappings. There will be one such + * structure for each IP address currently in use. + */ +struct spansarp { + struct arpmap sa_arpmap; /* Common entry header */ + struct spanscls *sa_cls; /* Interface where we learned answer */ + struct spansarp *sa_next; /* Hash chain */ + struct spansarp *sa_rnext; /* Retry chain */ + u_char sa_flags; /* Flags (see below) */ + u_char sa_origin; /* Origin (see below) */ + u_short sa_reftime; /* Entry reference time (minutes) */ + struct ipvcc *sa_ivp; /* IP VCCs waiting for answer */ +}; +#define sa_dstip sa_arpmap.am_dstip +#define sa_dstatm sa_arpmap.am_dstatm +#define sa_dstatmsub sa_arpmap.am_dstatmsub + +/* + * Entry Flags + */ +#define SAF_VALID ARPF_VALID /* Entry is valid */ +#define SAF_REFRESH ARPF_REFRESH /* Entry has been refreshed */ +#define SAF_LOCKED 0x04 /* Entry is locked */ + +/* + * Entry Origin + */ +#define SAO_PERM ARP_ORIG_PERM /* Entry is permanently installed */ +#define SAO_LOOKUP 20 /* Learned via lookup */ + + +/* + * SPANS CLS Packet Header + */ +struct spanscls_hdr { + /* IEEE 802.6 MAC header */ + spans_addr ch_dst; /* Destination SPANS address */ + spans_addr ch_src; /* Source SPANS address */ + u_char ch_proto; /* */ + u_char ch_extlen; /* */ + u_short ch_bridging; /* */ + + /* LLC SNAP header */ + u_char ch_dsap; /* Destination SAP */ + u_char ch_ssap; /* Source SAP */ + u_char ch_ctl; /* Control field */ + u_char ch_oui[3]; /* Organizationally Unique Identifier */ + u_short ch_pid; /* Protocol Identifier */ +}; + +/* + * SPANS ARP Packet Format + */ +struct spansarp_hdr { + u_short ah_hrd; /* Hardware type (see below) */ + u_short ah_pro; /* Protocol type */ + u_char ah_hln; /* Length of hardware address */ + u_char ah_pln; /* Length of protocol address */ + u_short ah_op; /* Operation code (see below) */ + spans_addr ah_sha; /* Source hardware address */ + u_char ah_spa[4]; /* Source protocol address */ + spans_addr ah_tha; /* Target hardware address */ + u_char ah_tpa[4]; /* Target protocol address */ +}; + +/* + * Hardware types + */ +#define ARP_SPANS 0x4040 + +/* + * Operation types + */ +#define ARP_REQUEST 1 /* SPANSARP request */ +#define ARP_REPLY 2 /* SPANSARP response */ + +#define ARP_PACKET_LEN \ + (sizeof(struct spanscls_hdr) + sizeof(struct spansarp_hdr)) + +#ifdef ATM_KERNEL +/* + * Macros for manipulating SPANS ARP tables and entries + */ +#define SPANSARP_HASH(ip) ((u_long)(ip) % SPANSARP_HASHSIZ) + +#define SPANSARP_ADD(sa) \ +{ \ + struct spansarp **h; \ + h = &spansarp_arptab[SPANSARP_HASH((sa)->sa_dstip.s_addr)]; \ + LINK2TAIL((sa), struct spansarp, *h, sa_next); \ +} + +#define SPANSARP_DELETE(sa) \ +{ \ + struct spansarp **h; \ + h = &spansarp_arptab[SPANSARP_HASH((sa)->sa_dstip.s_addr)]; \ + UNLINK((sa), struct spansarp, *h, sa_next); \ +} + +#define SPANSARP_LOOKUP(ip, sa) \ +{ \ + for ((sa) = spansarp_arptab[SPANSARP_HASH(ip)]; \ + (sa); (sa) = (sa)->sa_next) { \ + if ((sa)->sa_dstip.s_addr == (ip)) \ + break; \ + } \ +} + + +/* + * External variables + */ +extern struct spanscls *spanscls_head; +extern struct spanscls_hdr spanscls_hdr; + +#endif /* ATM_KERNEL */ + +#endif /* _SPANS_SPANSCLS_H */ diff --git a/sys/netatm/spans/spans_if.c b/sys/netatm/spans/spans_if.c new file mode 100644 index 0000000..4facf73 --- /dev/null +++ b/sys/netatm/spans/spans_if.c @@ -0,0 +1,1336 @@ +/* + * + * =================================== + * 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: spans_if.c,v 1.12 1998/08/26 23:29:09 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * External interfaces to SPANS manager. Includes support for + * running as a loadable kernel module. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_if.c,v 1.12 1998/08/26 23:29:09 mks Exp $"; +#endif + +#ifndef ATM_SPANS_MODULE +#include "opt_atm.h" +#endif + +#include <netatm/kern_include.h> + +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> + +/* + * Global variables + */ +struct sp_info spans_vcpool = { + "spans vcc pool", /* si_name */ + sizeof(struct spans_vccb), /* si_blksiz */ + 10, /* si_blkcnt */ + 50 /* si_maxallow */ +}; + +struct sp_info spans_msgpool = { + "spans message pool", /* si_name */ + sizeof(spans_msg), /* si_blksiz */ + 10, /* si_blkcnt */ + 50 /* si_maxallow */ +}; + +/* + * Local functions + */ +static int spans_start __P((void)); +static int spans_stop __P((void)); +static int spans_attach __P((struct sigmgr *, struct atm_pif *)); +static int spans_detach __P((struct atm_pif *)); +static int spans_setup __P((Atm_connvc *, int *)); +static int spans_release __P((struct vccb *, int *)); +static int spans_accept __P((struct vccb *, int *)); +static int spans_reject __P((struct vccb *, int *)); +static int spans_ioctl __P((int, caddr_t, caddr_t)); + +/* + * Local variables + */ +static struct sigmgr *spans_mgr = NULL; + + +/* + * Initialize SPANS processing + * + * This will be called during module loading. We'll just register + * the SPANS protocol descriptor and wait for a SPANS ATM interface + * to come online. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +spans_start() +{ + int err = 0; + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: spans=%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); + } + + /* + * Allocate protocol definition structure + */ + spans_mgr = (struct sigmgr *)KM_ALLOC(sizeof(struct sigmgr), + M_DEVBUF, M_NOWAIT); + if (spans_mgr == NULL) { + err = ENOMEM; + goto done; + } + KM_ZERO(spans_mgr, sizeof(struct sigmgr)); + + /* + * Initialize protocol invariant values + */ + spans_mgr->sm_proto = ATM_SIG_SPANS; + spans_mgr->sm_attach = spans_attach; + spans_mgr->sm_detach = spans_detach; + spans_mgr->sm_setup = spans_setup; + spans_mgr->sm_release = spans_release; + spans_mgr->sm_accept = spans_accept; + spans_mgr->sm_reject = spans_reject; + spans_mgr->sm_free = spans_free; + spans_mgr->sm_ioctl = spans_ioctl; + + /* + * Register ourselves with system + */ + err = atm_sigmgr_register(spans_mgr); + if (err) + goto done; + + /* + * Start up Connectionless Service + */ + err = spanscls_start(); + if (err) + goto done; + +done: + return (err); +} + + +/* + * Halt SPANS processing + * + * This should be called just prior to unloading the module from + * memory. All SPANS interfaces must be deregistered before the + * protocol can be shutdown. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +spans_stop() +{ + int err = 0; + int s = splnet(); + + /* + * Is protocol even set up? + */ + if (spans_mgr) { + + /* + * Any protocol instances still registered? + */ + if (spans_mgr->sm_prinst) { + + /* Yes, can't stop now */ + err = EBUSY; + goto done; + } + + /* + * Stop Connectionless Service + */ + spanscls_stop(); + + /* + * De-register from system + */ + err = atm_sigmgr_deregister(spans_mgr); + + /* + * Free up protocol block + */ + KM_FREE(spans_mgr, sizeof(struct sigmgr), M_DEVBUF); + spans_mgr = NULL; + + /* + * Free up our storage pools + */ + atm_release_pool(&spans_vcpool); + atm_release_pool(&spans_msgpool); + } else + err = ENXIO; + +done: + (void) splx(s); + return (err); +} + + +/* + * Attach a SPANS-controlled interface + * + * Each ATM physical interface must be attached with the signalling + * manager for the interface's signalling protocol (via the + * atm_sigmgr_attach function). This function will handle the + * attachment for SPANS-controlled interfaces. A new SPANS protocol + * instance will be created and then we'll just sit around waiting for + * status or connection requests. + * + * Function must be called at splnet. + * + * Arguments: + * smp pointer to SPANS signalling manager control block + * pip pointer to ATM physical interface control block + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +static int +spans_attach(smp, pip) + struct sigmgr *smp; + struct atm_pif *pip; +{ + int err = 0, n = 0, s; + struct spans *spp = NULL; + struct atm_nif *np; + + ATM_DEBUG2("spans_attach: smp=%x, pip=%x\n", smp, pip); + + /* + * Count network interfaces attached to the physical interface. + * If there are more or less than one, we have big problems. + */ + np = pip->pif_nif; + while (np) { + n++; + np = np->nif_pnext; + } + if (n != 1) { + err = ETOOMANYREFS; + goto done; + } + + /* + * Allocate SPANS protocol instance control block + */ + spp = (struct spans *)KM_ALLOC(sizeof(struct spans), + M_DEVBUF, M_NOWAIT); + if (spp == NULL) { + err = ENOMEM; + goto done; + } + KM_ZERO(spp, sizeof(struct spans)); + + /* + * Set variables in SPANS protocol instance control block + */ + spp->sp_state = SPANS_INIT; + spp->sp_h_epoch = time_second; + spp->sp_s_epoch = 0; + spp->sp_addr.address_format = T_ATM_ABSENT; + spp->sp_addr.address_length = 0; + spp->sp_subaddr.address_format = T_ATM_ABSENT; + spp->sp_subaddr.address_length = 0; + spp->sp_probe_ct = 0; + spp->sp_alloc_vci = SPANS_MIN_VCI; + spp->sp_alloc_vpi = SPANS_VPI; + spp->sp_min_vci = SPANS_MIN_VCI; + spp->sp_max_vci = pip->pif_maxvci; + + /* + * Link instance into manager's chain + */ + LINK2TAIL((struct siginst *)spp, struct siginst, smp->sm_prinst, + si_next); + + /* + * Link in interface + */ + spp->sp_pif = pip; + pip->pif_sigmgr = smp; + pip->pif_siginst = (struct siginst *) spp; + + /* + * Kick-start the SPANS protocol + */ + SPANS_TIMER(spp, 0); + + /* + * Notify Connectionless Service + */ + err = spanscls_attach(spp); + + /* + * Log the fact that we've attached + */ + if (!err) + log(LOG_INFO, "spans: attached to interface %s%d\n", + pip->pif_name, pip->pif_unit); + +done: + /* + * Reset our work if attach fails + */ + if (err) { + if (spp) { + SPANS_CANCEL(spp); + UNLINK((struct siginst *)spp, struct siginst, + smp->sm_prinst, si_next); + KM_FREE(spp, sizeof(struct spans), M_DEVBUF); + } + s = splimp(); + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + (void) splx(s); + } + + return (err); +} + + +/* + * Detach a SPANS-controlled interface + * + * Each ATM physical interface may be detached from its signalling + * manager (via the atm_sigmgr_detach function). This function will + * handle the detachment for all SPANS-controlled interfaces. All + * circuits will be immediately terminated. + * + * Function must be called at splnet. + * + * Arguments: + * pip pointer to ATM physical interface control block + * + * Returns: + * 0 detach successful + * errno detach failed - reason indicated + * + */ +static int +spans_detach(pip) + struct atm_pif *pip; +{ + struct spans *spp; + struct vccb *vcp, *vnext; + Atm_connection *cop; + int err; + + ATM_DEBUG1("spans_detach: pip=0x%x\n", pip); + + /* + * Get SPANS protocol instance + */ + spp = (struct spans *)pip->pif_siginst; + + /* + * Return an error if we're already detaching + */ + if (spp->sp_state == SPANS_DETACH) { + return(EALREADY); + } + + /* + * Cancel any outstanding timer + */ + SPANS_CANCEL(spp); + + /* + * Notify Connectionless Service + */ + spanscls_detach(spp); + + /* + * Terminate all of our VCCs + */ + for (vcp = Q_HEAD(spp->sp_vccq, struct vccb); vcp; vcp = vnext) { + + vnext = Q_NEXT(vcp, struct vccb, vc_sigelem); + + /* + * Don't close the signalling VCC yet + */ + if (vcp->vc_connvc && vcp->vc_connvc->cvc_conn == + spp->sp_conn) + continue; + + /* + * Close VCC and notify owner + */ + err = spans_clear_vcc(spp, (struct spans_vccb *)vcp); + if (err) { + log(LOG_ERR, "spans: error %d clearing VCCB 0x%x\n", + err, vcp); + } + } + + /* + * Now close the SPANS signalling VCC + */ + if (cop = spp->sp_conn) { + err = atm_cm_release(cop, &spans_cause); + if (err) + ATM_DEBUG2( + "spans_detach: close failed for SPANS signalling channel; cop=0x%x, err=%d\n", + cop, err); + } + + + /* + * Get rid of protocol instance if there are no VCCs queued + */ + if (Q_HEAD(spp->sp_vccq, struct vccb) == NULL) { + struct sigmgr *smp = pip->pif_sigmgr; + + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + UNLINK((struct siginst *)spp, struct siginst, + smp->sm_prinst, si_next); + KM_FREE(spp, sizeof(struct spans), M_DEVBUF); + } else { + /* + * Otherwise, wait for protocol instance to be freed + * during spans_free processing for the last queued VCC. + */ + spp->sp_state = SPANS_DETACH; + } + + /* + * Log the fact that we've detached + */ + log(LOG_INFO, "spans: detached from interface %s%d\n", + pip->pif_name, pip->pif_unit); + + return (0); +} + + +/* + * Open a SPANS ATM Connection + * + * All service user requests to open a VC connection (via + * atm_open_connection) over an ATM interface attached to the SPANS + * signalling manager are handled here. + * + * Function will be called at splnet. + * + * Arguments: + * cvp pointer to user's requested connection parameters + * errp pointer to an int for extended error information + * + * Returns: + * CALL_PROCEEDING connection establishment is in progress + * CALL_FAILED connection establishment failed + * CALL_CONNECTED connection has been successfully established + * + */ +static int +spans_setup(cvp, errp) + Atm_connvc *cvp; + int *errp; +{ + struct atm_pif *pip = cvp->cvc_attr.nif->nif_pif; + struct spans *spp = (struct spans *)pip->pif_siginst; + int rc = 0; + + ATM_DEBUG1("spans_setup: cvp=0x%x\n", cvp); + + /* + * Intialize the returned error code + */ + *errp = 0; + + /* + * Open the connection + */ + switch (cvp->cvc_attr.called.addr.address_format) { + case T_ATM_PVC_ADDR: + /* + * Create a PVC + */ + *errp = spans_open_vcc(spp, cvp); + rc = (*errp ? CALL_FAILED : CALL_CONNECTED); + break; + + case T_ATM_SPANS_ADDR: + + /* + * Create an SVC + */ + *errp = spans_open_vcc(spp, cvp); + rc = (*errp ? CALL_FAILED : CALL_PROCEEDING); + break; + + default: + *errp = EPROTONOSUPPORT; + rc = CALL_FAILED; + } + + return (rc); +} + + +/* + * Close a SPANS ATM Connection + * + * All service user requests to terminate a previously open VC + * connection (via the atm_close_connection function), which is running + * over an interface attached to the SPANS signalling manager, are + * handled here. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * errp pointer to an int for extended error information + * + * Returns: + * CALL_PROCEEDING connection termination is in progress + * CALL_FAILED connection termination failed + * CALL_CLEARED connection has been successfully terminated + * + */ +static int +spans_release(vcp, errp) + struct vccb *vcp; + int *errp; +{ + int rc = 0; + struct atm_pif *pip = vcp->vc_pif; + struct spans *spp = (struct spans *)pip->pif_siginst; + + ATM_DEBUG1("spans_release: vcp=0x%x\n", vcp); + + /* + * Initialize returned error code + */ + *errp = 0; + + /* + * Make sure VCC is open + */ + if ((vcp->vc_sstate == SPANS_VC_NULL) || + (vcp->vc_sstate == SPANS_VC_CLOSE) || + (vcp->vc_sstate == SPANS_VC_FREE) || + (vcp->vc_ustate == VCCU_NULL) || + (vcp->vc_ustate == VCCU_CLOSED)) { + *errp = EALREADY; + return(CALL_FAILED); + } + + /* + * Validate the connection type (PVC or SVC) + */ + if (!(vcp->vc_type & (VCC_PVC | VCC_SVC))) { + *errp = EPROTONOSUPPORT; + return(CALL_FAILED); + } + + /* + * Close the VCCB + */ + *errp = spans_close_vcc(spp, (struct spans_vccb *)vcp, FALSE); + + /* + * Set the return code + */ + if (vcp->vc_type & VCC_PVC) { + rc = (*errp ? CALL_FAILED : CALL_CLEARED); + } else { + rc = (*errp ? CALL_FAILED : CALL_PROCEEDING); + } + + return (rc); +} + + +/* + * Accept a SPANS Open from a remote host + * + * A user calls this routine (via the atm_accept_call function) + * after it is notified that an open request was received for it. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to user's VCCB + * errp pointer to an int for extended error information + * + * Returns: + * CALL_PROCEEDING connection establishment is in progress + * CALL_FAILED connection establishment failed + * CALL_CONNECTED connection has been successfully established + * + */ +static int +spans_accept(vcp, errp) + struct vccb *vcp; + int *errp; +{ + struct atm_pif *pip = vcp->vc_pif; + struct spans *spp = (struct spans *)pip->pif_siginst; + struct spans_vccb *svp = (struct spans_vccb *)vcp; + + ATM_DEBUG1("spans_accept: vcp=0x%x\n", vcp); + + /* + * Initialize the returned error code + */ + *errp = 0; + + /* + * Return an error if we're detaching + */ + if (spp->sp_state == SPANS_DETACH) { + *errp = ENETDOWN; + ATM_DEBUG0("spans_accept: detaching\n"); + return(CALL_FAILED); + } + + /* + * Respond to the open request + */ + *errp = spans_send_open_rsp(spp, svp, SPANS_OK); + if (*errp) { + ATM_DEBUG0("spans_accept: spans_send_open_rsp failed\n"); + goto failed; + } + + /* + * Update the VCC states + */ + svp->sv_sstate = SPANS_VC_OPEN; + svp->sv_ustate = VCCU_OPEN; + + return(CALL_CONNECTED); + +failed: + /* + * On error, free the VCCB and return CALL_FAILED + */ + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_ustate = VCCU_CLOSED; + DEQUEUE(svp, struct spans_vccb, sv_sigelem, spp->sp_vccq); + spans_free((struct vccb *)svp); + + return(CALL_FAILED); +} + + +/* + * Reject a SPANS Open from a remote host + * + * A user calls this routine (via the atm_reject_call function) + * after it is notified that an open request was received for it. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to user's VCCB + * errp pointer to an int for extended error information + * + * Returns: + * CALL_CLEARED call request rejected + * CALL_FAILED call rejection failed + * + */ +static int +spans_reject(vcp, errp) + struct vccb *vcp; + int *errp; +{ + struct atm_pif *pip = vcp->vc_pif; + struct spans *spp = (struct spans *)pip->pif_siginst; + struct spans_vccb *svp = (struct spans_vccb *)vcp; + + ATM_DEBUG1("spans_reject: vcp=0x%x\n", vcp); + + /* + * Initialize the returned error code + */ + *errp = 0; + + /* + * Return an error if we're detaching + */ + if (spp->sp_state == SPANS_DETACH) { + *errp = ENETDOWN; + ATM_DEBUG0("spans_reject: detaching\n"); + return(CALL_FAILED); + } + + ATM_DEBUG1("spans_reject: cause code is %d\n", + vcp->vc_connvc->cvc_attr.cause.v.cause_value); + + /* + * Clean up the VCCB--the connection manager will free it + * spans_close_vcc will send a SPANS open response + */ + if (*errp = spans_close_vcc(spp, svp, TRUE)) { + ATM_DEBUG0("spans_reject: spans_close_vcc failed\n"); + return(CALL_FAILED); + } + + return(CALL_CLEARED); +} + + +/* + * Abort a SPANS ATM Connection + * + * All (non-user) requests to abort a previously open VC connection (via + * the atm_abort_connection function), which is running over an + * interface attached to the SPANS signalling manager, are handled here. + * The VCC owner will be notified of the request, in order to initiate + * termination of the connection. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * + * Returns: + * 0 connection release was succesful + * errno connection release failed - reason indicated + * + */ +int +spans_abort(vcp) + struct vccb *vcp; +{ + + /* + * Make sure VCC is available + */ + if ((vcp->vc_sstate == SPANS_VC_NULL) || + (vcp->vc_sstate == SPANS_VC_CLOSE) || + (vcp->vc_sstate == SPANS_VC_FREE) || + (vcp->vc_ustate == VCCU_NULL) || + (vcp->vc_ustate == VCCU_CLOSED)) { + return(EALREADY); + } + + /* + * Only abort once + */ + if (vcp->vc_sstate == SPANS_VC_ABORT) { + return (EALREADY); + } + + /* + * Cancel any timer that might be running + */ + SPANS_VC_CANCEL(vcp); + + /* + * Set immediate timer to schedule connection termination + */ + vcp->vc_sstate = SPANS_VC_ABORT; + SPANS_VC_TIMER(vcp, 0); + + return (0); +} + + +/* + * Free SPANS ATM connection resources + * + * All service user requests to free the resources of a closed + * VCC connection (via the atm_free_connection function), which + * is running over an interface attached to the SigPVC signalling + * manager, are handled here. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * + * Returns: + * 0 connection free was successful + * errno connection free failed - reason indicated + * + */ +int +spans_free(vcp) + struct vccb *vcp; +{ + struct atm_pif *pip = vcp->vc_pif; + struct spans *spp = (struct spans *)pip->pif_siginst; + + ATM_DEBUG1("spans_free: vcp = 0x%x\n", vcp); + + /* + * Make sure VCC has been closed + */ + if ((vcp->vc_ustate != VCCU_CLOSED) || + (vcp->vc_sstate != SPANS_VC_FREE)) { + ATM_DEBUG2("spans_free: bad state, sstate=%d, ustate=%d\n", + vcp->vc_sstate, vcp->vc_ustate); + return(EEXIST); + } + + /* + * Remove VCCB from protocol queue + */ + DEQUEUE(vcp, struct vccb, vc_sigelem, spp->sp_vccq); + + /* + * Free VCCB storage + */ + vcp->vc_ustate = VCCU_NULL; + vcp->vc_sstate = SPANS_VC_NULL; + atm_free((caddr_t)vcp); + + /* + * If we're detaching and this was the last VCC queued, + * get rid of the protocol instance + */ + if ((spp->sp_state == SPANS_DETACH) && + (Q_HEAD(spp->sp_vccq, struct vccb) == NULL)) { + struct sigmgr *smp = pip->pif_sigmgr; + + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + UNLINK((struct siginst *)spp, struct siginst, smp->sm_prinst, + si_next); + KM_FREE(spp, sizeof(struct spans), M_DEVBUF); + } + + return (0); +} + + +/* + * SPANS IOCTL support + * + * Function will be called at splnet. + * + * Arguments: + * code PF_ATM sub-operation code + * data pointer to code specific parameter data area + * arg1 pointer to code specific argument + * + * Returns: + * 0 request procesed + * errno error processing request - reason indicated + * + */ +static int +spans_ioctl(code, data, arg1) + int code; + caddr_t data; + caddr_t arg1; +{ + struct atmdelreq *adp; + struct atminfreq *aip; + struct spans *spp; + struct spans_vccb *svp; + struct air_vcc_rsp rsp; + Atm_connection *cop; + int buf_len, err = 0, i, vpi, vci; + caddr_t buf_addr; + + + switch (code) { + + case AIOCS_DEL_PVC: + case AIOCS_DEL_SVC: + /* + * Delete a VCC + */ + adp = (struct atmdelreq *)data; + spp = (struct spans *)arg1; + + /* + * Don't let a user close the SPANS signalling VC or + * the SPANS CLS VC + */ + vpi = adp->adr_pvc_vpi; + vci = adp->adr_pvc_vci; + if ((vpi == SPANS_SIG_VPI && vci == SPANS_SIG_VCI) || + (vpi == SPANS_CLS_VPI && + vci == SPANS_CLS_VCI)) + return(EINVAL); + + /* + * Find requested VCC + */ + for (svp = Q_HEAD(spp->sp_vccq, struct spans_vccb); svp; + svp = Q_NEXT(svp, struct spans_vccb, sv_sigelem)) { + if ((svp->sv_vpi == vpi) && (svp->sv_vci == vci)) + break; + } + if (svp == NULL) + return (ENOENT); + + /* + * Check VCC type + */ + switch (code) { + case AIOCS_DEL_PVC: + if (!(svp->sv_type & VCC_PVC)) { + return(EINVAL); + } + break; + case AIOCS_DEL_SVC: + if (!(svp->sv_type & VCC_SVC)) { + return(EINVAL); + } + break; + } + + /* + * Schedule VCC termination + */ + err = spans_abort((struct vccb *)svp); + break; + + case AIOCS_INF_VCC: + /* + * Return VCC information + */ + aip = (struct atminfreq *)data; + spp = (struct spans *)arg1; + + buf_addr = aip->air_buf_addr; + buf_len = aip->air_buf_len; + + /* + * Loop through the VCC queue + */ + for (svp = Q_HEAD(spp->sp_vccq, struct spans_vccb); svp; + svp = Q_NEXT(svp, struct spans_vccb, sv_sigelem)) { + /* + * Make sure there's room in the user's buffer + */ + if (buf_len < sizeof(rsp)) { + err = ENOSPC; + break; + } + + /* + * Fill out the response struct for the VCC + */ + (void) sprintf(rsp.avp_intf, "%s%d", + spp->sp_pif->pif_name, + spp->sp_pif->pif_unit); + rsp.avp_vpi = svp->sv_vpi; + rsp.avp_vci = svp->sv_vci; + rsp.avp_type = svp->sv_type; + rsp.avp_aal = svp->sv_connvc->cvc_attr.aal.type; + rsp.avp_sig_proto = svp->sv_proto; + cop = svp->sv_connvc->cvc_conn; + if (cop) + rsp.avp_encaps = cop->co_mpx; + else + rsp.avp_encaps = 0; + rsp.avp_state = svp->sv_sstate; + KM_ZERO(rsp.avp_owners, sizeof(rsp.avp_owners)); + for (i = 0; cop && i < sizeof(rsp.avp_owners); + cop = cop->co_next, + i += T_ATM_APP_NAME_LEN+1) { + strncpy(&rsp.avp_owners[i], + cop->co_endpt->ep_getname(cop->co_toku), + T_ATM_APP_NAME_LEN); + } + rsp.avp_daddr.address_format = T_ATM_SPANS_ADDR; + rsp.avp_daddr.address_length = + sizeof(Atm_addr_spans); + if (svp->sv_type & VCC_OUT) { + spans_addr_copy(&svp->sv_conn.con_dst, + rsp.avp_daddr.address); + } else { + spans_addr_copy(&svp->sv_conn.con_src, + rsp.avp_daddr.address); + } + rsp.avp_dsubaddr.address_format = T_ATM_ABSENT; + rsp.avp_dsubaddr.address_length = 0; + rsp.avp_ipdus = svp->sv_ipdus; + rsp.avp_opdus = svp->sv_opdus; + rsp.avp_ibytes = svp->sv_ibytes; + rsp.avp_obytes = svp->sv_obytes; + rsp.avp_ierrors = svp->sv_ierrors; + rsp.avp_oerrors = svp->sv_oerrors; + rsp.avp_tstamp = svp->sv_tstamp; + + /* + * Copy the response into the user's buffer + */ + if (err = copyout((caddr_t)&rsp, buf_addr, + sizeof(rsp))) + break; + buf_addr += sizeof(rsp); + buf_len -= sizeof(rsp); + } + + /* + * Update the buffer pointer and length + */ + aip->air_buf_addr = buf_addr; + aip->air_buf_len = buf_len; + break; + + case AIOCS_ADD_ARP: + case AIOCS_DEL_ARP: + case AIOCS_INF_ARP: + case AIOCS_INF_ASV: + /* + * ARP specific ioctl's + */ + err = spansarp_ioctl(code, data, arg1); + break; + + default: + err = EOPNOTSUPP; + } + + return (err); +} + + +#ifdef ATM_SPANS_MODULE +/* + ******************************************************************* + * + * Loadable Module Support + * + ******************************************************************* + */ +static int spans_doload __P((void)); +static int spans_dounload __P((void)); + +/* + * 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 +spans_doload() +{ + int err = 0; + + /* + * Start us up + */ + err = spans_start(); + if (err) + /* Problems, clean up */ + (void)spans_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 +spans_dounload() +{ + int err = 0; + + /* + * OK, try to clean up our mess + */ + err = spans_stop(); + + return (err); +} + + +#ifdef sun +/* + * Loadable driver description + */ +struct vdldrv spans_drv = { + VDMAGIC_PSEUDO, /* Pseudo Driver */ + "spans_mod", /* name */ + NULL, /* 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 +spans_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 = spans_doload(); + if (err == 0) + /* Let vd driver know about us */ + vdp->vdd_vdtab = (struct vdlinkage *)&spans_drv; + break; + + case VDUNLOAD: + /* + * Module Unload + */ + err = spans_dounload(); + break; + + case VDSTAT: + /* + * Module Status + */ + + /* Not much to say at the moment */ + + break; + + default: + log(LOG_ERR, "spans_mod: Unknown vd command 0x%x\n", cmd); + err = EINVAL; + } + + return (err); +} +#endif /* sun */ + +#ifdef __FreeBSD__ + +#include <sys/exec.h> +#include <sys/sysent.h> +#include <sys/lkm.h> + +/* + * Loadable miscellaneous module description + */ +MOD_MISC(spans); + + +/* + * 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 +spans_load(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(spans_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 +spans_unload(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(spans_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 +spans_mod(lkmtp, cmd, ver) + struct lkm_table *lkmtp; + int cmd; + int ver; +{ + MOD_DISPATCH(spans, lkmtp, cmd, ver, + spans_load, spans_unload, lkm_nullcmd); +} +#endif /* __FreeBSD__ */ + +#else /* !ATM_SPANS_MODULE */ + +/* + ******************************************************************* + * + * Kernel Compiled Module Support + * + ******************************************************************* + */ +static void spans_doload __P((void *)); + +SYSINIT(atmspans, SI_SUB_PROTO_END, SI_ORDER_ANY, spans_doload, NULL) + +/* + * Kernel initialization + * + * Arguments: + * arg Not used + * + * Returns: + * none + * + */ +static void +spans_doload(void *arg) +{ + int err = 0; + + /* + * Start us up + */ + err = spans_start(); + if (err) { + /* Problems, clean up */ + (void)spans_stop(); + + log(LOG_ERR, "ATM SPANS unable to initialize (%d)!!\n", err); + } + return; +} +#endif /* ATM_SPANS_MODULE */ + diff --git a/sys/netatm/spans/spans_kxdr.c b/sys/netatm/spans/spans_kxdr.c new file mode 100644 index 0000000..b6534de --- /dev/null +++ b/sys/netatm/spans/spans_kxdr.c @@ -0,0 +1,684 @@ +/* + * + * =================================== + * 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: spans_kxdr.c,v 1.2 1997/05/06 22:17:00 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * Kernel XDR (External Data Representation) routines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_kxdr.c,v 1.2 1997/05/06 22:17:00 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +/* + * This file contains code that has been copied and/or modified from + * the following FreeBSD files: + * + * /usr/src/lib/libc/xdr/xdr.c + * /usr/src/lib/libc/xdr/xdr_mem.c + * + * which are covered by the copyright notice below. + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if !defined(sun) + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr.c 1.35 87/08/12";*/ +/*static char *sccsid = "from: @(#)xdr.c 2.1 88/07/29 4.0 RPCSRC";*/ +/*static char *rcsid = "Id: xdr.c,v 1.2.4.2 1996/06/05 02:52:02 jkh Exp";*/ +#endif + +/* + * xdr.c, Generic XDR routines implementation. + * + * Copyright (C) 1986, Sun Microsystems, Inc. + * + * These are the "generic" xdr routines used to serialize and de-serialize + * most common data items. See xdr.h for more info on the interface to + * xdr. + */ + +#include <rpc/types.h> +#include <rpc/xdr.h> + +/* + * constants specific to the xdr "protocol" + */ +#define XDR_FALSE ((long) 0) +#define XDR_TRUE ((long) 1) +#define LASTUNSIGNED ((u_int) 0-1) + +/* + * for unit alignment + */ +static char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 }; + +/* + * XDR integers + */ +bool_t +xdr_int(xdrs, ip) + XDR *xdrs; + int *ip; +{ + +#ifdef lint + (void) (xdr_short(xdrs, (short *)ip)); + return (xdr_long(xdrs, (long *)ip)); +#else + if (sizeof (int) == sizeof (long)) { + return (xdr_long(xdrs, (long *)ip)); + } else { + return (xdr_short(xdrs, (short *)ip)); + } +#endif +} + +/* + * XDR unsigned integers + */ +bool_t +xdr_u_int(xdrs, up) + XDR *xdrs; + u_int *up; +{ + +#ifdef lint + (void) (xdr_short(xdrs, (short *)up)); + return (xdr_u_long(xdrs, (u_long *)up)); +#else + if (sizeof (u_int) == sizeof (u_long)) { + return (xdr_u_long(xdrs, (u_long *)up)); + } else { + return (xdr_short(xdrs, (short *)up)); + } +#endif +} + +/* + * XDR long integers + * same as xdr_u_long - open coded to save a proc call! + */ +bool_t +xdr_long(xdrs, lp) + register XDR *xdrs; + long *lp; +{ + + if (xdrs->x_op == XDR_ENCODE) + return (XDR_PUTLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_FREE) + return (TRUE); + + return (FALSE); +} + +/* + * XDR unsigned long integers + * same as xdr_long - open coded to save a proc call! + */ +bool_t +xdr_u_long(xdrs, ulp) + register XDR *xdrs; + u_long *ulp; +{ + + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, (long *)ulp)); + if (xdrs->x_op == XDR_ENCODE) + return (XDR_PUTLONG(xdrs, (long *)ulp)); + if (xdrs->x_op == XDR_FREE) + return (TRUE); + return (FALSE); +} + +/* + * XDR short integers + */ +bool_t +xdr_short(xdrs, sp) + register XDR *xdrs; + short *sp; +{ + long l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (long) *sp; + return (XDR_PUTLONG(xdrs, &l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &l)) { + return (FALSE); + } + *sp = (short) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * XDR unsigned short integers + */ +bool_t +xdr_u_short(xdrs, usp) + register XDR *xdrs; + u_short *usp; +{ + u_long l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (u_long) *usp; + return (XDR_PUTLONG(xdrs, &l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &l)) { + return (FALSE); + } + *usp = (u_short) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + + +/* + * XDR a char + */ +bool_t +xdr_char(xdrs, cp) + XDR *xdrs; + char *cp; +{ + int i; + + i = (*cp); + if (!xdr_int(xdrs, &i)) { + return (FALSE); + } + *cp = i; + return (TRUE); +} + +/* + * XDR an unsigned char + */ +bool_t +xdr_u_char(xdrs, cp) + XDR *xdrs; + u_char *cp; +{ + u_int u; + + u = (*cp); + if (!xdr_u_int(xdrs, &u)) { + return (FALSE); + } + *cp = u; + return (TRUE); +} + +/* + * XDR booleans + */ +bool_t +xdr_bool(xdrs, bp) + register XDR *xdrs; + bool_t *bp; +{ + long lb; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + lb = *bp ? XDR_TRUE : XDR_FALSE; + return (XDR_PUTLONG(xdrs, &lb)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &lb)) { + return (FALSE); + } + *bp = (lb == XDR_FALSE) ? FALSE : TRUE; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * XDR enumerations + */ +bool_t +xdr_enum(xdrs, ep) + XDR *xdrs; + enum_t *ep; +{ +#ifndef lint + enum sizecheck { SIZEVAL }; /* used to find the size of an enum */ + + /* + * enums are treated as ints + */ + if (sizeof (enum sizecheck) == sizeof (long)) { + return (xdr_long(xdrs, (long *)ep)); + } else if (sizeof (enum sizecheck) == sizeof (short)) { + return (xdr_short(xdrs, (short *)ep)); + } else { + return (FALSE); + } +#else + (void) (xdr_short(xdrs, (short *)ep)); + return (xdr_long(xdrs, (long *)ep)); +#endif +} + +/* + * XDR opaque data + * Allows the specification of a fixed size sequence of opaque bytes. + * cp points to the opaque object and cnt gives the byte length. + */ +bool_t +xdr_opaque(xdrs, cp, cnt) + register XDR *xdrs; + caddr_t cp; + register u_int cnt; +{ + register u_int rndup; + static char crud[BYTES_PER_XDR_UNIT]; + + /* + * if no data we are done + */ + if (cnt == 0) + return (TRUE); + + /* + * round byte count to full xdr units + */ + rndup = cnt % BYTES_PER_XDR_UNIT; + if (rndup > 0) + rndup = BYTES_PER_XDR_UNIT - rndup; + + if (xdrs->x_op == XDR_DECODE) { + if (!XDR_GETBYTES(xdrs, cp, cnt)) { + return (FALSE); + } + if (rndup == 0) + return (TRUE); + return (XDR_GETBYTES(xdrs, crud, rndup)); + } + + if (xdrs->x_op == XDR_ENCODE) { + if (!XDR_PUTBYTES(xdrs, cp, cnt)) { + return (FALSE); + } + if (rndup == 0) + return (TRUE); + return (XDR_PUTBYTES(xdrs, xdr_zero, rndup)); + } + + if (xdrs->x_op == XDR_FREE) { + return (TRUE); + } + + return (FALSE); +} + + +/* + * XDR implementation using kernel buffers + */ + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/ +/*static char *sccsid = "from: @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC";*/ +/*static char *rcsid = "Id: xdr_mem.c,v 1.2.4.2 1996/06/05 02:52:04 jkh Exp";*/ +#endif + +/* + * xdr_mem.h, XDR implementation using memory buffers. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * If you have some data to be interpreted as external data representation + * or to be converted to external data representation in a memory buffer, + * then this is the package for you. + * + */ + + +void xdrmbuf_init __P((XDR *, KBuffer *, enum xdr_op)); +static bool_t xdrmbuf_getlong __P((XDR *, long *)); +static bool_t xdrmbuf_putlong __P((XDR *, long *)); +static bool_t xdrmbuf_getbytes __P((XDR *, caddr_t, u_int)); +static bool_t xdrmbuf_putbytes __P((XDR *, caddr_t, u_int)); +static u_int xdrmbuf_getpos __P((XDR *)); + +static struct xdr_ops xdrmbuf_ops = { + xdrmbuf_getlong, + xdrmbuf_putlong, + xdrmbuf_getbytes, + xdrmbuf_putbytes, + xdrmbuf_getpos, + NULL, + NULL, + NULL +}; + +/* + * The procedure xdrmbuf_init initializes a stream descriptor for a + * kernel buffer. + */ +void +xdrmbuf_init(xdrs, m, op) + register XDR *xdrs; + KBuffer *m; + enum xdr_op op; +{ + + xdrs->x_op = op; + xdrs->x_ops = &xdrmbuf_ops; + xdrs->x_base = (caddr_t)m; + KB_DATASTART(m, xdrs->x_private, caddr_t); + xdrs->x_handy = KB_LEN(m); +} + +static bool_t +xdrmbuf_getlong(xdrs, lp) + register XDR *xdrs; + long *lp; +{ + + /* + * See if long is contained in this buffer + */ + if ((xdrs->x_handy -= sizeof(long)) < 0) { + register KBuffer *m; + + /* + * We (currently) don't allow a long to span a buffer + */ + if (xdrs->x_handy != -sizeof(long)) { + printf("xdrmbuf_getlong: data spans buffers\n"); + return (FALSE); + } + + /* + * Try to move to a chained buffer + */ + if ((m = (KBuffer *)(xdrs->x_base)) != NULL) { + m = KB_NEXT(m); + xdrs->x_base = (caddr_t)m; + } + if (m) { + /* + * Setup new buffer's info + */ + KB_DATASTART(m, xdrs->x_private, caddr_t); + if ((xdrs->x_handy = KB_LEN(m) - sizeof(long)) < 0) { + printf("xdrmbuf_getlong: short buffer\n"); + return (FALSE); + } + } else { + /* + * No more buffers + */ + return (FALSE); + } + } + + /* + * Return the long value + */ + *lp = (long)ntohl((u_long)(*((long *)(xdrs->x_private)))); + + /* + * Advance the data stream + */ + xdrs->x_private += sizeof(long); + return (TRUE); +} + +static bool_t +xdrmbuf_putlong(xdrs, lp) + register XDR *xdrs; + long *lp; +{ + + /* + * See if long will fit in this buffer + */ + if ((xdrs->x_handy -= sizeof(long)) < 0) { + register KBuffer *m; + + /* + * We (currently) don't allow a long to span a buffer + */ + if (xdrs->x_handy != -sizeof(long)) { + printf("xdrmbuf_putlong: data spans buffers\n"); + return (FALSE); + } + + /* + * Try to move to a chained buffer + */ + if ((m = (KBuffer *)(xdrs->x_base)) != NULL) { + m = KB_NEXT(m); + xdrs->x_base = (caddr_t)m; + } + if (m) { + /* + * Setup new buffer's info + */ + KB_DATASTART(m, xdrs->x_private, caddr_t); + if ((xdrs->x_handy = KB_LEN(m) - sizeof(long)) < 0) { + printf("xdrmbuf_putlong: short buffer\n"); + return (FALSE); + } + } else { + /* + * No more buffers + */ + return (FALSE); + } + } + + /* + * Store the long value into our buffer + */ + *(long *)xdrs->x_private = (long)htonl((u_long)(*lp)); + + /* + * Advance the data stream + */ + xdrs->x_private += sizeof(long); + return (TRUE); +} + +static bool_t +xdrmbuf_getbytes(xdrs, addr, len) + register XDR *xdrs; + caddr_t addr; + register u_int len; +{ + + while (len > 0) { + u_int copy; + + if (xdrs->x_handy <= 0) { + register KBuffer *m; + + /* + * No data in current buffer, move to a chained buffer + */ + if ((m = (KBuffer *)(xdrs->x_base)) != NULL) { + m = KB_NEXT(m); + xdrs->x_base = (caddr_t)m; + } + if (m) { + /* + * Setup new buffer's info + */ + KB_DATASTART(m, xdrs->x_private, caddr_t); + xdrs->x_handy = KB_LEN(m); + } else { + /* + * No more buffers + */ + return (FALSE); + } + } + + /* + * Copy from buffer to user's space + */ + copy = MIN(len, xdrs->x_handy); + KM_COPY(xdrs->x_private, addr, copy); + + /* + * Update data stream controls + */ + xdrs->x_private += copy; + xdrs->x_handy -= copy; + addr += copy; + len -= copy; + } + return (TRUE); +} + +static bool_t +xdrmbuf_putbytes(xdrs, addr, len) + register XDR *xdrs; + caddr_t addr; + register u_int len; +{ + + while (len > 0) { + u_int copy; + + if (xdrs->x_handy <= 0) { + register KBuffer *m; + + /* + * No data in current buffer, move to a chained buffer + */ + if ((m = (KBuffer *)(xdrs->x_base)) != NULL) { + m = KB_NEXT(m); + xdrs->x_base = (caddr_t)m; + } + if (m) { + /* + * Setup new buffer's info + */ + KB_DATASTART(m, xdrs->x_private, caddr_t); + xdrs->x_handy = KB_LEN(m); + } else { + /* + * No more buffers + */ + return (FALSE); + } + } + + /* + * Copy from user's space into buffer + */ + copy = MIN(len, xdrs->x_handy); + KM_COPY(addr, xdrs->x_private, copy); + + /* + * Update data stream controls + */ + xdrs->x_private += copy; + xdrs->x_handy -= copy; + addr += copy; + len -= copy; + } + return (TRUE); +} + +static u_int +xdrmbuf_getpos(xdrs) + register XDR *xdrs; +{ + + return ((u_int)xdrs->x_private - (u_int)xdrs->x_base); +} + +#endif /* !defined(sun) */ + diff --git a/sys/netatm/spans/spans_msg.c b/sys/netatm/spans/spans_msg.c new file mode 100644 index 0000000..0dc5df9 --- /dev/null +++ b/sys/netatm/spans/spans_msg.c @@ -0,0 +1,1633 @@ +/* + * + * =================================== + * 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: spans_msg.c,v 1.8 1998/08/26 23:29:09 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS signalling message processing. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_msg.c,v 1.8 1998/08/26 23:29:09 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <rpc/rpc.h> +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> + +/* + * External functions + */ +void xdrmbuf_init __P((XDR *, KBuffer *, enum xdr_op)); + +/* + * Local functions + */ +static void spans_host_link __P((struct spans *, long)); +static void spans_status_ind __P((struct spans *, spans_msg *)); +static void spans_status_rsp __P((struct spans *, spans_msg *)); +static void spans_open_req __P((struct spans *, spans_msg *)); +static void spans_open_rsp __P((struct spans *, spans_msg *)); +static void spans_close_req __P((struct spans *, spans_msg *)); +static void spans_close_rsp __P((struct spans *, spans_msg *)); +static void spans_multi_req __P((struct spans *, spans_msg *)); +static void spans_add_req __P((struct spans *, spans_msg *)); +static void spans_join_req __P((struct spans *, spans_msg *)); +static void spans_leave_req __P((struct spans *, spans_msg *)); +static void spans_vcir_ind __P((struct spans *, spans_msg *)); +static void spans_query_req __P((struct spans *, spans_msg *)); + + +/* + * Called to set status when a status message comes in from a host + * connected back-to-back with us. Check the epoch and, if it has + * changed, set the appropriate state and save updated state + * information. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * host_epoch epoch of host at far end of link + * + * Returns: + * 0 message sent OK + * errno error encountered + * + */ +static void +spans_host_link(spp, host_epoch) + struct spans *spp; + long host_epoch; +{ + struct atm_pif *pip = spp->sp_pif; + + /* + * There's a host at the other end of the link. If its + * epoch has changed, clean up our state and save the + * new information. + */ + if (spp->sp_s_epoch != host_epoch) { + spp->sp_s_epoch = host_epoch; + spans_switch_reset(spp, SPANS_UNI_UP); + spp->sp_addr.address_format = T_ATM_SPANS_ADDR; + spp->sp_addr.address_length = sizeof(spans_addr); + KM_COPY(&pip->pif_macaddr.ma_data[2], + &spp->sp_addr.address[4], + 4); + log(LOG_INFO, + "spans: using SPANS address of %s on interface %s%d\n", + spans_addr_print((spans_addr *)spp->sp_addr.address), + pip->pif_name, + pip->pif_unit); + } +} + +/* + * Send a SPANS signalling message + * + * Called to send a SPANS message. This routine gets a buffer, performs + * XDR processing, and hands the message to the AAL for transmission. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to status message + * + * Returns: + * 0 message sent OK + * errno error encountered + * + */ +int +spans_send_msg(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + int err = 0; + KBuffer *m; + XDR xdrs; + +#ifdef NOTDEF + ATM_DEBUG2("spans_send_msg: msg=0x%x, type=%d\n", msg, + msg->sm_type); + if (msg->sm_type != SPANS_STAT_REQ && + msg->sm_type != SPANS_STAT_IND && + msg->sm_type != SPANS_STAT_RSP) { + printf("spans_send_msg: sending "); + spans_print_msg(msg); + } +#endif + + /* + * If the signalling channel has been closed, don't do anything + */ + if (!spp->sp_conn) + return(ECONNABORTED); + + /* + * Get a buffer + */ + KB_ALLOCPKT(m, sizeof(spans_msg), KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) { + /* No buffer available */ + return(ENOBUFS); + } + + /* + * Convert message to network order + */ + KB_LEN(m) = KB_BFRLEN(m); + xdrmbuf_init(&xdrs, m, XDR_ENCODE); + if (!xdr_spans_msg(&xdrs, msg)) { + log(LOG_ERR, "spans_send_msg: XDR encode failed\n"); + KB_LEN(m) = XDR_GETPOS(&xdrs); + spans_dump_buffer(m); + KB_FREEALL(m); + return(EIO); + } + KB_LEN(m) = XDR_GETPOS(&xdrs); + + /* + * Send the message + */ + err = atm_cm_cpcs_data(spp->sp_conn, m); + if (err) + KB_FREEALL(m); + + return(err); +} + + +/* + * Send an open request + * + * Build and send an open request. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * svp pointer to VCCB for which the request is being sent + * + * Returns: + * none + * + */ +int +spans_send_open_req(spp, svp) + struct spans *spp; + struct spans_vccb *svp; +{ + spans_msg *req; + int err = 0; + + ATM_DEBUG1("spans_send_open_req: svp=0x%x\n", svp); + + /* + * Get memory for a request message + */ + req = (spans_msg *)atm_allocate(&spans_msgpool); + if (req == NULL) { + err = ENOBUFS; + goto done; + } + + /* + * Fill in the request + */ + req->sm_vers = SPANS_VERS_1_0; + req->sm_type = SPANS_OPEN_REQ; + req->sm_open_req.opreq_conn = svp->sv_conn; + req->sm_open_req.opreq_aal = svp->sv_spans_aal; + req->sm_open_req.opreq_desrsrc = svp->sv_spans_qos; + req->sm_open_req.opreq_minrsrc.rsc_peak = 0; + req->sm_open_req.opreq_minrsrc.rsc_mean = 0; + req->sm_open_req.opreq_minrsrc.rsc_burst = 0; + req->sm_open_req.opreq_vpvc.vpf_valid = FALSE; + + /* + * Send the request + */ + err = spans_send_msg(spp, req); + atm_free(req); + +done: + return(err); +} + + +/* + * Send an open response + * + * Build and send a response to an open request or open indication. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * svp pointer to VCCB for which the response is being sent + * result result code to include in the response + * + * Returns: + * none + * + */ +int +spans_send_open_rsp(spp, svp, result) + struct spans *spp; + struct spans_vccb *svp; + spans_result result; +{ + spans_msg *rsp; + int rc; + + ATM_DEBUG2("spans_send_open_rsp: svp=0x%x, result=%d\n", svp, + result); + + /* + * Get memory for a response message + */ + rsp = (spans_msg *)atm_allocate(&spans_msgpool); + if (rsp == NULL) + return(ENOBUFS); + + /* + * Fill in the response + */ + rsp->sm_vers = SPANS_VERS_1_0; + rsp->sm_type = SPANS_OPEN_RSP; + rsp->sm_open_rsp.oprsp_conn = svp->sv_conn; + rsp->sm_open_rsp.oprsp_result = result; + rsp->sm_open_rsp.oprsp_rsrc = svp->sv_spans_qos; + rsp->sm_open_rsp.oprsp_vpvc = + SPANS_PACK_VPIVCI(svp->sv_vpi, svp->sv_vci); + + /* + * Send the response + */ + rc = spans_send_msg(spp, rsp); + atm_free(rsp); + + return(rc); +} + + +/* + * Send a close request + * + * Called to send a close request. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * svp pointer to VCCB for which the close is being sent + * + * Returns: + * none + * + */ +int +spans_send_close_req(spp, svp) + struct spans *spp; + struct spans_vccb *svp; +{ + spans_msg *req; + int err = 0; + + ATM_DEBUG1("spans_send_close_req: svp=0x%x\n", svp); + + /* + * Get memory for a close request + */ + req = (spans_msg *)atm_allocate(&spans_msgpool); + if (req == NULL) { + err = ENOBUFS; + goto done; + } + + /* + * Fill in the request + */ + req->sm_vers = SPANS_VERS_1_0; + if (svp->sv_type & VCC_OUT) { + req->sm_type = SPANS_CLOSE_REQ; + } else if (svp->sv_type & VCC_IN) { + req->sm_type = SPANS_RCLOSE_REQ; + } else { + err = EINVAL; + ATM_DEBUG1( + "spans_send_close_req: invalid VCCB type 0x%x\n", + svp->sv_type); + goto done; + } + req->sm_close_req.clreq_conn = svp->sv_conn; + + /* + * Send the close request + */ + err = spans_send_msg(spp, req); + +done: + if (req) + atm_free(req); + + return(err); +} + + + +/* + * Process a status indication or status request + * + * Called when a status indication or status request is received. + * Processing will be based on the current SPANS state. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the status message + * + * Returns: + * none + * + */ +static void +spans_status_ind(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + spans_msg *rsp_msg; + struct atm_pif *pip = spp->sp_pif; + + /* + * Reset the probe count. + */ + spp->sp_probe_ct = 0; + + switch (spp->sp_state) { + case SPANS_PROBE: + /* + * Interface just came up, update signalling state + */ + spp->sp_state = SPANS_ACTIVE; + break; + + case SPANS_ACTIVE: + break; + + default: + log(LOG_ERR, "spans: received status msg in state %d\n", + spp->sp_state); + } + + /* + * Process the message + */ + switch (msg->sm_type) { + + case SPANS_STAT_REQ: + /* + * Handle a request from a host at the other end of + * the link. + */ + spans_host_link(spp, msg->sm_stat_req.streq_es_epoch); + break; + + case SPANS_STAT_IND: + + /* + * There's a switch at the other end of the link. If + * its epoch has changed, reset the SPANS state and save + * the new information. + */ + if (spp->sp_s_epoch != + msg->sm_stat_ind.stind_sw_epoch) { + spans_switch_reset(spp, SPANS_UNI_UP); + spp->sp_s_epoch = + msg->sm_stat_ind.stind_sw_epoch; + spp->sp_addr.address_format = T_ATM_SPANS_ADDR; + spp->sp_addr.address_length = + sizeof(spans_addr); + spans_addr_copy(&msg->sm_stat_ind.stind_es_addr, + spp->sp_addr.address); + log(LOG_INFO, + "spans: received SPANS address %s from switch for interface %s%d\n", + spans_addr_print((spans_addr *)spp->sp_addr.address), + pip->pif_name, + pip->pif_unit); + } + break; + + default: + ATM_DEBUG1("spans_status_ind: Invalid message type %d\n", + msg->sm_type); + return; + } + + /* + * Respond to the status request or indication with a + * status response + */ + rsp_msg = (spans_msg *)atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_STAT_RSP; + rsp_msg->sm_stat_rsp.strsp_es_epoch = spp->sp_h_epoch; + spans_addr_copy(spp->sp_addr.address, + &rsp_msg->sm_stat_rsp.strsp_es_addr); + spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + + +/* + * Process a status response + * + * Called when a status response is received. + * Processing will be based on the current SPANS state. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the status response message + * + * Returns: + * none + * + */ +static void +spans_status_rsp(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + + /* + * Reset the probe count. + */ + spp->sp_probe_ct = 0; + + switch (spp->sp_state) { + case SPANS_PROBE: + /* + * Interface just came up, update signalling state + */ + spp->sp_state = SPANS_ACTIVE; + break; + + case SPANS_ACTIVE: + break; + + default: + log(LOG_ERR, "spans: received status msg in state %d\n", + spp->sp_state); + } + + /* + * Process the message + */ + spans_host_link(spp, msg->sm_stat_req.streq_es_epoch); +} + + +/* + * Process an open indication or open request + * + * Called when an open indication or open request is received. + * Processing will be based on the state of the requested connection. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the open message + * + * Returns: + * none + * + */ +static void +spans_open_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + spans_result result = SPANS_OK; + spans_msg *rsp_msg; + struct spans_vccb *svp = NULL; + struct atm_pif *pip; + spans_vpvc vpvc; + int err = 0, vpi, vci; + Aal_t aal; + Atm_attributes call_attrs; + + ATM_DEBUG2("spans_open_req: spp=0x%x, msg=0x%x\n", spp, msg); + + /* + * See if the connection is new + */ + if (svp = spans_find_conn(spp, &msg->sm_open_req.opreq_conn)) { + /* + * We already have a VCCB that matches the connection in + * the request + */ + vpi = SPANS_EXTRACT_VPI(msg->sm_open_req.opreq_vpvc.vpf_vpvc); + vci = SPANS_EXTRACT_VCI(msg->sm_open_req.opreq_vpvc.vpf_vpvc); + if (msg->sm_open_req.opreq_aal == svp->sv_spans_aal && + (!msg->sm_open_req.opreq_vpvc.vpf_valid || + (vpi == svp->sv_vpi && + vci == svp->sv_vci))) { + /* + * VCCB already exists, process depending on + * state + */ + switch (svp->sv_sstate) { + case SPANS_VC_R_POPEN: + /* I'm still thinking about it */ + return; + case SPANS_VC_OPEN: + /* Retransmit the open_rsp */ + break; + case SPANS_VC_POPEN: + case SPANS_VC_CLOSE: + case SPANS_VC_ABORT: + ATM_DEBUG0("spans_open_req: bad VCCB state\n"); + result = SPANS_FAIL; + break; + } + } else { + /* + * VCCB is for same connection, but other + * parameters don't match + */ + ATM_DEBUG0("spans_open_req: VCCB confusion\n"); + result = SPANS_FAIL; + } + svp = NULL; + goto response; + } + + /* + * Verify that the request is for our ATM addres + */ + if (spans_addr_cmp(spp->sp_addr.address, + &msg->sm_open_req.opreq_conn.con_dst)) { + ATM_DEBUG0("spans_open_req: bad destination\n"); + result = SPANS_BADDEST; + goto response; + } + + /* + * See if we recognize the specified AAL + */ + if (!spans_get_local_aal(msg->sm_open_req.opreq_aal, &aal)) { + ATM_DEBUG0("spans_open_req: bad AAL\n"); + result = SPANS_FAIL; + goto response; + } + + /* + * Should verify that we can handle requested connection QOS + */ + + /* + * Select a VPI/VCI for the new connection + */ + if (msg->sm_open_req.opreq_vpvc.vpf_valid) { + /* + * Requestor asked for a certain VPI/VCI. Make sure we + * aren't already using the pair that was asked for. + */ + vpi = SPANS_EXTRACT_VPI(msg->sm_open_req.opreq_vpvc.vpf_vpvc); + vci = SPANS_EXTRACT_VCI(msg->sm_open_req.opreq_vpvc.vpf_vpvc); + if (spans_find_vpvc(spp, vci, vpi, VCC_IN)) { + ATM_DEBUG0("spans_open_req: VPI, VCI busy\n"); + result = SPANS_NOVPVC; + goto response; + } + vpvc = msg->sm_open_req.opreq_vpvc.vpf_vpvc; + } else { + /* + * Allocate a VPI/VCI for this end of the VCC + */ + vpvc = spans_alloc_vpvc(spp); + if (vpvc == 0) { + ATM_DEBUG0("spans_open_req: no VPI, VCI available\n"); + result = SPANS_NOVPVC; + goto response; + } + } + + /* + * Get a new VCCB for the connection + */ + svp = (struct spans_vccb *)atm_allocate(&spans_vcpool); + if (svp == NULL) { + ATM_DEBUG0("spans_open_req: VCCB pool empty\n"); + result = SPANS_NORSC; + goto response; + } + + /* + * Find the physical interface structure + */ + pip = spp->sp_pif; + + /* + * Fill in the VCCB fields that we can at this point + */ + svp->sv_type = VCC_SVC | VCC_IN; + svp->sv_proto = ATM_SIG_SPANS; + svp->sv_sstate = SPANS_VC_R_POPEN; + svp->sv_ustate = VCCU_POPEN; + svp->sv_pif = pip; + svp->sv_nif = pip->pif_nif; + svp->sv_conn = msg->sm_open_req.opreq_conn; + svp->sv_spans_qos = msg->sm_open_req.opreq_desrsrc; + svp->sv_spans_aal = msg->sm_open_req.opreq_aal; + svp->sv_tstamp = time_second; + + svp->sv_vpi = SPANS_EXTRACT_VPI(vpvc); + svp->sv_vci = SPANS_EXTRACT_VCI(vpvc); + + /* + * Put the VCCB on the SPANS queue + */ + ENQUEUE(svp, struct spans_vccb, sv_sigelem, spp->sp_vccq); + + /* + * Set up the ATM attributes block + */ + KM_ZERO(&call_attrs, sizeof(call_attrs)); + call_attrs.nif = svp->sv_nif; + call_attrs.api = CMAPI_CPCS; + + call_attrs.aal.tag = T_ATM_PRESENT; + call_attrs.aal.type = aal; + switch(aal) { + case ATM_AAL3_4: + call_attrs.aal.v.aal4.forward_max_SDU_size = + ATM_NIF_MTU; + call_attrs.aal.v.aal4.backward_max_SDU_size = + ATM_NIF_MTU; + call_attrs.aal.v.aal4.SSCS_type = + T_ATM_NULL; + call_attrs.aal.v.aal4.mid_low = 0; + call_attrs.aal.v.aal4.mid_high = 1023; + break; + case ATM_AAL5: + call_attrs.aal.v.aal5.forward_max_SDU_size = + ATM_NIF_MTU; + call_attrs.aal.v.aal5.backward_max_SDU_size = + ATM_NIF_MTU; + call_attrs.aal.v.aal5.SSCS_type = + T_ATM_NULL; + break; + } + + call_attrs.traffic.tag = T_ATM_PRESENT; + call_attrs.traffic.v.forward.PCR_high_priority = T_ATM_ABSENT; + call_attrs.traffic.v.forward.PCR_all_traffic = + msg->sm_open_req.opreq_desrsrc.rsc_peak * + 1000 / 53; + call_attrs.traffic.v.forward.SCR_high_priority = T_ATM_ABSENT; + call_attrs.traffic.v.forward.SCR_all_traffic = T_ATM_ABSENT; + call_attrs.traffic.v.forward.MBS_high_priority = T_ATM_ABSENT; + call_attrs.traffic.v.forward.MBS_all_traffic = T_ATM_ABSENT; + call_attrs.traffic.v.forward.tagging = T_NO; + call_attrs.traffic.v.backward.PCR_high_priority = T_ATM_ABSENT; + call_attrs.traffic.v.backward.PCR_all_traffic = + call_attrs.traffic.v.forward.PCR_all_traffic; + call_attrs.traffic.v.backward.SCR_high_priority = T_ATM_ABSENT; + call_attrs.traffic.v.backward.SCR_all_traffic = T_ATM_ABSENT; + call_attrs.traffic.v.backward.MBS_high_priority = T_ATM_ABSENT; + call_attrs.traffic.v.backward.MBS_all_traffic = T_ATM_ABSENT; + call_attrs.traffic.v.backward.tagging = T_NO; + call_attrs.traffic.v.best_effort = T_YES; + + call_attrs.bearer.tag = T_ATM_PRESENT; + call_attrs.bearer.v.bearer_class = T_ATM_CLASS_X; + call_attrs.bearer.v.traffic_type = T_ATM_NULL; + call_attrs.bearer.v.timing_requirements = T_ATM_NULL; + call_attrs.bearer.v.clipping_susceptibility = T_NO; + call_attrs.bearer.v.connection_configuration = T_ATM_1_TO_1; + + + call_attrs.bhli.tag = T_ATM_ABSENT; + call_attrs.blli.tag_l2 = T_ATM_ABSENT; + call_attrs.blli.tag_l3 = T_ATM_ABSENT; + call_attrs.llc.tag = T_ATM_ABSENT; + + call_attrs.called.tag = T_ATM_PRESENT; + spans_addr_copy(&msg->sm_open_req.opreq_conn.con_dst, + call_attrs.called.addr.address); + call_attrs.called.addr.address_format = T_ATM_SPANS_ADDR; + call_attrs.called.addr.address_length = sizeof(spans_addr); + call_attrs.called.subaddr.address_format = T_ATM_ABSENT; + call_attrs.called.subaddr.address_length = 0; + + call_attrs.calling.tag = T_ATM_PRESENT; + spans_addr_copy(&msg->sm_open_req.opreq_conn.con_src, + call_attrs.calling.addr.address); + call_attrs.calling.addr.address_format = T_ATM_SPANS_ADDR; + call_attrs.calling.addr.address_length = sizeof(spans_addr); + call_attrs.calling.subaddr.address_format = T_ATM_ABSENT; + call_attrs.calling.subaddr.address_length = 0; + + call_attrs.qos.tag = T_ATM_PRESENT; + call_attrs.qos.v.coding_standard = T_ATM_NETWORK_CODING; + call_attrs.qos.v.forward.qos_class = T_ATM_QOS_CLASS_0; + call_attrs.qos.v.backward.qos_class = T_ATM_QOS_CLASS_0; + + call_attrs.transit.tag = T_ATM_ABSENT; + call_attrs.cause.tag = T_ATM_ABSENT; + + /* + * Notify the connection manager that it has a new channel + */ + err = atm_cm_incoming((struct vccb *)svp, &call_attrs); + if (err) { + ATM_DEBUG0("spans_open_req: atm_cm_incoming returned error\n"); + result = SPANS_FAIL; + goto response; + } + + /* + * Wait for the connection recipient to issue an accept + */ + return; + +response: + /* + * Clean up the VCCB and the atm_conn block if we got them + */ + if (svp) { + DEQUEUE(svp, struct spans_vccb, sv_sigelem, + spp->sp_vccq); + atm_free(svp); + } + + /* + * Some problem was detected with the request. Send a SPANS + * message rejecting the connection. + */ + rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + + /* + * Fill out the response + */ + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_OPEN_RSP; + rsp_msg->sm_open_rsp.oprsp_conn = msg->sm_open_req.opreq_conn; + rsp_msg->sm_open_rsp.oprsp_result = result; + rsp_msg->sm_open_rsp.oprsp_vpvc = 0; + + /* + * Send the Open Response + */ + spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process an open response or open confirmation + * + * Called when an open response or open confirmation is received. + * Processing will be based on the state of the requested connection and + * the status returned. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the open response or confirmation message + * + * Returns: + * none + * + */ +static void +spans_open_rsp(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + struct spans_vccb *svp; + + ATM_DEBUG2("spans_open_rsp: spp=0x%x, msg=0x%x\n", spp, msg); + + /* + * Locate the VCCB for the connection + */ + svp = spans_find_conn(spp, &msg->sm_open_rsp.oprsp_conn); + if (svp == NULL) + return; + + /* + * Check the connection state + */ + if ((svp->sv_sstate != SPANS_VC_POPEN && + svp->sv_sstate != SPANS_VC_R_POPEN) || + svp->sv_ustate != VCCU_POPEN) { + ATM_DEBUG2( + "spans_open_rsp: invalid VCCB state, sstate=%d, ustate=%d\n", + svp->sv_sstate, svp->sv_ustate); + return; + } + + /* + * Cancel the retransmission timer + */ + SPANS_VC_CANCEL((struct vccb *) svp); + + /* + * Check the result + */ + switch (msg->sm_open_rsp.oprsp_result) { + + case SPANS_OK: + /* + * Save the assigned VPI and VCI + */ + svp->sv_vpi = SPANS_EXTRACT_VPI(msg->sm_open_rsp.oprsp_vpvc); + svp->sv_vci = SPANS_EXTRACT_VCI(msg->sm_open_rsp.oprsp_vpvc); + + /* + * Update the VCC state and notify the VCC owner + */ + svp->sv_sstate = SPANS_VC_OPEN; + svp->sv_ustate = VCCU_OPEN; + svp->sv_tstamp = time_second; + atm_cm_connected(svp->sv_connvc); + break; + + case SPANS_FAIL: + case SPANS_NOVPVC: + case SPANS_NORSC: + case SPANS_BADDEST: + /* + * Close out the VCCB and notify the user + */ + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_ustate = VCCU_CLOSED; + svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT; + svp->sv_connvc->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + svp->sv_connvc->cvc_attr.cause.v.location = + T_ATM_LOC_USER; + svp->sv_connvc->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_CALL_REJECTED; + KM_ZERO(svp->sv_connvc->cvc_attr.cause.v.diagnostics, + sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics)); + atm_cm_cleared(svp->sv_connvc); + break; + + default: + log(LOG_ERR, "spans: unknown result %d in open rsp\n", + msg->sm_open_rsp.oprsp_result); + break; + } +} + + +/* + * Process a close request from the network + * + * Called when a close request, close indication, rclose request, or + * rclose indication is received. Processing will be based on the + * state of the connection. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the close request message + * + * Returns: + * none + * + */ +static void +spans_close_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + struct spans_vccb *svp; + spans_result result; + spans_msg *rsp_msg; + u_char outstate; + Atm_connvc *cvp; + + ATM_DEBUG2("spans_close_req: spp=0x%x, msg=0x%x\n", spp, msg); + + /* + * Locate the VCCB for the connection + */ + svp = spans_find_conn(spp, &msg->sm_close_req.clreq_conn); + if (svp == NULL) { + result = SPANS_BADDEST; + goto response; + } + + /* + * Check the connection type + */ + if (!(svp->sv_type & VCC_SVC)) { + result = SPANS_FAIL; + goto response; + } + + /* + * Check the connection state + */ + switch (svp->sv_sstate) { + case SPANS_VC_OPEN: + case SPANS_VC_R_POPEN: + case SPANS_VC_POPEN: + /* + * VCC is open or opening--continue + */ + break; + case SPANS_VC_CLOSE: + case SPANS_VC_FREE: + case SPANS_VC_ABORT: + /* + * We're already closing--give a response, since this + * is probably a retransmission + */ + result = SPANS_OK; + goto response; + case SPANS_VC_NULL: + result = SPANS_FAIL; + goto response; + } + + /* + * Cancel the retransmission timer + */ + SPANS_VC_CANCEL((struct vccb *) svp); + + /* + * Close out the VCCB and notify the user + */ + outstate = svp->sv_sstate; + svp->sv_ustate = VCCU_CLOSED; + svp->sv_sstate = SPANS_VC_FREE; + cvp = svp->sv_connvc; + switch (outstate) { + case SPANS_VC_R_POPEN: + spans_free((struct vccb *)svp); + /* FALLTHRU */ + + case SPANS_VC_POPEN: + case SPANS_VC_OPEN: + cvp->cvc_attr.cause.tag = T_ATM_PRESENT; + cvp->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + cvp->cvc_attr.cause.v.location = T_ATM_LOC_USER; + cvp->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_NORMAL_CALL_CLEARING; + KM_ZERO(cvp->cvc_attr.cause.v.diagnostics, + sizeof(cvp->cvc_attr.cause.v.diagnostics)); + atm_cm_cleared(svp->sv_connvc); + break; + } + + result = SPANS_OK; + +response: + /* + * Respond to the SPANS_CLOSE_IND with a SPANS_CLOSE_RSP + */ + rsp_msg = (spans_msg *)atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + rsp_msg->sm_vers = SPANS_VERS_1_0; + if (msg->sm_type == SPANS_RCLOSE_REQ || + msg->sm_type == SPANS_RCLOSE_IND) { + rsp_msg->sm_type = SPANS_RCLOSE_RSP; + } else { + rsp_msg->sm_type = SPANS_CLOSE_RSP; + } + rsp_msg->sm_close_rsp.clrsp_conn = msg->sm_close_req.clreq_conn; + rsp_msg->sm_close_rsp.clrsp_result = result; + spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process a close response or close confirmation + * + * Called when an close response or close confirmation is received. + * Processing will be based on the state of the requested connection and + * the returned status. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the close response or confirmation message + * + * Returns: + * none + * + */ +static void +spans_close_rsp(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + struct spans_vccb *svp; + + ATM_DEBUG2("spans_close_rsp: spp=0x%x, msg=0x%x\n", spp, msg); + + /* + * Locate the VCCB for the connection + */ + svp = spans_find_conn(spp, &msg->sm_close_rsp.clrsp_conn); + if (svp == NULL) { + return; + } + + /* + * Check the VCCB state + */ + if (svp->sv_sstate != SPANS_VC_CLOSE) { + return; + } + + /* + * Cancel the retransmission timer + */ + SPANS_VC_CANCEL((struct vccb *) svp); + + /* + * Check the response from the remote end + */ + switch (msg->sm_close_rsp.clrsp_result) { + + case SPANS_OK: + /* + * Mark the VCCB as closed and notify the owner + */ + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT; + svp->sv_connvc->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + svp->sv_connvc->cvc_attr.cause.v.location = + T_ATM_LOC_USER; + svp->sv_connvc->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_NORMAL_CALL_CLEARING; + KM_ZERO(svp->sv_connvc->cvc_attr.cause.v.diagnostics, + sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics)); + atm_cm_cleared(svp->sv_connvc); + break; + + case SPANS_NOVPVC: + case SPANS_BADDEST: + case SPANS_FAIL: + case SPANS_NORSC: + /* + * Mark the VCCB as closed and notify the owner + */ + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT; + svp->sv_connvc->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + svp->sv_connvc->cvc_attr.cause.v.location = + T_ATM_LOC_USER; + svp->sv_connvc->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_UNSPECIFIED_NORMAL; + KM_ZERO(svp->sv_connvc->cvc_attr.cause.v.diagnostics, + sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics)); + atm_cm_cleared(svp->sv_connvc); + break; + + default: + log(LOG_ERR, "spans: unknown result %d in close rsp\n", + msg->sm_close_rsp.clrsp_result); + break; + } +} + + +/* + * Process a multi request or multi indication + * + * Called when a multi response or multi confirmation is received. We + * don't support multicast channels, so we just reject the request. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the multi request or indication message + * + * Returns: + * none + * + */ +static void +spans_multi_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + spans_msg *rsp_msg; + + /* + * Get memory for a SPANS_MULTI_RSP message. + */ + rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + + /* + * Fill out the response. + */ + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_MULTI_RSP; + rsp_msg->sm_multi_rsp.mursp_conn = msg->sm_multi_req.mureq_conn; + rsp_msg->sm_multi_rsp.mursp_result = SPANS_FAIL; + rsp_msg->sm_multi_rsp.mursp_rsrc = msg->sm_multi_req.mureq_desrsrc; + rsp_msg->sm_multi_rsp.mursp_vpvc = 0; + + /* + * Send the response and free the message. + */ + (void) spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process an add request or add indication + * + * Called when an add response or add confirmation is received. We + * don't support multicast channels, so we just reject the request. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the add request or indication message + * + * Returns: + * none + * + */ +static void +spans_add_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + spans_msg *rsp_msg; + + /* + * Get memory for a SPANS_ADD_RSP message. + */ + rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + + /* + * Fill out the response. + */ + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_ADD_RSP; + rsp_msg->sm_add_rsp.adrsp_conn = msg->sm_add_req.adreq_desconn; + rsp_msg->sm_add_rsp.adrsp_result = SPANS_FAIL; + rsp_msg->sm_add_rsp.adrsp_rsrc.rsc_peak = 0; + rsp_msg->sm_add_rsp.adrsp_rsrc.rsc_mean = 0; + rsp_msg->sm_add_rsp.adrsp_rsrc.rsc_burst = 0; + + /* + * Send the response and free the message. + */ + (void) spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process a join request + * + * Called when an join request is received. We don't support group + * addresses, so we just reject the request. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the join request message + * + * Returns: + * none + * + */ +static void +spans_join_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + spans_msg *rsp_msg; + + /* + * Get memory for a SPANS_JOIN_CNF message. + */ + rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + + /* + * Fill out the response. + */ + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_JOIN_CNF; + spans_addr_copy(&msg->sm_join_req.jnreq_addr, + &rsp_msg->sm_join_cnf.jncnf_addr); + rsp_msg->sm_join_cnf.jncnf_result = SPANS_FAIL; + + /* + * Send the response and free the message. + */ + (void) spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process a leave request + * + * Called when an leave request is received. We don't support group + * addresses, so we just reject the request. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the leave request message + * + * Returns: + * none + * + */ +static void +spans_leave_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + spans_msg *rsp_msg; + + /* + * Get memory for a SPANS_LEAVE_CNF message. + */ + rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + + /* + * Fill out the response. + */ + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_LEAVE_CNF; + spans_addr_copy(&msg->sm_leave_req.lvreq_addr, + &rsp_msg->sm_leave_cnf.lvcnf_addr); + rsp_msg->sm_leave_cnf.lvcnf_result = SPANS_FAIL; + + /* + * Send the response and free the message. + */ + (void) spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process a VCI range indication + * + * Called when a VCI range indication is received. Adjust the VCI + * bounds if they have changed. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the VCI range indication message + * + * Returns: + * none + * + */ +static void +spans_vcir_ind(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + /* + * Adjust the limits if they have changed + */ + if (msg->sm_vcir_ind.vrind_min != spp->sp_min_vci) { + spp->sp_min_vci = + (msg->sm_vcir_ind.vrind_min < + SPANS_MIN_VCI ? + SPANS_MIN_VCI : + msg->sm_vcir_ind.vrind_min); + } + if (msg->sm_vcir_ind.vrind_max != spp->sp_max_vci) { + spp->sp_max_vci = + (msg->sm_vcir_ind.vrind_max > + SPANS_MAX_VCI ? + SPANS_MAX_VCI : + msg->sm_vcir_ind.vrind_max); + } +} + + +/* + * Process a query request + * + * Called when a query request is received. Respond with the + * appropriate query response. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * msg pointer to the VCI range indication message + * + * Returns: + * none + * + */ +static void +spans_query_req(spp, msg) + struct spans *spp; + spans_msg *msg; +{ + struct spans_vccb *svp = NULL; + spans_msg *rsp_msg; + + ATM_DEBUG1("spans_query_req: msg=0x%x\n", msg); + + /* + * Ignore an end-to-end query + */ + if (msg->sm_query_req.qyreq_type == SPANS_QUERY_END_TO_END) { + return; + } + + /* + * Get memory for a SPANS_QUERY_RSP message. + */ + rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool); + if (rsp_msg == NULL) + return; + + /* + * Fill out the response. + */ + rsp_msg->sm_vers = SPANS_VERS_1_0; + rsp_msg->sm_type = SPANS_QUERY_RSP; + rsp_msg->sm_query_rsp.qyrsp_conn = msg->sm_query_req.qyreq_conn; + rsp_msg->sm_query_rsp.qyrsp_type = msg->sm_query_req.qyreq_type; + rsp_msg->sm_query_rsp.qyrsp_data = 0; + + /* + * Get the state of the requested connection + */ + svp = spans_find_conn(spp, &msg->sm_query_req.qyreq_conn); + if (svp) { + switch(svp->sv_sstate) { + case SPANS_VC_NULL: + case SPANS_VC_FREE: + rsp_msg->sm_query_rsp.qyrsp_state = + SPANS_CONN_CLOSED; + break; + case SPANS_VC_OPEN: + rsp_msg->sm_query_rsp.qyrsp_state = + SPANS_CONN_OPEN; + break; + case SPANS_VC_POPEN: + case SPANS_VC_R_POPEN: + rsp_msg->sm_query_rsp.qyrsp_state = + SPANS_CONN_OPEN_PEND; + break; + case SPANS_VC_CLOSE: + case SPANS_VC_ABORT: + rsp_msg->sm_query_rsp.qyrsp_state = + SPANS_CONN_CLOSE_PEND; + break; + case SPANS_VC_ACTIVE: + case SPANS_VC_ACT_DOWN: + /* + * VCCB is for a PVC (shouldn't happen) + */ + atm_free(rsp_msg); + return; + } + } else { + /* + * No VCCB found--connection doesn't exist + */ + rsp_msg->sm_query_rsp.qyrsp_state = SPANS_CONN_CLOSED; + } + + /* + * Send the response and free the message. + */ + (void) spans_send_msg(spp, rsp_msg); + atm_free(rsp_msg); +} + + +/* + * Process a SPANS signalling message + * + * Called when a SPANS message is received. The message is converted + * into internal format with XDR and decoded by calling the appropriate + * mesage handling routine. Unrecognized and unexpected messages are + * logged. + * + * Arguments: + * spp pointer to SPANS protocol instance block + * m pointer to a buffer chain containing the SPANS message + * + * Returns: + * none + * + */ +void +spans_rcv_msg(spp, m) + struct spans *spp; + KBuffer *m; +{ + XDR xdrs; + spans_msg *msg; + + /* + * Get storage for the message + */ + msg = (spans_msg *)atm_allocate(&spans_msgpool); + if (msg == NULL) { + return; + } + + /* + * Convert the message from network order to internal format + */ + xdrmbuf_init(&xdrs, m, XDR_DECODE); + if (!xdr_spans_msg(&xdrs, msg)) { + log(LOG_ERR, "spans_rcv_msg: XDR decode failed\n"); + spans_dump_buffer(m); + goto done; + } + +#ifdef NOTDEF + /* + * Debug--print some information about the message + */ + if (msg->sm_type != SPANS_STAT_REQ && + msg->sm_type != SPANS_STAT_IND && + msg->sm_type != SPANS_STAT_RSP) { + printf("spans_rcv_msg: got "); + spans_print_msg(msg); + } +#endif + + /* + * Verify the message sm_vers + */ + if (msg->sm_vers != SPANS_VERS_1_0) { + log(LOG_ERR, "spans: invalid message version 0x%x\n", + msg->sm_vers); + } + + /* + * Ignore the message if SPANS isn't up yet + */ + if (spp->sp_state != SPANS_ACTIVE && + (spp->sp_state != SPANS_PROBE || + (msg->sm_type != SPANS_STAT_REQ && + msg->sm_type != SPANS_STAT_RSP && + msg->sm_type != SPANS_STAT_IND))) { + goto done; + } + + /* + * Process the message based on its type + */ + switch(msg->sm_type) { + case SPANS_STAT_REQ: + spans_status_ind(spp, msg); + break; + case SPANS_STAT_IND: + spans_status_ind(spp, msg); + break; + case SPANS_STAT_RSP: + spans_status_rsp(spp, msg); + break; + case SPANS_OPEN_REQ: + spans_open_req(spp, msg); + break; + case SPANS_OPEN_IND: + spans_open_req(spp, msg); + break; + case SPANS_OPEN_RSP: + spans_open_rsp(spp, msg); + break; + case SPANS_OPEN_CNF: + spans_open_rsp(spp, msg); + break; + case SPANS_CLOSE_REQ: + spans_close_req(spp, msg); + break; + case SPANS_CLOSE_IND: + spans_close_req(spp, msg); + break; + case SPANS_CLOSE_RSP: + spans_close_rsp(spp, msg); + break; + case SPANS_CLOSE_CNF: + spans_close_rsp(spp, msg); + break; + case SPANS_RCLOSE_REQ: + spans_close_req(spp, msg); + break; + case SPANS_RCLOSE_IND: + spans_close_req(spp, msg); + break; + case SPANS_RCLOSE_RSP: + spans_close_rsp(spp, msg); + break; + case SPANS_RCLOSE_CNF: + spans_close_rsp(spp, msg); + break; + case SPANS_MULTI_REQ: + spans_multi_req(spp, msg); + break; + case SPANS_MULTI_IND: + spans_multi_req(spp, msg); + break; + case SPANS_MULTI_RSP: + log(LOG_ERR, + "spans: unexpected message (multi_rsp)\n"); + break; + case SPANS_MULTI_CNF: + log(LOG_ERR, + "spans: unexpected message (multi_conf)\n"); + break; + case SPANS_ADD_REQ: + spans_add_req(spp, msg); + break; + case SPANS_ADD_IND: + spans_add_req(spp, msg); + break; + case SPANS_ADD_RSP: + log(LOG_ERR, + "spans: unexpected message (add_rsp)\n"); + break; + case SPANS_ADD_CNF: + log(LOG_ERR, "spans: unexpected message (add_conf)\n"); + break; + case SPANS_JOIN_REQ: + spans_join_req(spp, msg); + break; + case SPANS_JOIN_CNF: + log(LOG_ERR, "spans: unexpected message (join_conf)\n"); + break; + case SPANS_LEAVE_REQ: + spans_leave_req(spp, msg); + break; + case SPANS_LEAVE_CNF: + log(LOG_ERR, + "spans: unexpected message (leave_conf)\n"); + break; + case SPANS_VCIR_IND: + spans_vcir_ind(spp, msg); + break; + case SPANS_QUERY_REQ: + spans_query_req(spp, msg); + break; + case SPANS_QUERY_RSP: + log(LOG_ERR, + "spans: unexpected message (query_rsp)\n"); + break; + default: + log(LOG_ERR, "spans: unknown SPANS message type %d\n", + msg->sm_type); + } + +done: + /* + * Free the incoming message (both buffer and internal format) if + * necessary. + */ + if (msg) + atm_free(msg); + if (m) + KB_FREEALL(m); +} diff --git a/sys/netatm/spans/spans_print.c b/sys/netatm/spans/spans_print.c new file mode 100644 index 0000000..60dc670 --- /dev/null +++ b/sys/netatm/spans/spans_print.c @@ -0,0 +1,1062 @@ +/* + * + * =================================== + * 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: spans_print.c,v 1.4 1997/05/06 22:17:11 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS Print Routines. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_print.c,v 1.4 1997/05/06 22:17:11 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> + +/* + * If LONGPRINT is defined, every field of the SPANS message will be + * printed. If not, a shorter summary (useful for debugging without + * swamping the console) is printed. + */ +/* #define LONGPRINT */ + +/* + * Local functions + */ +static void inc_indent __P((void)); +static void dec_indent __P((void)); +static void spans_aal_str __P((spans_aal *, char *)); +static void spans_result_str __P((spans_result *, char *)); +static void spans_msgtype_str __P((spans_msgtype *, char *)); +static void spans_query_type_str __P((spans_query_type *, char *)); +static void spans_state_str __P((spans_query_type *, char *)); +static void spans_print_version __P((spans_version *)); +static void spans_print_vpvc __P((spans_vpvc *)); +static void spans_print_vpvc_pref __P((spans_vpvc_pref *)); +static void spans_print_addr __P((spans_addr *)); +static void spans_print_sap __P((spans_sap *)); +static void spans_print_atm_conn __P((spans_atm_conn *)); +static void spans_print_resrc __P((spans_resrc *)); +static void spans_print_aal __P((spans_aal *)); +static void spans_print_result __P((spans_result *)); +static void spans_print_msgtype __P((spans_msgtype *)); +static void spans_print_parm_stat_req __P((spans_parm_stat_req *)); +static void spans_print_parm_stat_ind __P((spans_parm_stat_ind *)); +static void spans_print_parm_stat_rsp __P((spans_parm_stat_rsp *)); +static void spans_print_parm_open_req __P((spans_parm_open_req *)); +static void spans_print_parm_open_ind __P((spans_parm_open_ind *)); +static void spans_print_parm_open_rsp __P((spans_parm_open_rsp *)); +static void spans_print_parm_open_cnf __P((spans_parm_open_cnf *)); +static void spans_print_parm_close_req __P((spans_parm_close_req *)); +static void spans_print_parm_close_ind __P((spans_parm_close_ind *)); +static void spans_print_parm_close_rsp __P((spans_parm_close_rsp *)); +static void spans_print_parm_close_cnf __P((spans_parm_close_cnf *)); +static void spans_print_parm_rclose_req __P((spans_parm_rclose_req *)); +static void spans_print_parm_rclose_ind __P((spans_parm_rclose_ind *)); +static void spans_print_parm_rclose_rsp __P((spans_parm_rclose_rsp *)); +static void spans_print_parm_rclose_cnf __P((spans_parm_rclose_cnf *)); +static void spans_print_parm_multi_req __P((spans_parm_multi_req *)); +static void spans_print_parm_multi_ind __P((spans_parm_multi_ind *)); +static void spans_print_parm_multi_rsp __P((spans_parm_multi_rsp *)); +static void spans_print_parm_multi_cnf __P((spans_parm_multi_cnf *)); +static void spans_print_parm_add_req __P((spans_parm_add_req *)); +static void spans_print_parm_add_ind __P((spans_parm_add_ind *)); +static void spans_print_parm_add_rsp __P((spans_parm_add_rsp *)); +static void spans_print_parm_add_cnf __P((spans_parm_add_cnf *)); +static void spans_print_parm_join_req __P((spans_parm_join_req *)); +static void spans_print_parm_join_cnf __P((spans_parm_join_cnf *)); +static void spans_print_parm_leave_req __P((spans_parm_leave_req *)); +static void spans_print_parm_leave_cnf __P((spans_parm_leave_cnf *)); +static void spans_print_parm_vcir_ind __P((spans_parm_vcir_ind *)); +static void spans_print_parm_query_req __P((spans_parm_query_req *)); +static void spans_print_parm_query_rsp __P((spans_parm_query_rsp *)); +static void spans_print_msgbody __P((spans_msgbody *)); + + +/* + * Local variables + */ +#define MAX_INDENT 10 +#define INIT_INDENT &indent_str[MAX_INDENT] +static char *spans_indent; +static char indent_str[11] = " "; + +static void +inc_indent() +{ + if (spans_indent != &indent_str[0]) { + *spans_indent--; + } +} + +static void +dec_indent() +{ + if (spans_indent != INIT_INDENT) { + *spans_indent++; + } +} + +static void +spans_aal_str(objp, dest) + spans_aal *objp; + char *dest; +{ + static char *aal_names[] = { + "SPANS_AAL0", + "SPANS_AAL1", + "SPANS_AAL2", + "SPANS_AAL3", + "SPANS_AAL4", + "SPANS_AAL5" + }; + + if (*objp < SPANS_AAL0 || *objp > SPANS_AAL5) { + sprintf(dest, "Invalid (%d)", (int)*objp); + } else { + sprintf(dest, "%s (%d)", aal_names[(int)*objp], + (int)*objp); + } +} + +static void +spans_result_str(objp, dest) + spans_result *objp; + char *dest; +{ + static char *result_names[] = { + "SPANS_OK", + "SPANS_FAIL", + "SPANS_NOVPVC", + "SPANS_NORSC", + "SPANS_BADDEST" + }; + + if (*objp < SPANS_OK || *objp > SPANS_BADDEST) { + sprintf(dest, "Invalid (%d)", (int)*objp); + } else { + sprintf(dest, "%s (%d)", + result_names[(int)*objp], (int)*objp); + } +} + +static void +spans_msgtype_str(objp, dest) + spans_msgtype *objp; + char *dest; +{ + int i; + + static struct { + spans_msgtype type; + char *name; + } msgtype_names[] = { + { SPANS_STAT_REQ, "SPANS_STAT_REQ" }, + { SPANS_STAT_IND, "SPANS_STAT_IND" }, + { SPANS_STAT_RSP, "SPANS_STAT_RSP" }, + { SPANS_OPEN_REQ, "SPANS_OPEN_REQ" }, + { SPANS_OPEN_IND, "SPANS_OPEN_IND" }, + { SPANS_OPEN_RSP, "SPANS_OPEN_RSP" }, + { SPANS_OPEN_CNF, "SPANS_OPEN_CNF" }, + { SPANS_CLOSE_REQ, "SPANS_CLOSE_REQ" }, + { SPANS_CLOSE_IND, "SPANS_CLOSE_IND" }, + { SPANS_CLOSE_RSP, "SPANS_CLOSE_RSP" }, + { SPANS_CLOSE_CNF, "SPANS_CLOSE_CNF" }, + { SPANS_RCLOSE_REQ, "SPANS_RCLOSE_REQ" }, + { SPANS_RCLOSE_IND, "SPANS_RCLOSE_IND" }, + { SPANS_RCLOSE_RSP, "SPANS_RCLOSE_RSP" }, + { SPANS_RCLOSE_CNF, "SPANS_RCLOSE_CNF" }, + { SPANS_MULTI_REQ, "SPANS_MULTI_REQ" }, + { SPANS_MULTI_IND, "SPANS_MULTI_IND" }, + { SPANS_MULTI_RSP, "SPANS_MULTI_RSP" }, + { SPANS_MULTI_CNF, "SPANS_MULTI_CNF" }, + { SPANS_ADD_REQ, "SPANS_ADD_REQ" }, + { SPANS_ADD_IND, "SPANS_ADD_IND" }, + { SPANS_ADD_RSP, "SPANS_ADD_RSP" }, + { SPANS_ADD_CNF, "SPANS_ADD_CNF" }, + { SPANS_JOIN_REQ, "SPANS_JOIN_REQ" }, + { SPANS_JOIN_CNF, "SPANS_JOIN_CNF" }, + { SPANS_LEAVE_REQ, "SPANS_LEAVE_REQ" }, + { SPANS_LEAVE_CNF, "SPANS_LEAVE_CNF" }, + { SPANS_VCIR_IND, "SPANS_VCIR_IND" }, + { SPANS_QUERY_REQ, "SPANS_QUERY_REQ" }, + { SPANS_QUERY_RSP, "SPANS_QUERY_RSP" }, + { 0, (char *) 0 } + }; + + /* + * Search the name table for the specified type + */ + for (i=0; msgtype_names[i].name; i++) { + if (*objp == msgtype_names[i].type) { + sprintf(dest, "%s (%d)", + msgtype_names[i].name, + (int)*objp); + return; + } + } + + /* + * Type was not found--return an error indicator + */ + sprintf(dest, "Invalid (%d)", (int)*objp); +} + +static void +spans_query_type_str(objp, dest) + spans_query_type *objp; + char *dest; +{ + static char *query_names[] = { + "SPANS_QUERY_NORMAL", + "SPANS_QUERY_DEBUG", + "SPANS_QUERY_END_TO_END" + }; + + if (*objp < SPANS_QUERY_NORMAL || + *objp > SPANS_QUERY_END_TO_END) { + sprintf(dest, "Invalid (%d)", (int)*objp); + } else { + sprintf(dest, "%s (%d)", query_names[(int)*objp], + (int)*objp); + } +} + +static void +spans_state_str(objp, dest) + spans_query_type *objp; + char *dest; +{ + static char *state_names[] = { + "SPANS_CONN_OPEN", + "SPANS_CONN_OPEN_PEND", + "SPANS_CONN_CLOSE_PEND", + "SPANS_CONN_CLOSED" + }; + + if (*objp < SPANS_CONN_OPEN || *objp > SPANS_CONN_CLOSED) { + sprintf(dest, "Invalid (%d)", (int)*objp); + } else { + sprintf(dest, "%s (%d)", state_names[(int)*objp], + (int)*objp); + } +} + +#ifdef LONGPRINT + +static void +spans_print_version(objp) + spans_version *objp; +{ + printf("%sspans_version 0x%x\n", spans_indent, *objp); +} + +static void +spans_print_vpvc(objp) + spans_vpvc *objp; +{ + printf("%sVP/VC %d/%d\n", spans_indent, + SPANS_EXTRACT_VPI(*objp), + SPANS_EXTRACT_VCI(*objp)); +} + +static void +spans_print_vpvc_pref(objp) + spans_vpvc_pref *objp; +{ + printf("%sspans_vpvc_pref\n", spans_indent); + inc_indent(); + printf("%s%s\n", spans_indent, + (objp->vpf_valid ? "Valid" : "Not valid")); + spans_print_vpvc(&objp->vpf_vpvc); + dec_indent(); +} + +static void +spans_print_addr(objp) + spans_addr *objp; +{ + char addr_str[80]; + + strncpy(addr_str, spans_addr_print(objp), sizeof(addr_str)); + printf("%sspans_addr %s\n", spans_indent, addr_str); +} + +static void +spans_print_sap(objp) + spans_sap *objp; +{ + printf("%sSAP %d\n", spans_indent, *objp); +} + +static void +spans_print_atm_conn(objp) + spans_atm_conn *objp; +{ + printf("%sspans_atm_conn\n", spans_indent); + inc_indent(); + spans_print_addr(&objp->con_dst); + spans_print_addr(&objp->con_src); + spans_print_sap(&objp->con_dsap); + spans_print_sap(&objp->con_ssap); + dec_indent(); +} + +static void +spans_print_resrc(objp) + spans_resrc *objp; +{ + printf("%sspans_resrc\n", spans_indent); + inc_indent(); + printf("%srsc_peak %d\n", spans_indent, objp->rsc_peak); + printf("%srsc_mean %d\n", spans_indent, objp->rsc_mean); + printf("%srsc_burst %d\n", spans_indent, objp->rsc_burst); + dec_indent(); +} + +static void +spans_print_aal(objp) + spans_aal *objp; +{ + char aal_str[80]; + + spans_aal_str(objp, aal_str); + printf("%sspans_aal %s\n", spans_indent, aal_str); +} + +static void +spans_print_result(objp) + spans_result *objp; +{ + char result_str[80]; + + spans_result_str(objp, result_str); + printf("%sspans_result %s\n", spans_indent, result_str); +} + +static void +spans_print_msgtype(objp) + spans_msgtype *objp; +{ + char msgtype_str[80]; + + spans_msgtype_str(objp, msgtype_str); + printf("%sspans_msgtype %s\n", spans_indent, msgtype_str); +} + +static void +spans_print_parm_stat_req(objp) + spans_parm_stat_req *objp; +{ + printf("%sspans_parm_stat_req\n", spans_indent); + inc_indent(); + printf("%sstreq_es_epoch %d\n", spans_indent, + objp->streq_es_epoch); + dec_indent(); +} + +static void +spans_print_parm_stat_ind(objp) + spans_parm_stat_ind *objp; +{ + printf("%sspans_parm_stat_ind\n", spans_indent); + inc_indent(); + printf("%sstind_sw_epoch %d\n", spans_indent, + objp->stind_sw_epoch); + spans_print_addr(&objp->stind_es_addr); + spans_print_addr(&objp->stind_sw_addr); + dec_indent(); +} + +static void +spans_print_parm_stat_rsp(objp) + spans_parm_stat_rsp *objp; +{ + printf("%sspans_parm_stat_rsp\n", spans_indent); + inc_indent(); + printf("%sstrsp_es_epoch %d\n", spans_indent, + objp->strsp_es_epoch); + spans_print_addr(&objp->strsp_es_addr); + dec_indent(); +} + +static void +spans_print_parm_open_req(objp) + spans_parm_open_req *objp; +{ + printf("%sspans_parm_open_req\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->opreq_conn); + spans_print_aal(&objp->opreq_aal); + spans_print_resrc(&objp->opreq_desrsrc); + spans_print_resrc(&objp->opreq_minrsrc); + spans_print_vpvc_pref(&objp->opreq_vpvc); + dec_indent(); +} + +static void +spans_print_parm_open_ind(objp) + spans_parm_open_ind *objp; +{ + printf("%sspans_parm_open_ind\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->opind_conn); + spans_print_aal(&objp->opind_aal); + spans_print_resrc(&objp->opind_desrsrc); + spans_print_resrc(&objp->opind_minrsrc); + spans_print_vpvc_pref(&objp->opind_vpvc); + dec_indent(); +} + +static void +spans_print_parm_open_rsp(objp) + spans_parm_open_rsp *objp; +{ + printf("%sspans_parm_open_rsp\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->oprsp_conn); + spans_print_result(&objp->oprsp_result); + spans_print_resrc(&objp->oprsp_rsrc); + spans_print_vpvc(&objp->oprsp_vpvc); + dec_indent(); +} + +static void +spans_print_parm_open_cnf(objp) + spans_parm_open_cnf *objp; +{ + printf("%sspans_parm_open_cnf\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->opcnf_conn); + spans_print_result(&objp->opcnf_result); + spans_print_resrc(&objp->opcnf_rsrc); + spans_print_vpvc(&objp->opcnf_vpvc); + dec_indent(); +} + +static void +spans_print_parm_close_req(objp) + spans_parm_close_req *objp; +{ + printf("%sspans_parm_close_req\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->clreq_conn); + dec_indent(); +} + +static void +spans_print_parm_close_ind(objp) + spans_parm_close_ind *objp; +{ + printf("%sspans_parm_close_ind\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->clind_conn); + dec_indent(); +} + +static void +spans_print_parm_close_rsp(objp) + spans_parm_close_rsp *objp; +{ + printf("%sspans_parm_close_rsp\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->clrsp_conn); + spans_print_result(&objp->clrsp_result); + dec_indent(); +} + +static void +spans_print_parm_close_cnf(objp) + spans_parm_close_cnf *objp; +{ + printf("%sspans_parm_close_cnf\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->clcnf_conn); + spans_print_result(&objp->clcnf_result); + dec_indent(); +} + +static void +spans_print_parm_rclose_req(objp) + spans_parm_rclose_req *objp; +{ + printf("%sspans_parm_rclose_req\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->rcreq_conn); + dec_indent(); +} + +static void +spans_print_parm_rclose_ind(objp) + spans_parm_rclose_ind *objp; +{ + printf("%sspans_parm_rclose_ind\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->rcind_conn); + dec_indent(); +} + +static void +spans_print_parm_rclose_rsp(objp) + spans_parm_rclose_rsp *objp; +{ + printf("%sspans_parm_rclose_rsp\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->rcrsp_conn); + spans_print_result(&objp->rcrsp_result); + dec_indent(); +} + +static void +spans_print_parm_rclose_cnf(objp) + spans_parm_rclose_cnf *objp; +{ + printf("%sspans_parm_rclose_cnf\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->rccnf_conn); + spans_print_result(&objp->rccnf_result); + dec_indent(); +} + +static void +spans_print_parm_multi_req(objp) + spans_parm_multi_req *objp; +{ + printf("%sspans_parm_multi_req\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->mureq_conn); + spans_print_aal(&objp->mureq_aal); + spans_print_resrc(&objp->mureq_desrsrc); + spans_print_resrc(&objp->mureq_minrsrc); + spans_print_vpvc(&objp->mureq_vpvc); + dec_indent(); +} + +static void +spans_print_parm_multi_ind(objp) + spans_parm_multi_ind *objp; +{ + printf("%sspans_parm_multi_ind\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->muind_conn); + spans_print_aal(&objp->muind_aal); + spans_print_resrc(&objp->muind_desrsrc); + spans_print_resrc(&objp->muind_minrsrc); + spans_print_vpvc(&objp->muind_vpvc); + dec_indent(); +} + +static void +spans_print_parm_multi_rsp(objp) + spans_parm_multi_rsp *objp; +{ + printf("%sspans_parm_multi_rsp\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->mursp_conn); + spans_print_result(&objp->mursp_result); + spans_print_resrc(&objp->mursp_rsrc); + spans_print_vpvc(&objp->mursp_vpvc); + dec_indent(); +} + +static void +spans_print_parm_multi_cnf(objp) + spans_parm_multi_cnf *objp; +{ + printf("%sspans_parm_multi_cnf\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->mucnf_conn); + spans_print_result(&objp->mucnf_result); + spans_print_resrc(&objp->mucnf_rsrc); + spans_print_vpvc(&objp->mucnf_vpvc); + dec_indent(); +} + +static void +spans_print_parm_add_req(objp) + spans_parm_add_req *objp; +{ + printf("%sspans_parm_add_req\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->adreq_desconn); + spans_print_atm_conn(&objp->adreq_xstconn); + dec_indent(); +} + +static void +spans_print_parm_add_ind(objp) + spans_parm_add_ind *objp; +{ + printf("%sspans_parm_add_ind\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->adind_desconn); + spans_print_atm_conn(&objp->adind_xstconn); + dec_indent(); +} + +static void +spans_print_parm_add_rsp(objp) + spans_parm_add_rsp *objp; +{ + printf("%sspans_parm_add_rsp\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->adrsp_conn); + spans_print_result(&objp->adrsp_result); + spans_print_resrc(&objp->adrsp_rsrc); + dec_indent(); +} + +static void +spans_print_parm_add_cnf(objp) + spans_parm_add_cnf *objp; +{ + printf("%sspans_parm_add_cnf\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->adcnf_conn); + spans_print_result(&objp->adcnf_result); + spans_print_resrc(&objp->adcnf_rsrc); + dec_indent(); +} + +static void +spans_print_parm_join_req(objp) + spans_parm_join_req *objp; +{ + printf("%sspans_parm_join_req\n", spans_indent); + inc_indent(); + spans_print_addr(&objp->jnreq_addr); + dec_indent(); +} + +static void +spans_print_parm_join_cnf(objp) + spans_parm_join_cnf *objp; +{ + printf("%sspans_print_parm_join_cnf\n", spans_indent); + inc_indent(); + spans_print_addr(&objp->jncnf_addr); + spans_print_result(&objp->jncnf_result); + dec_indent(); +} + +static void +spans_print_parm_leave_req(objp) + spans_parm_leave_req *objp; +{ + printf("%sspans_print_parm_leave_req\n", spans_indent); + inc_indent(); + spans_print_addr(&objp->lvreq_addr); + dec_indent(); +} + +static void +spans_print_parm_leave_cnf(objp) + spans_parm_leave_cnf *objp; +{ + printf("%sspans_parm_leave_cnf\n", spans_indent); + inc_indent(); + spans_print_addr(&objp->lvcnf_addr); + spans_print_result(&objp->lvcnf_result); + dec_indent(); +} + +static void +spans_print_parm_vcir_ind(objp) + spans_parm_vcir_ind *objp; +{ + printf("%sspans_parm_vcir_ind\n", spans_indent); + inc_indent(); + printf("%svrind_min %d\n", spans_indent, objp->vrind_min); + printf("%svrind_max %d\n", spans_indent, objp->vrind_max); + dec_indent(); +} + +static void +spans_print_parm_query_req(objp) + spans_parm_query_req *objp; +{ + char query_type_str[80]; + + printf("%sspans_parm_query_req\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->qyreq_conn); + spans_query_type_str(&objp->qyreq_type, query_type_str); + printf("%sqyreq_type %s\n", spans_indent, query_type_str); + dec_indent(); +} + +static void +spans_print_parm_query_rsp(objp) + spans_parm_query_rsp *objp; +{ + char query_type_str[80], state_type_str[80]; + + printf("%sspans_parm_query_rsp\n", spans_indent); + inc_indent(); + spans_print_atm_conn(&objp->qyrsp_conn); + spans_query_type_str(&objp->qyrsp_type, query_type_str); + printf("%sqyrsp_type %s\n", spans_indent, query_type_str); + spans_state_str(&objp->qyrsp_state, state_type_str); + printf("%sqyrsp_state %s\n", spans_indent, state_type_str); + printf("%sqyrsp_data 0x%x\n", spans_indent, + objp->qyrsp_data); + dec_indent(); +} + +static void +spans_print_msgbody(objp) + spans_msgbody *objp; +{ + printf("%sspans_msgbody\n", spans_indent); + inc_indent(); + spans_print_msgtype(&objp->mb_type); + switch (objp->mb_type) { + case SPANS_STAT_REQ: + spans_print_parm_stat_req(&objp->spans_msgbody_u.mb_stat_req); + break; + case SPANS_STAT_IND: + spans_print_parm_stat_ind(&objp->spans_msgbody_u.mb_stat_ind); + break; + case SPANS_STAT_RSP: + spans_print_parm_stat_rsp(&objp->spans_msgbody_u.mb_stat_rsp); + break; + case SPANS_OPEN_REQ: + spans_print_parm_open_req(&objp->spans_msgbody_u.mb_open_req); + break; + case SPANS_OPEN_IND: + spans_print_parm_open_ind(&objp->spans_msgbody_u.mb_open_ind); + break; + case SPANS_OPEN_RSP: + spans_print_parm_open_rsp(&objp->spans_msgbody_u.mb_open_rsp); + break; + case SPANS_OPEN_CNF: + spans_print_parm_open_cnf(&objp->spans_msgbody_u.mb_open_cnf); + break; + case SPANS_CLOSE_REQ: + spans_print_parm_close_req(&objp->spans_msgbody_u.mb_close_req); + break; + case SPANS_CLOSE_IND: + spans_print_parm_close_ind(&objp->spans_msgbody_u.mb_close_ind); + break; + case SPANS_CLOSE_RSP: + spans_print_parm_close_rsp(&objp->spans_msgbody_u.mb_close_rsp); + break; + case SPANS_CLOSE_CNF: + spans_print_parm_close_cnf(&objp->spans_msgbody_u.mb_close_cnf); + break; + case SPANS_RCLOSE_REQ: + spans_print_parm_rclose_req(&objp->spans_msgbody_u.mb_rclose_req); + break; + case SPANS_RCLOSE_IND: + spans_print_parm_rclose_ind(&objp->spans_msgbody_u.mb_rclose_ind); + break; + case SPANS_RCLOSE_RSP: + spans_print_parm_rclose_rsp(&objp->spans_msgbody_u.mb_rclose_rsp); + break; + case SPANS_RCLOSE_CNF: + spans_print_parm_rclose_cnf(&objp->spans_msgbody_u.mb_rclose_cnf); + break; + case SPANS_MULTI_REQ: + spans_print_parm_multi_req(&objp->spans_msgbody_u.mb_multi_req); + break; + case SPANS_MULTI_IND: + spans_print_parm_multi_ind(&objp->spans_msgbody_u.mb_multi_ind); + break; + case SPANS_MULTI_RSP: + spans_print_parm_multi_rsp(&objp->spans_msgbody_u.mb_multi_rsp); + break; + case SPANS_MULTI_CNF: + spans_print_parm_multi_cnf(&objp->spans_msgbody_u.mb_multi_cnf); + break; + case SPANS_ADD_REQ: + spans_print_parm_add_req(&objp->spans_msgbody_u.mb_add_req); + break; + case SPANS_ADD_IND: + spans_print_parm_add_ind(&objp->spans_msgbody_u.mb_add_ind); + break; + case SPANS_ADD_RSP: + spans_print_parm_add_rsp(&objp->spans_msgbody_u.mb_add_rsp); + break; + case SPANS_ADD_CNF: + spans_print_parm_add_cnf(&objp->spans_msgbody_u.mb_add_cnf); + break; + case SPANS_JOIN_REQ: + spans_print_parm_join_req(&objp->spans_msgbody_u.mb_join_req); + break; + case SPANS_JOIN_CNF: + spans_print_parm_join_cnf(&objp->spans_msgbody_u.mb_join_cnf); + break; + case SPANS_LEAVE_REQ: + spans_print_parm_leave_req(&objp->spans_msgbody_u.mb_leave_req); + break; + case SPANS_LEAVE_CNF: + spans_print_parm_leave_cnf(&objp->spans_msgbody_u.mb_leave_cnf); + break; + case SPANS_VCIR_IND: + spans_print_parm_vcir_ind(&objp->spans_msgbody_u.mb_vcir_ind); + break; + case SPANS_QUERY_REQ: + spans_print_parm_query_req(&objp->spans_msgbody_u.mb_query_req); + break; + case SPANS_QUERY_RSP: + spans_print_parm_query_rsp(&objp->spans_msgbody_u.mb_query_rsp); + break; + } + dec_indent(); +} + +void +spans_print_msg(objp) + spans_msg *objp; +{ + spans_indent = INIT_INDENT; + printf("%sspans_msg\n", spans_indent); + inc_indent(); + spans_print_version(&objp->sm_vers); + spans_print_msgbody(&objp->sm_body); + dec_indent(); +} + +#else /* ifdef LONGPRINT */ + +static void +spans_print_msgbody(objp) + spans_msgbody *objp; +{ + char daddr[80], msgtype_str[80], result_str[80], saddr[80]; + spans_parm_stat_req *streq_p; + spans_parm_stat_ind *stind_p; + spans_parm_stat_rsp *strsp_p; + spans_parm_open_req *opreq_p; + spans_parm_open_ind *opind_p; + spans_parm_open_rsp *oprsp_p; + spans_parm_open_cnf *opcnf_p; + spans_parm_close_req *clreq_p; + spans_parm_close_ind *clind_p; + spans_parm_close_rsp *clrsp_p; + spans_parm_close_cnf *clcnf_p; + spans_parm_rclose_req *rcreq_p; + spans_parm_rclose_ind *rcind_p; + spans_parm_rclose_rsp *rcrsp_p; + spans_parm_rclose_cnf *rccnf_p; + + spans_msgtype_str(&objp->mb_type, msgtype_str); + printf("%s: ", msgtype_str); + switch (objp->mb_type) { + case SPANS_STAT_REQ: + streq_p = &objp->spans_msgbody_u.mb_stat_req; + printf("es_epoch=0x%x", streq_p->streq_es_epoch); + break; + case SPANS_STAT_IND: + stind_p = &objp->spans_msgbody_u.mb_stat_ind; + strncpy(daddr, spans_addr_print(&stind_p->stind_es_addr), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&stind_p->stind_sw_addr), + sizeof(daddr)); + printf("sw_epoch=0x%x, es_addr=%s, sw_addr=0x%s", + stind_p->stind_sw_epoch, + daddr, saddr); + break; + case SPANS_STAT_RSP: + strsp_p = &objp->spans_msgbody_u.mb_stat_rsp; + strncpy(daddr, spans_addr_print(&strsp_p->strsp_es_addr), + sizeof(daddr)); + printf("es_epoch=0x%x, es_addr=%s", + strsp_p->strsp_es_epoch, daddr); + break; + case SPANS_OPEN_REQ: + opreq_p = &objp->spans_msgbody_u.mb_open_req; + strncpy(daddr, spans_addr_print(&opreq_p->opreq_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&opreq_p->opreq_conn.con_src), + sizeof(saddr)); + printf("daddr=%s, saddr=%s, dsap=%d, ssap=%d, aal=%d", + daddr, saddr, + opreq_p->opreq_conn.con_dsap, + opreq_p->opreq_conn.con_ssap, + opreq_p->opreq_aal); + if (opreq_p->opreq_vpvc.vpf_valid) + printf(", vp.vc=%d.%d", + SPANS_EXTRACT_VPI(opreq_p->opreq_vpvc.vpf_vpvc), + SPANS_EXTRACT_VCI(opreq_p->opreq_vpvc.vpf_vpvc)); + break; + case SPANS_OPEN_IND: + opind_p = &objp->spans_msgbody_u.mb_open_ind; + strncpy(daddr, spans_addr_print(&opind_p->opind_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&opind_p->opind_conn.con_src), + sizeof(saddr)); + printf("daddr=%s, saddr=%s, dsap=%d, ssap=%d, aal=%d", + daddr, saddr, + opind_p->opind_conn.con_dsap, + opind_p->opind_conn.con_ssap, + opind_p->opind_aal); + if (opind_p->opind_vpvc.vpf_valid) + printf(", vp.vc=%d.%d", + SPANS_EXTRACT_VPI(opind_p->opind_vpvc.vpf_vpvc), + SPANS_EXTRACT_VCI(opind_p->opind_vpvc.vpf_vpvc)); + break; + case SPANS_OPEN_RSP: + oprsp_p = &objp->spans_msgbody_u.mb_open_rsp; + strncpy(daddr, spans_addr_print(&oprsp_p->oprsp_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&oprsp_p->oprsp_conn.con_src), + sizeof(saddr)); + spans_result_str(&oprsp_p->oprsp_result, result_str); + printf("result=%s, daddr=%s, saddr=%s, dsap=%d, ssap=%d, vp.vc=%d.%d", + result_str, daddr, saddr, + oprsp_p->oprsp_conn.con_dsap, + oprsp_p->oprsp_conn.con_ssap, + SPANS_EXTRACT_VPI(oprsp_p->oprsp_vpvc), + SPANS_EXTRACT_VCI(oprsp_p->oprsp_vpvc)); + break; + case SPANS_OPEN_CNF: + opcnf_p = &objp->spans_msgbody_u.mb_open_cnf; + strncpy(daddr, spans_addr_print(&opcnf_p->opcnf_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&opcnf_p->opcnf_conn.con_src), + sizeof(saddr)); + spans_result_str(&opcnf_p->opcnf_result, result_str); + printf("result=%s, daddr=%s, saddr=%s, dsap=%d, ssap=%d, vp.vc=%d.%d", + result_str, daddr, saddr, + opcnf_p->opcnf_conn.con_dsap, + opcnf_p->opcnf_conn.con_ssap, + SPANS_EXTRACT_VPI(opcnf_p->opcnf_vpvc), + SPANS_EXTRACT_VCI(opcnf_p->opcnf_vpvc)); + break; + case SPANS_CLOSE_REQ: + clreq_p = &objp->spans_msgbody_u.mb_close_req; + strncpy(daddr, spans_addr_print(&clreq_p->clreq_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&clreq_p->clreq_conn.con_src), + sizeof(saddr)); + printf("daddr=%s, saddr=%s, dsap=%d, ssap=%d", + daddr, saddr, + clreq_p->clreq_conn.con_dsap, + clreq_p->clreq_conn.con_ssap); + break; + case SPANS_CLOSE_IND: + clind_p = &objp->spans_msgbody_u.mb_close_ind; + strncpy(daddr, spans_addr_print(&clind_p->clind_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&clind_p->clind_conn.con_src), + sizeof(saddr)); + printf("daddr=%s, saddr=%s, dsap=%d, ssap=%d", + daddr, saddr, + clind_p->clind_conn.con_dsap, + clind_p->clind_conn.con_ssap); + break; + case SPANS_CLOSE_RSP: + clrsp_p = &objp->spans_msgbody_u.mb_close_rsp; + strncpy(daddr, spans_addr_print(&clrsp_p->clrsp_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&clrsp_p->clrsp_conn.con_src), + sizeof(saddr)); + spans_result_str(&clrsp_p->clrsp_result, result_str); + printf("result=%s, daddr=%s, saddr=%s, dsap=%d, ssap=%d", + result_str, daddr, saddr, + clrsp_p->clrsp_conn.con_dsap, + clrsp_p->clrsp_conn.con_ssap); + break; + case SPANS_CLOSE_CNF: + clcnf_p = &objp->spans_msgbody_u.mb_close_cnf; + strncpy(daddr, spans_addr_print(&clcnf_p->clcnf_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&clcnf_p->clcnf_conn.con_src), + sizeof(saddr)); + spans_result_str(&clcnf_p->clcnf_result, result_str); + printf("result=%s, daddr=%s, saddr=%s, dsap=%d, ssap=%d", + result_str, daddr, saddr, + clcnf_p->clcnf_conn.con_dsap, + clcnf_p->clcnf_conn.con_ssap); + break; + case SPANS_RCLOSE_REQ: + rcreq_p = &objp->spans_msgbody_u.mb_rclose_req; + strncpy(daddr, spans_addr_print(&rcreq_p->rcreq_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&rcreq_p->rcreq_conn.con_src), + sizeof(saddr)); + printf("daddr=%s, saddr=%s, dsap=%d, ssap=%d", + daddr, saddr, + rcreq_p->rcreq_conn.con_dsap, + rcreq_p->rcreq_conn.con_ssap); + break; + case SPANS_RCLOSE_IND: + rcind_p = &objp->spans_msgbody_u.mb_rclose_ind; + strncpy(daddr, spans_addr_print(&rcind_p->rcind_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&rcind_p->rcind_conn.con_src), + sizeof(saddr)); + printf("daddr=%s, saddr=%s, dsap=%d, ssap=%d", + daddr, saddr, + rcind_p->rcind_conn.con_dsap, + rcind_p->rcind_conn.con_ssap); + break; + case SPANS_RCLOSE_RSP: + rcrsp_p = &objp->spans_msgbody_u.mb_rclose_rsp; + strncpy(daddr, spans_addr_print(&rcrsp_p->rcrsp_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&rcrsp_p->rcrsp_conn.con_src), + sizeof(saddr)); + spans_result_str(&rcrsp_p->rcrsp_result, result_str); + printf("result=%s, daddr=%s, saddr=%s, dsap=%d, ssap=%d", + result_str, daddr, saddr, + rcrsp_p->rcrsp_conn.con_dsap, + rcrsp_p->rcrsp_conn.con_ssap); + break; + case SPANS_RCLOSE_CNF: + rccnf_p = &objp->spans_msgbody_u.mb_rclose_cnf; + strncpy(daddr, spans_addr_print(&rccnf_p->rccnf_conn.con_dst), + sizeof(daddr)); + strncpy(saddr, spans_addr_print(&rccnf_p->rccnf_conn.con_src), + sizeof(saddr)); + spans_result_str(&rccnf_p->rccnf_result, result_str); + printf("result=%s, daddr=%s, saddr=%s, dsap=%d, ssap=%d", + result_str, daddr, saddr, + rccnf_p->rccnf_conn.con_dsap, + rccnf_p->rccnf_conn.con_ssap); + break; + } + printf("\n"); +} + +void +spans_print_msg(objp) + spans_msg *objp; +{ + spans_indent = INIT_INDENT; + spans_print_msgbody(&objp->sm_body); +} + +#endif /* ifdef LONGPRINT */ diff --git a/sys/netatm/spans/spans_proto.c b/sys/netatm/spans/spans_proto.c new file mode 100644 index 0000000..e298ed8 --- /dev/null +++ b/sys/netatm/spans/spans_proto.c @@ -0,0 +1,558 @@ +/* + * + * =================================== + * 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: spans_proto.c,v 1.7 1998/08/26 23:29:10 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS protocol processing module. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_proto.c,v 1.7 1998/08/26 23:29:10 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> + +/* + * Internal functions + */ +caddr_t spans_getname __P((void *)); +void spans_connected __P((void *)); +void spans_cleared __P((void *, struct t_atm_cause *)); +void spans_cpcs_data __P((void *, KBuffer *)); + + +/* + * ATM endpoint for SPANS signalling channel + */ +static Atm_endpoint spans_endpt = { + NULL, /* ep_next */ + ENDPT_SPANS_SIG, /* ep_id */ + NULL, /* ep_ioctl */ + spans_getname, /* ep_getname */ + spans_connected, /* ep_connected */ + spans_cleared, /* ep_cleared */ + NULL, /* ep_incoming */ + NULL, /* ep_addparty */ + NULL, /* ep_dropparty */ + NULL, /* ep_cpcs_ctl */ + spans_cpcs_data, /* ep_cpcs_data */ + NULL, /* ep_saal_ctl */ + NULL, /* ep_saal_data */ + NULL, /* ep_sscop_ctl */ + NULL /* ep_sscop_data */ +}; + + +/* + * ATM connection attributes for UNI signalling channel + */ +static Atm_attributes spans_attr = { + NULL, /* nif */ + CMAPI_CPCS, /* api */ + 0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, /* aal.tag */ + ATM_AAL3_4 /* aal.aal_type */ + }, + { /* traffic */ + T_ATM_PRESENT, /* traffic.tag */ + { /* traffic.v */ + { /* traffic.v.forward */ + T_ATM_ABSENT, /* PCR_high */ + 0, /* PCR_all */ + T_ATM_ABSENT, /* SCR_high */ + T_ATM_ABSENT, /* SCR_all */ + T_ATM_ABSENT, /* MBS_high */ + T_ATM_ABSENT, /* MBS_all */ + T_NO, /* tagging */ + }, + { /* traffic.v.backward */ + T_ATM_ABSENT, /* PCR_high */ + 0, /* PCR_all */ + T_ATM_ABSENT, /* SCR_high */ + T_ATM_ABSENT, /* SCR_all */ + T_ATM_ABSENT, /* MBS_high */ + T_ATM_ABSENT, /* MBS_all */ + T_NO, /* tagging */ + }, + T_YES, /* best_effort */ + } + }, + { /* bearer */ + T_ATM_PRESENT, /* bearer.tag */ + { /* bearer.v */ + T_ATM_CLASS_X, /* class */ + T_ATM_NULL, /* traffic_type */ + T_ATM_NO_END_TO_END, /* timing_req */ + T_NO, /* clipping */ + T_ATM_1_TO_1, /* conn_conf */ + } + }, + { /* bhli */ + T_ATM_ABSENT, /* bhli.tag */ + }, + { /* blli */ + T_ATM_ABSENT, /* blli.tag_l2 */ + T_ATM_ABSENT, /* blli.tag_l3 */ + }, + { /* llc */ + T_ATM_ABSENT, /* llc.tag */ + }, + { /* called */ + T_ATM_PRESENT, /* called.tag */ + }, + { /* calling */ + T_ATM_ABSENT, /* calling.tag */ + }, + { /* qos */ + T_ATM_PRESENT, /* qos.tag */ + { /* qos.v */ + T_ATM_NETWORK_CODING, /* coding_standard */ + { /* qos.v.forward */ + T_ATM_QOS_CLASS_0, /* class */ + }, + { /* qos.v.backward */ + T_ATM_QOS_CLASS_0, /* class */ + } + } + }, + { /* transit */ + T_ATM_ABSENT, /* transit.tag */ + }, + { /* cause */ + T_ATM_ABSENT, /* cause.tag */ + } +}; + + +/* + * SPANS cause structre + */ +struct t_atm_cause spans_cause = { + T_ATM_ITU_CODING, /* coding_standard */ + T_ATM_LOC_USER, /* location */ + T_ATM_CAUSE_UNSPECIFIED_NORMAL, /* cause_value */ + { 0, 0, 0, 0 } /* diagnostics */ +}; + + +/* + * Process a SPANS timeout + * + * Called when a previously scheduled spans control block timer expires. + * Processing will based on the current SPANS state. + * + * Called at splnet. + * + * Arguments: + * tip pointer to spans timer control block + * + * Returns: + * none + * + */ +void +spans_timer(tip) + struct atm_time *tip; +{ + struct spans *spp; + spans_msg *msg; + Atm_addr_pvc *pvcp; + int err; + + /* + * Back-off to SPANS control block + */ + spp = (struct spans *) + ((caddr_t)tip - (int)(&((struct spans *)0)->sp_time)); + + ATM_DEBUG2("spans_timer: spp=0x%x,state=%d\n", + (int)spp, spp->sp_state); + + /* + * Process timeout based on protocol state + */ + switch (spp->sp_state) { + + case SPANS_INIT: + + /* + * Open signalling channel + */ + spans_attr.nif = spp->sp_pif->pif_nif; + + spans_attr.aal.v.aal4.forward_max_SDU_size = + ATM_NIF_MTU; + spans_attr.aal.v.aal4.backward_max_SDU_size = + ATM_NIF_MTU; + spans_attr.aal.v.aal4.SSCS_type = + T_ATM_SSCS_SSCOP_UNREL; + spans_attr.aal.v.aal4.mid_low = 0; + spans_attr.aal.v.aal4.mid_high = 0; + + spans_attr.called.tag = T_ATM_PRESENT; + spans_attr.called.addr.address_format = T_ATM_PVC_ADDR; + spans_attr.called.addr.address_length = + sizeof(Atm_addr_pvc); + pvcp = (Atm_addr_pvc *)spans_attr.called.addr.address; + ATM_PVC_SET_VPI(pvcp, SPANS_SIG_VPI); + ATM_PVC_SET_VCI(pvcp, SPANS_SIG_VCI); + spans_attr.called.subaddr.address_format = T_ATM_ABSENT; + spans_attr.called.subaddr.address_length = 0; + + spans_attr.traffic.v.forward.PCR_all_traffic = + spp->sp_pif->pif_pcr; + spans_attr.traffic.v.backward.PCR_all_traffic = + spp->sp_pif->pif_pcr; + + err = atm_cm_connect(&spans_endpt, spp, &spans_attr, + &spp->sp_conn); + if (err) { + log(LOG_CRIT, "spans: signalling channel setup failed\n"); + return; + } + + /* + * Signalling channel open, start probing + */ + spp->sp_state = SPANS_PROBE; + + /* FALLTHRU */ + + case SPANS_PROBE: + case SPANS_ACTIVE: + + /* + * Send out SPANS_STAT_REQ message + */ + msg = (spans_msg *)atm_allocate(&spans_msgpool); + if (msg == NULL) { + /* Retry later if no memory */ + SPANS_TIMER(spp, SPANS_PROBE_ERR_WAIT); + break; + } + msg->sm_vers = SPANS_VERS_1_0; + msg->sm_type = SPANS_STAT_REQ; + msg->sm_stat_req.streq_es_epoch = spp->sp_h_epoch; + if (spans_send_msg(spp, msg)) { + /* Retry later if send fails */ + SPANS_TIMER(spp, SPANS_PROBE_ERR_WAIT); + atm_free(msg); + break; + } + atm_free(msg); + spp->sp_probe_ct++; + + /* + * Check whether we're getting an answer to our probes + */ + if (spp->sp_state == SPANS_ACTIVE && + spp->sp_probe_ct > SPANS_PROBE_THRESH) { + /* + * Interface is down, notify VCC owners + */ + spans_switch_reset(spp, SPANS_UNI_DOWN); + + /* + * Set new state and increment host epoch so + * switch knows we reset everyting. + */ + spp->sp_state = SPANS_PROBE; + spp->sp_h_epoch++; + spp->sp_s_epoch = 0; + } + + /* + * Keep sending status requests + */ + SPANS_TIMER(spp, SPANS_PROBE_INTERVAL); + + break; + + case SPANS_DETACH: + /* + * Try to terminate the SPANS signalling PVC + */ + err = atm_cm_release(spp->sp_conn, &spans_cause); + if (err) { + log(LOG_ERR, "spans: can't close signalling channel\n"); + } + break; + + default: + log(LOG_ERR, "spans: timer state: spp=0x%x, state=%d\n", + (int)spp, spp->sp_state); + } +} + + +/* + * Process a SPANS VCC timeout + * + * Called when a previously scheduled SPANS VCCB timer expires. + * Processing will based on the current VCC state. + * + * Called at splnet. + * + * Arguments: + * tip pointer to vccb timer control block + * + * Returns: + * none + * + */ +void +spans_vctimer(tip) + struct atm_time *tip; +{ + int err; + struct spans *spp; + struct spans_vccb *svp; + + /* + * Get VCCB and SPANS control block addresses + */ + svp = (struct spans_vccb *) ((caddr_t)tip - + (int)(&((struct vccb *)0)->vc_time)); + spp = (struct spans *)svp->sv_pif->pif_siginst; + + ATM_DEBUG3("spans_vctimer: svp=0x%x, sstate=%d, ustate=%d\n", + (int)svp, svp->sv_sstate, svp->sv_ustate); + + /* + * Process timeout based on protocol state + */ + switch (svp->sv_sstate) { + + case SPANS_VC_ABORT: + /* + * Kill the VCCB and notify the owner + */ + err = spans_clear_vcc(spp, svp); + break; + + case SPANS_VC_FREE: + /* + * Free VCCB storage + */ + svp->sv_ustate = VCCU_CLOSED; + svp->sv_sstate = SPANS_VC_FREE; + spans_free((struct vccb *)svp); + break; + + case SPANS_VC_POPEN: + /* + * Issued open request, but didn't get response. + */ + if (svp->sv_retry < SV_MAX_RETRY) { + /* + * Retransmit the open request + */ + err = spans_send_open_req(spp, svp); + svp->sv_retry++; + SPANS_VC_TIMER((struct vccb *) svp, SV_TIMEOUT); + } else { + /* + * Retry limit exceeded--report the open failed + */ + svp->sv_ustate = VCCU_CLOSED; + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_connvc->cvc_attr.cause.tag = + T_ATM_PRESENT; + svp->sv_connvc->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + svp->sv_connvc->cvc_attr.cause.v.location = + T_ATM_LOC_USER; + svp->sv_connvc->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_NO_USER_RESPONDING; + KM_ZERO(svp->sv_connvc->cvc_attr.cause.v.diagnostics, + sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics)); + atm_cm_cleared(svp->sv_connvc); + } + break; + + case SPANS_VC_CLOSE: + /* + * Issued close request, but didn't get response. + */ + if (svp->sv_retry < SV_MAX_RETRY) { + /* + * Retransmit the close request + */ + err = spans_send_close_req(spp, svp); + svp->sv_retry++; + SPANS_VC_TIMER((struct vccb *) svp, SV_TIMEOUT); + } else { + /* + * Retry limit exceeded--just finish the close + */ + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT; + svp->sv_connvc->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + svp->sv_connvc->cvc_attr.cause.v.location = + T_ATM_LOC_USER; + svp->sv_connvc->cvc_attr.cause.v.cause_value = + T_ATM_CAUSE_NO_USER_RESPONDING; + KM_ZERO(svp->sv_connvc->cvc_attr.cause.v.diagnostics, + sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics)); + atm_cm_cleared(svp->sv_connvc); + } + break; + + case SPANS_VC_ACTIVE: + case SPANS_VC_ACT_DOWN: + /* + * Shouldn't happen + */ + log(LOG_ERR, "spans_vctimer: unexpected state %d\n", + svp->sv_sstate); + break; + + default: + log(LOG_ERR, "spans: vctimer state: svp=0x%x, sstate=%d\n", + (int)svp, svp->sv_sstate); + } +} + + +/* + * SPANS name routine + * + * Arguments: + * tok SPANS signalling channel token (ignored) + * + * Returns: + * pointer to a string identifying the SPANS signalling manager + * + */ +caddr_t +spans_getname(tok) + void *tok; +{ + return("SPANS"); +} + + +/* + * Process a VCC connection notification + * + * Should never be called + * + * Arguments: + * tok user's connection token (SPANS protocol block) + * + * Returns: + * none + * + */ +void +spans_connected(tok) + void *tok; +{ + struct spans *spp = (struct spans *)tok; + + ATM_DEBUG2("spans_connected: spp=0x%x,state=%d\n", + (int)spp, spp->sp_state); + + /* + * Connected routine shouldn't ever get called for a PVC + */ + log(LOG_ERR, "spans: connected function called, tok=0x%x\n", + (int)spp); +} + + +/* + * Process a VCC close notification + * + * Called when the SPANS signalling channel is closed + * + * Arguments: + * tok user's connection token (spans protocol block) + * cp pointer to cause structure + * + * Returns: + * none + * + */ +void +spans_cleared(tok, cp) + void *tok; + struct t_atm_cause *cp; +{ + struct spans *spp = (struct spans *)tok; + + /* + * VCC has been closed. + */ + log(LOG_ERR, "spans: signalling channel closed\n"); + SPANS_CANCEL(spp); + spp->sp_conn = 0; +} + + +/* + * SPANS CPCS data handler + * + * This is the module which receives data on the SPANS signalling + * channel. Processing is based on the indication received from the + * AAL and the protocol state. + * + * Arguments: + * tok session token (pointer to spans protocol control block) + * m pointer to buffer with data + * + * Returns: + * none + * + */ +void +spans_cpcs_data(tok, m) + void *tok; + KBuffer *m; +{ + struct spans *spp = tok; + + ATM_DEBUG3("spans_cpcs_data: spp=0x%x,state=%d,m=0x%x,\n", + (int)spp, spp->sp_state, m); + + /* + * Process data + */ + spans_rcv_msg(spp, m); +} diff --git a/sys/netatm/spans/spans_subr.c b/sys/netatm/spans/spans_subr.c new file mode 100644 index 0000000..48b9705 --- /dev/null +++ b/sys/netatm/spans/spans_subr.c @@ -0,0 +1,494 @@ +/* + * + * =================================== + * 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: spans_subr.c,v 1.9 1998/08/26 23:29:10 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS-related subroutines. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_subr.c,v 1.9 1998/08/26 23:29:10 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> + + +/* + * Open a SPANS VCC + * + * Called when a user wants to open a VC. This function will construct + * a VCCB, create the stack requested by the user, and, if we are + * opening an SVC, start the SPANS signalling message exchange. The + * user will have to wait for a notify event to be sure the SVC is fully + * open. + * + * Must be called at splnet. + * + * Arguments: + * spp pointer to SPANS protocol instance + * acp pointer to PVC's connection parameters + * + * Returns: + * 0 VCC creation successful + * errno VCC setup failed - reason indicated + * + */ +int +spans_open_vcc(spp, cvp) + struct spans *spp; + Atm_connvc *cvp; + +{ + struct atm_pif *pip = spp->sp_pif; + struct spans_vccb *svp; + Atm_addr_pvc *pvp; + spans_aal aal; + int err, pvc, vpi, vci; + + ATM_DEBUG2("spans_open_vcc: spp=0x%x, cvp=0x%x\n", spp, cvp); + + /* + * Validate user parameters. AAL and encapsulation are + * checked by the connection manager. + */ + + /* + * Check called party address(es) + */ + if (cvp->cvc_attr.called.tag != T_ATM_PRESENT || + cvp->cvc_attr.called.addr.address_format == + T_ATM_ABSENT || + cvp->cvc_attr.called.subaddr.address_format != + T_ATM_ABSENT) { + return(EINVAL); + } + switch (cvp->cvc_attr.called.addr.address_format) { + case T_ATM_PVC_ADDR: + /* + * Make sure VPI/VCI is valid + */ + pvc = 1; + pvp = (Atm_addr_pvc *)cvp->cvc_attr.called.addr.address; + vpi = ATM_PVC_GET_VPI(pvp); + vci = ATM_PVC_GET_VCI(pvp); + if ((vpi > pip->pif_maxvpi) || + (vci == 0) || + (vci > pip->pif_maxvci)) { + return(ERANGE); + } + + /* + * Make sure VPI/VCI is not already in use + */ + if (spans_find_vpvc(spp, vpi, vci, 0)) { + return(EADDRINUSE); + } + ATM_DEBUG2("spans_open_vcc: VPI.VCI=%d.%d\n", + vpi, vci); + break; + + case T_ATM_SPANS_ADDR: + pvc = 0; + vpi = vci = 0; + + /* + * Check signalling state + */ + if (spp->sp_state != SPANS_ACTIVE) { + return(ENETDOWN); + } + + /* + *Check destination address length + */ + if (cvp->cvc_attr.called.addr.address_length != + sizeof(spans_addr)) { + return(EINVAL); + } + break; + + default: + return(EINVAL); + } + + /* + * Check that this is for the same interface SPANS uses + */ + if (!cvp->cvc_attr.nif || + cvp->cvc_attr.nif->nif_pif != spp->sp_pif) { + return(EINVAL); + } + + /* + * Check AAL + */ + if (!spans_get_spans_aal(cvp->cvc_attr.aal.type, &aal)) { + return(EINVAL); + } + +#ifdef NOTDEF + /* + * Check encapsulation + */ + /* XXX -- How do we check encapsulation? */ + if (cvp->ac_encaps != ATM_ENC_NULL) { + return(EINVAL); + } +#endif + + /* + * Allocate control block for VCC + */ + svp = (struct spans_vccb *)atm_allocate(&spans_vcpool); + if (svp == NULL) { + return(ENOMEM); + } + + /* + * Fill in VCCB + */ + if (pvc) { + svp->sv_type = VCC_PVC | VCC_IN | VCC_OUT; + svp->sv_vpi = vpi; + svp->sv_vci = vci; + svp->sv_sstate = (spp->sp_state == SPANS_ACTIVE ? + SPANS_VC_ACTIVE : SPANS_VC_ACT_DOWN); + svp->sv_ustate = VCCU_OPEN; + } else { + svp->sv_type = VCC_SVC | VCC_OUT; + spans_addr_copy(cvp->cvc_attr.called.addr.address, + &svp->sv_conn.con_dst); + spans_addr_copy(spp->sp_addr.address, + &svp->sv_conn.con_src); + svp->sv_conn.con_dsap = SPANS_SAP_IP; + svp->sv_conn.con_ssap = spans_ephemeral_sap(spp); + svp->sv_sstate = SPANS_VC_POPEN; + svp->sv_ustate = VCCU_POPEN; + } + svp->sv_proto = ATM_SIG_SPANS; + svp->sv_pif = spp->sp_pif; + svp->sv_nif = cvp->cvc_attr.nif; + svp->sv_connvc = cvp; + svp->sv_spans_aal = aal; + svp->sv_tstamp = time_second; + + /* + * Put VCCB on SPANS queue + */ + ENQUEUE(svp, struct spans_vccb, sv_sigelem, spp->sp_vccq); + + /* + * Link VCCB to VCC connection block + */ + cvp->cvc_vcc = (struct vccb *) svp; + + /* + * Start the SPANS message exchange if this is an SVC + */ + if (!pvc) { + svp->sv_retry = 0; + svp->sv_spans_qos.rsc_peak = 1; + svp->sv_spans_qos.rsc_mean = 1; + svp->sv_spans_qos.rsc_burst = 1; + err = spans_send_open_req(spp, svp); + if (err) { + /* + * On error, delete the VCCB + */ + DEQUEUE(svp, struct spans_vccb, sv_sigelem, + spp->sp_vccq); + cvp->cvc_vcc = (struct vccb *)0; + atm_free((caddr_t)svp); + return(err); + } else { + /* + * VCCB is opening--set the retransmit timer + */ + SPANS_VC_TIMER((struct vccb *) svp, SV_TIMEOUT); + } + } + + return(0); +} + + +/* + * Close a SPANS VCC + * + * Called when a user wants to close a VCC. This function will clean + * up the VCCB and, for an SVC, send a close request. + * + * Must be called at splnet. + * + * Arguments: + * spp pointer to SPANS protocol instance + * svp pointer to VCCB for the VCC to be closed + * + * Returns: + * 0 VCC is now closed + * errno error encountered + */ +int +spans_close_vcc(spp, svp, force) + struct spans *spp; + struct spans_vccb *svp; + int force; + +{ + int err = 0; + + ATM_DEBUG2("spans_close_vcc: svp=0x%x, state=%d\n", svp, + svp->sv_sstate); + + /* + * Check that this is for the same interface SPANS uses + */ + if (svp->sv_pif != spp->sp_pif) { + return (EINVAL); + } + + /* + * Kill any possible timer + */ + SPANS_VC_CANCEL((struct vccb *) svp); + + /* + * Mark the close time. + */ + svp->sv_tstamp = time_second; + + /* + * Process based on the connection type + */ + if (svp->sv_type & VCC_PVC) { + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_ustate = VCCU_CLOSED; + } else if (svp->sv_type & VCC_SVC) { + /* + * Update VCCB states + */ + svp->sv_ustate = VCCU_CLOSED; + + /* + * Send the appropriate SPANS close message + */ + switch (svp->sv_sstate) { + case SPANS_VC_R_POPEN: + err = spans_send_open_rsp(spp, svp, SPANS_FAIL); + svp->sv_sstate = SPANS_VC_FREE; + break; + case SPANS_VC_OPEN: + case SPANS_VC_POPEN: + case SPANS_VC_ABORT: + svp->sv_retry = 0; + err = spans_send_close_req(spp, svp); + if (force) { + svp->sv_sstate = SPANS_VC_FREE; + } else { + svp->sv_sstate = SPANS_VC_CLOSE; + SPANS_VC_TIMER((struct vccb *) svp, + SV_TIMEOUT); + } + break; + case SPANS_VC_CLOSE: + if (force) { + svp->sv_sstate = SPANS_VC_FREE; + } + break; + } + } + + /* + * Wait for user to free resources + */ + return(err); +} + + +/* + * Clear a SPANS VCC + * + * Called when the signalling manager wants to close a VCC immediately. + * This function will clean up the VCCB and notify the owner. + * + * Must be called at splnet. + * + * Arguments: + * spp pointer to SPANS protocol instance + * svp pointer to VCCB for the VCC to be closed + * + * Returns: + * 0 VCC is now closed + * errno error encountered + */ +int +spans_clear_vcc(spp, svp) + struct spans *spp; + struct spans_vccb *svp; + +{ + u_char outstate; + + ATM_DEBUG2("spans_clear_vcc: svp=0x%x, state=%d\n", svp, + svp->sv_sstate); + + /* + * Check that this is for the same interface SPANS uses + */ + if (svp->sv_pif != spp->sp_pif) { + return (EINVAL); + } + + /* + * Kill any possible timer + */ + SPANS_VC_CANCEL((struct vccb *) svp); + + /* + * Mark the close time + */ + svp->sv_tstamp = time_second; + + /* + * Mark the VCCB closed + */ + outstate = svp->sv_sstate; + svp->sv_sstate = SPANS_VC_FREE; + svp->sv_ustate = VCCU_CLOSED; + + /* + * Notify the user if old state indicates. + */ + switch (outstate) { + case SPANS_VC_ACTIVE: + case SPANS_VC_ACT_DOWN: + case SPANS_VC_POPEN: + case SPANS_VC_OPEN: + case SPANS_VC_CLOSE: + case SPANS_VC_ABORT: + /* XXX -- set cause */ + atm_cm_cleared(svp->sv_connvc); + break; + case SPANS_VC_NULL: + case SPANS_VC_R_POPEN: + case SPANS_VC_FREE: + break; + } + + /* + * Wait for user to free resources + */ + return(0); +} + + +/* + * Reset the switch state + * + * Called when the switch or host at the far end of the ATM link has + * gone away. This can be deteched either by a number of SPANS_STAT_REQ + * messages going unanswered or by the host epoch changing in a SPANS + * SPANS_STAT_IND or SPANS_STAT_REQ message. + * + * Arguments: + * spp pointer to SPANS protocol instance + * + * Returns: + * none + * + */ +void +spans_switch_reset(spp, cause) + struct spans *spp; + int cause; + +{ + int s; + struct vccb *vcp, *vnext; + + ATM_DEBUG2("spans_switch_reset: spp=0x%x, cause=%d\n", + spp, cause); + + /* + * Log the event + */ + log(LOG_INFO, "spans: signalling %s on interface %s%d\n", + (cause == SPANS_UNI_DOWN ? "down" : "up"), + spp->sp_pif->pif_name, + spp->sp_pif->pif_unit); + + /* + * Terminate all of our VCCs + */ + s = splnet(); + for (vcp = Q_HEAD(spp->sp_vccq, struct vccb); vcp; + vcp = vnext) { + + u_char outstate; + + vnext = Q_NEXT(vcp, struct vccb, vc_sigelem); + + if (vcp->vc_type & VCC_SVC) { + /* + * Close the SVC and notify the owner + */ + outstate = vcp->vc_sstate; + SPANS_VC_CANCEL((struct vccb *) vcp); + vcp->vc_ustate = VCCU_CLOSED; + vcp->vc_sstate = SPANS_VC_FREE; + if (outstate == SPANS_VC_OPEN || + outstate == SPANS_VC_POPEN) { + /* XXX -- set cause */ + atm_cm_cleared(vcp->vc_connvc); + } + } else if (vcp->vc_type & VCC_PVC) { + /* + * Note new state + */ + switch(cause) { + case SPANS_UNI_DOWN: + vcp->vc_sstate = SPANS_VC_ACT_DOWN; + break; + case SPANS_UNI_UP: + vcp->vc_sstate = SPANS_VC_ACTIVE; + break; + } + } else { + log(LOG_ERR, "spans: invalid VCC type: vccb=0x%x, type=%d\n", + vcp, vcp->vc_type); + } + } + (void) splx(s); +} diff --git a/sys/netatm/spans/spans_util.c b/sys/netatm/spans/spans_util.c new file mode 100644 index 0000000..6d470e1 --- /dev/null +++ b/sys/netatm/spans/spans_util.c @@ -0,0 +1,477 @@ +/* + * + * =================================== + * 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: spans_util.c,v 1.6 1998/08/26 23:29:10 mks Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS-related utility routines. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: spans_util.c,v 1.6 1998/08/26 23:29:10 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include "spans_xdr.h" +#include <netatm/spans/spans_var.h> + + +#ifdef NOTDEF +/* XXX -- Remove all SAP checks? */ +#define MAX_SAP_ENT 1 +static struct { + spans_sap spans_sap; + Sap_t local_sap; +} sap_table[MAX_SAP_ENT] = { + {SPANS_SAP_IP, SAP_IP}, +}; + + +/* + * Translate an internal SAP to a SPANS SAP + * + * Search the SAP table for the given SAP. Put the corresponding SPANS + * SAP into the indicated variable. + * + * Arguments: + * lsap the value of the internal SAP + * ssap a pointer to the variable to receive the SPANS SAP value + * + * Returns: + * TRUE the SAP was found; *ssap is valid + * FALSE the SAP was not found; *ssap is not valid + * + */ +int +spans_get_spans_sap(lsap, ssap) + Sap_t lsap; + spans_sap *ssap; +{ + int i; + + /* + * Search the SAP table for the given local SAP + */ + for (i=0; i< MAX_SAP_ENT; i++) { + if (sap_table[i].local_sap == lsap) { + *ssap = sap_table[i].spans_sap; + return(TRUE); + } + } + return(FALSE); +} + + +/* + * Translate a SPANS SAP to internal format + * + * Search the SAP table for the given SAP. Put the corresponding + * internal SAP into the indicated variable. + * + * Arguments: + * ssap the value of the SPANS SAP + * lsap a pointer to the variable to receive the internal + * SAP value + * + * Returns: + * TRUE the SAP was found; *lsap is valid + * FALSE the SAP was not found; *lsap is not valid + * + */ +int +spans_get_local_sap(ssap, lsap) + spans_sap ssap; + Sap_t *lsap; +{ + int i; + + /* + * Search the SAP table for the given SPANS SAP + */ + for (i=0; i< MAX_SAP_ENT; i++) { + if (sap_table[i].spans_sap == ssap) { + *lsap = sap_table[i].local_sap; + return(TRUE); + } + } + return(FALSE); +} +#endif + + +/* + * Allocate an ephemeral SPANS SAP + * + * Arguments: + * spp pointer to SPANS protocol instance + * + * Returns: + * a SPANS ephemeral SAP number + * + */ +int +spans_ephemeral_sap(spp) + struct spans *spp; +{ + return(SPANS_SAP_EPHEMERAL); +} + + +/* + * Translate an internal AAL designator to a SPANS AAL type + * + * Arguments: + * laal internal AAL designation + * saal a pointer to the variable to receive the SPANS AAL type + * + * Returns: + * TRUE the AAL was found; *saal is valid + * FALSE the AAL was not found; *saal is not valid + * + */ +int +spans_get_spans_aal(laal, saal) + Aal_t laal; + spans_aal *saal; +{ + /* + * + */ + switch (laal) { + case ATM_AAL0: + *saal = SPANS_AAL0; + return(TRUE); + case ATM_AAL1: + *saal = SPANS_AAL1; + return(TRUE); + case ATM_AAL2: + *saal = SPANS_AAL2; + return(TRUE); + case ATM_AAL3_4: + *saal = SPANS_AAL4; + return(TRUE); + case ATM_AAL5: + *saal = SPANS_AAL5; + return(TRUE); + default: + return(FALSE); + } +} + + +/* + * Translate a SPANS AAL type to an internal AAL designator + * + * Arguments: + * saal the SPANS AAL type + * laal a pointer to the variable to receive the internal + * AAL designation + * + * Returns: + * TRUE the AAL was found; *laal is valid + * FALSE the AAL was not found; *laal is not valid + * + */ +int +spans_get_local_aal(saal, laal) + spans_aal saal; + Aal_t *laal; +{ + /* + * + */ + switch (saal) { + case SPANS_AAL0: + *laal = ATM_AAL0; + return(TRUE); + case SPANS_AAL1: + *laal = ATM_AAL1; + return(TRUE); + case SPANS_AAL2: + *laal = ATM_AAL2; + return(TRUE); + case SPANS_AAL3: + case SPANS_AAL4: + *laal = ATM_AAL3_4; + return(TRUE); + case SPANS_AAL5: + *laal = ATM_AAL5; + return(TRUE); + default: + return(FALSE); + } +} + + +/* + * Verify a VCCB + * + * Search SPANS's VCCB queue to verify that a VCCB belongs to SPANS. + * + * Arguments: + * spp pointer to SPANS protocol instance + * svp pointer to a VCCB + * + * Returns: + * TRUE the VCCB belongs to SPANS + * FALSE the VCCB doesn't belong to SPANS + * + */ +int +spans_verify_vccb(spp, svp) + struct spans *spp; + struct spans_vccb *svp; + +{ + struct spans_vccb *vcp, *vcnext; + + for (vcp = Q_HEAD(spp->sp_vccq, struct spans_vccb); + vcp; vcp = vcnext){ + vcnext = Q_NEXT(vcp, struct spans_vccb, sv_sigelem); + if (svp == vcp) { + return(TRUE); + } + } + return(FALSE); +} + + +/* + * Find a VCCB + * + * Find a VCCB given the VPI and VCI. + * + * Arguments: + * spp pointer to SPANS protocol instance + * vpi the VPI to search for + * vci the VCI to search for + * dir the direction of the VCC (VCC_IN, VCC_OUT, or both). + * If dir is set to zero, return the address of any VCCB + * with the given VPI/VCI, regardless of direction. + * + * Returns: + * 0 there is no such VCCB + * address the address of the VCCB + * + */ +struct spans_vccb * +spans_find_vpvc(spp, vpi, vci, dir) + struct spans *spp; + int vpi, vci; + u_char dir; + +{ + struct spans_vccb *svp, *svnext; + + for (svp = Q_HEAD(spp->sp_vccq, struct spans_vccb); svp; + svp = svnext){ + svnext = Q_NEXT(svp, struct spans_vccb, sv_sigelem); + if (svp->sv_vpi == vpi && + svp->sv_vci == vci && + (svp->sv_type & dir) == dir) + break; + } + return(svp); +} + + +/* + * Find a connection + * + * Find a VCCB given the connection structure. + * + * Arguments: + * spp pointer to SPANS protocol instance + * p pointer to an spans_atm_conn structure + * + * Returns: + * 0 there is no such VCCB + * address the address of the VCCB + * + */ +struct spans_vccb * +spans_find_conn(spp, p) + struct spans *spp; + struct spans_atm_conn *p; +{ + struct spans_vccb *svp, *svnext; + + for (svp = Q_HEAD(spp->sp_vccq, struct spans_vccb); svp; svp = svnext){ + svnext = Q_NEXT(svp, struct spans_vccb, sv_sigelem); + if (!bcmp(p, &svp->sv_conn, sizeof (spans_atm_conn))) + break; + } + return(svp); +} + + +/* + * Allocate a VPI/VCI pair + * + * When we get an open request or indication from the network, we have + * allocate a VPI and VCI for the conection. This routine will allocate + * a VPI/VCI based on the next available VCI in the SPANS protocol block. + * The VPI/VCI chose must be within the range allowed by the interface and + * must not already be in use. + * + * Currently the Fore ATM interface only supports VPI 0, so this code only + * allocates a VCI. + * + * There's probably a more elegant way to do this. + * + * Arguments: + * spp pointer to connection's SPANS protocol instance + * + * Returns: + * 0 no VPI/VCI available + * vpvc the VPI/VCI for the connection + * + */ +spans_vpvc +spans_alloc_vpvc(spp) + struct spans *spp; +{ + int vpi, vci; + + /* + * Loop through the allowable VCIs, starting with the curent one, + * to find one that's not in use. + */ + while (spp->sp_alloc_vci <= spp->sp_max_vci) { + vpi = spp->sp_alloc_vpi; + vci = spp->sp_alloc_vci++; + if (!spans_find_vpvc(spp, vpi, vci, 0)) { + return(SPANS_PACK_VPIVCI(vpi, vci)); + } + } + + /* + * Reset the VCI to the minimum + */ + spp->sp_alloc_vci = spp->sp_min_vci; + + /* + * Try looping through again + */ + while (spp->sp_alloc_vci <= spp->sp_max_vci) { + vpi = spp->sp_alloc_vpi; + vci = spp->sp_alloc_vci++; + if (!spans_find_vpvc(spp, vpi, vci, 0)) { + return(SPANS_PACK_VPIVCI(vpi, vci)); + } + } + + /* + * All allowable VCIs are in use + */ + return(0); +} + + +/* + * Print a SPANS address + * + * Convert a SPANS address into an ASCII string suitable for printing. + * + * Arguments: + * p pointer to a struct spans_addr + * + * Returns: + * the address of a string with the ASCII representation of the + * address. + * + */ +char * +spans_addr_print(p) + struct spans_addr *p; +{ + static char strbuff[80]; + union { + int w; + char c[4]; + } u1, u2; + + + /* + * Clear the returned string + */ + KM_ZERO(strbuff, sizeof(strbuff)); + + /* + * Get address into integers + */ + u1.c[0] =p->addr[0]; + u1.c[1] =p->addr[1]; + u1.c[2] =p->addr[2]; + u1.c[3] =p->addr[3]; + u2.c[0] =p->addr[4]; + u2.c[1] =p->addr[5]; + u2.c[2] =p->addr[6]; + u2.c[3] =p->addr[7]; + + /* + * Print and return the string + */ + sprintf(strbuff, "%x.%x", ntohl(u1.w), ntohl(u2.w)); + return(strbuff); +} + + +/* + * Print a buffer chain + * + * Arguments: + * m pointer to a buffer chain + * + * Returns: + * none + * + */ +void +spans_dump_buffer(m) + KBuffer *m; +{ + int i; + caddr_t cp; + + printf("spans_dump_buffer:\n"); + while (m) { + KB_DATASTART(m, cp, caddr_t); + for (i = 0; i < KB_LEN(m); i++) { + if (i == 0) + printf(" bfr=0x%x: ", (int)m); + printf("%x ", (u_char)*cp++); + } + printf("<end_bfr>\n"); + m = KB_NEXT(m); + } +} diff --git a/sys/netatm/spans/spans_var.h b/sys/netatm/spans/spans_var.h new file mode 100644 index 0000000..8f9ac8a --- /dev/null +++ b/sys/netatm/spans/spans_var.h @@ -0,0 +1,259 @@ +/* + * + * =================================== + * 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: spans_var.h,v 1.7 1998/04/09 14:24:18 johnc Exp $ + * + */ + +/* + * SPANS Signalling Manager + * --------------------------- + * + * Protocol control blocks + * + */ + +#ifndef _SPANS_SPANS_VAR_H +#define _SPANS_SPANS_VAR_H + +#ifdef ATM_KERNEL +/* + * Constants to indicate the state of the signalling interface + */ +#define SPANS_UNI_UP 1 +#define SPANS_UNI_DOWN -1 + + +/* + * Structure containing state information for each SPANS protocol + * instance. There will be one instance for each ATM device interface + * using the SPANS signalling manager. + */ +struct spans { + struct siginst sp_inst; /* Header */ + struct atm_time sp_time; /* Timer controls */ + void (*sp_lower) /* Lower command handler */ + __P((int, void *, int, int)); + Atm_connection *sp_conn; /* Signalling connection */ + long sp_s_epoch; /* Switch epoch */ + long sp_h_epoch; /* Host epoch */ + u_int sp_probe_ct; /* Status_req msgs unanswered */ + u_int sp_alloc_vci; /* Next VCI to allocate */ + u_int sp_alloc_vpi; /* Next VPI to allocate */ + u_int sp_min_vci; /* Lowest VCI to allocate */ + u_int sp_max_vci; /* Highest VCI to allocate */ + struct spanscls *sp_cls; /* CLS instance */ +}; + +#define sp_next sp_inst.si_next +#define sp_pif sp_inst.si_pif +#define sp_addr sp_inst.si_addr +#define sp_subaddr sp_inst.si_subaddr +#define sp_vccq sp_inst.si_vccq +#define sp_state sp_inst.si_state +#define sp_ipserv sp_inst.si_ipserv +#endif /* ATM_KERNEL */ + +/* + * SPANS Protocol States + */ +#define SPANS_ACTIVE 1 /* Active */ +#define SPANS_DETACH 2 /* Detach in progress */ +#define SPANS_INIT 3 /* Initializing */ +#define SPANS_PROBE 4 /* Exchanging status info */ + +#define SPANS_PROBE_INTERVAL (ATM_HZ) /* Interval between SPANS_STAT_REQs */ +#define SPANS_PROBE_THRESH 10 /* Probe time-out threshold */ +#define SPANS_PROBE_ERR_WAIT (3 * ATM_HZ) /* Time to wait if send probe fails */ + + +#ifdef ATM_KERNEL +/* + * SPANS Virtual Channel Connection control block. All information + * regarding the state of a SPANS-controlled VCC will be recorded here. + * There will be one SPANS VCC control block for each SPANS-controlled + * VCC. + */ +struct spans_vccb { + struct vccb vcp_hdr; /* Generic VCCB */ + u_short sv_retry; /* Xmit retry count */ + spans_atm_conn sv_conn; /* SPANS connection info */ + spans_resrc sv_spans_qos; /* QoS for VCC */ + spans_aal sv_spans_aal; /* AAL for VCC */ +}; + +#define sv_type vcp_hdr.vc_type +#define sv_proto vcp_hdr.vc_proto +#define sv_sstate vcp_hdr.vc_sstate +#define sv_ustate vcp_hdr.vc_ustate +#define sv_pif vcp_hdr.vc_pif +#define sv_nif vcp_hdr.vc_nif +#define sv_sigelem vcp_hdr.vc_sigelem +#define sv_time vcp_hdr.vc_time +#define sv_vpi vcp_hdr.vc_vpi +#define sv_vci vcp_hdr.vc_vci +#define sv_connvc vcp_hdr.vc_connvc +#define sv_ipdus vcp_hdr.vc_ipdus +#define sv_opdus vcp_hdr.vc_opdus +#define sv_ibytes vcp_hdr.vc_ibytes +#define sv_obytes vcp_hdr.vc_obytes +#define sv_ierrors vcp_hdr.vc_ierrors +#define sv_oerrors vcp_hdr.vc_oerrors +#define sv_tstamp vcp_hdr.vc_tstamp +#define sv_daddr sv_conn.daddr +#define sv_saddr sv_conn.saddr +#define sv_dsap sv_conn.dsap +#define sv_ssap sv_conn.ssap + +#define SV_MAX_RETRY 3 +#define SV_TIMEOUT (ATM_HZ) + +#endif /* ATM_KERNEL */ + + +/* + * SPANS VCC Signalling Protocol States + */ +#define SPANS_VC_NULL 0 /* No state */ +#define SPANS_VC_ACTIVE 1 /* Active */ +#define SPANS_VC_ACT_DOWN 2 /* Active - Interface down */ +#define SPANS_VC_POPEN 3 /* VCC open in progress */ +#define SPANS_VC_R_POPEN 4 /* VCC rmt open in progress */ +#define SPANS_VC_OPEN 5 /* VCC open */ +#define SPANS_VC_CLOSE 6 /* VCC close in progress */ +#define SPANS_VC_ABORT 7 /* VCC abort in progress */ +#define SPANS_VC_FREE 8 /* Waiting for user to free resources */ + + +#ifdef ATM_KERNEL +/* + * Macro to compare two SPANS addresses. + * + * Returns 0 if the addresses are equal. + */ +#define spans_addr_cmp(a, b) \ + (bcmp((caddr_t)a, (caddr_t)b, sizeof(struct spans_addr))) + +/* + * Macro to copy a SPANS address from a to b. + */ +#define spans_addr_copy(a, b) \ + (KM_COPY((caddr_t)a, (caddr_t)b, sizeof(struct spans_addr))) + + +/* + * Timer macros + */ +#define SPANS_TIMER(s, t) atm_timeout(&(s)->sp_time, (t), spans_timer) +#define SPANS_CANCEL(s) atm_untimeout(&(s)->sp_time) +#define SPANS_VC_TIMER(v, t) atm_timeout(&(v)->vc_time, (t), spans_vctimer) +#define SPANS_VC_CANCEL(v) atm_untimeout(&(v)->vc_time) + + +/* + * Global function declarations + */ +struct ipvcc; + + /* spans_arp.c */ +int spansarp_svcout __P((struct ipvcc *, struct in_addr *)); +int spansarp_svcin __P((struct ipvcc *, Atm_addr *, Atm_addr *)); +int spansarp_svcactive __P((struct ipvcc *)); +void spansarp_vcclose __P((struct ipvcc *)); +void spansarp_ipact __P((struct spanscls *)); +void spansarp_ipdact __P((struct spanscls *)); +void spansarp_stop __P((void)); +void spansarp_input __P((struct spanscls *, KBuffer *)); +int spansarp_ioctl __P((int, caddr_t, caddr_t)); + + /* spans_cls.c */ +int spanscls_start __P((void)); +void spanscls_stop __P((void)); +int spanscls_attach __P((struct spans *)); +void spanscls_detach __P((struct spans *)); +void spanscls_closevc __P((struct spanscls *, + struct t_atm_cause *)); + + /* spans_if.c */ +int spans_abort __P((struct vccb *)); +int spans_free __P((struct vccb *)); + + /* spans_msg.c */ +int spans_send_msg __P((struct spans *, spans_msg *)); +int spans_send_open_req __P((struct spans *, + struct spans_vccb *)); +int spans_send_open_rsp __P((struct spans *, + struct spans_vccb *, + spans_result)); +int spans_send_close_req __P((struct spans *, + struct spans_vccb *)); +void spans_rcv_msg __P((struct spans *, KBuffer *)); + + /* spans_print.c */ +void spans_print_msg __P((spans_msg *)); + + /* spans_proto.c */ +void spans_timer __P((struct atm_time *)); +void spans_vctimer __P((struct atm_time *)); +void spans_upper __P((int, void *, int, int)); +void spans_notify __P((void *, int, int)); + + /* spans_subr.c */ +int spans_open_vcc __P((struct spans *, Atm_connvc *)); +int spans_close_vcc __P((struct spans *, + struct spans_vccb *, int)); +int spans_clear_vcc __P((struct spans *, + struct spans_vccb *)); +void spans_switch_reset __P((struct spans *, int)); + + /* spans_util.c */ +int spans_get_spans_sap __P((Sap_t, spans_sap *)); +int spans_get_local_sap __P((spans_sap, Sap_t *)); +int spans_ephemeral_sap __P((struct spans *)); +int spans_get_spans_aal __P((Aal_t, spans_aal *)); +int spans_get_local_aal __P((spans_aal, Aal_t *)); +int spans_verify_vccb __P((struct spans *, + struct spans_vccb *)); +struct spans_vccb * + spans_find_vpvc __P((struct spans *, int, int, u_char)); +struct spans_vccb * + spans_find_conn __P((struct spans *, + struct spans_atm_conn *)); +spans_vpvc spans_alloc_vpvc __P((struct spans *)); +char * spans_addr_print __P((struct spans_addr *)); +void spans_dump_buffer __P((KBuffer *)); + + +/* + * External variables + */ +extern struct spans_addr spans_bcastaddr; +extern struct sp_info spans_vcpool; +extern struct sp_info spans_msgpool; +extern struct t_atm_cause spans_cause; + +#endif /* ATM_KERNEL */ + +#endif /* _SPANS_SPANS_VAR_H */ diff --git a/sys/netatm/spans/spans_xdr.x b/sys/netatm/spans/spans_xdr.x new file mode 100644 index 0000000..7e53f53 --- /dev/null +++ b/sys/netatm/spans/spans_xdr.x @@ -0,0 +1,513 @@ +%/* +% * +% * =================================== +% * 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: spans_xdr.x,v 1.4 1997/05/06 22:17:36 mks Exp $ +% * +% */ +% +/* + * SPANS Signalling Manager + * --------------------------- + * + * SPANS Protocol Message XDR Specification + * + */ + +#ifdef RPC_HDR +%/* +% * SPANS Signalling Manager +% * --------------------------- +% * +% * SPANS Protocol Message Definitions +% * +% */ +% +%#ifndef _SPANS_SPANS_XDR_H +%#define _SPANS_SPANS_XDR_H +% +%#include <rpc/types.h> +% +#endif + +#ifdef RPC_XDR +%/* +% * SPANS Signalling Manager +% * --------------------------- +% * +% * SPANS Protocol Message XDR Routines +% * +% */ +% +%#ifndef lint +%static char *RCSid = "@(#) $Id: spans_xdr.x,v 1.4 1997/05/06 22:17:36 mks Exp $"; +%#endif +% +#endif + + +/* + * SPANS Signalling + */ +const SPANS_SIG_VPI = 0; /* Signalling VPI */ +const SPANS_SIG_VCI = 15; /* Signalling VCI */ +const SPANS_CLS_VPI = 0; /* Connectionless VPI */ +const SPANS_CLS_VCI = 14; /* Connectionless VCI */ + +const SPANS_MIN_VCI = 32; /* Lowest VCI to allocate */ +const SPANS_MAX_VCI = 1023; /* Highest VCI to allocate */ +const SPANS_VPI = 0; /* Only VPI to allocate */ + +/* + * SPANS Protocol Version + * + * Major_version * 256 + Minor_version + */ +typedef u_int spans_version; + +const SPANS_VERS_1_0 = 0x0100; /* Version 1.0 */ + + +/* + * VPI/VCI + * + * Format: + * 4 bits - unused + * 12 bits - VPI value + * 16 bits - VCI value + */ +typedef u_int spans_vpvc; /* VPI/VCI value */ + +#ifdef RPC_HDR +%#define SPANS_EXTRACT_VPI(p) (((p) >> 16) & 0x0FFF) +%#define SPANS_EXTRACT_VCI(p) ((p) & 0x0FFFF) +%#define SPANS_PACK_VPIVCI(p, c) ((((p) & 0x0FFF) << 16) | ((c) & 0x0FFFF)) +#endif + + +/* + * VPI/VCI Preference + */ +struct spans_vpvc_pref { + bool vpf_valid; /* VPI/VCI values valid */ + spans_vpvc vpf_vpvc; /* VPI/VCI value */ +}; + + +/* + * SPANS ATM Address + */ +struct spans_addr { + opaque addr[8]; /* SPANS ATM address */ +}; + + +/* + * Service Access Point (SAP) + */ +typedef u_int spans_sap; /* SAP value */ + +const SPANS_SAP_IP = 1025; /* TCP/IP */ +const SPANS_SAP_EPHEMERAL = 2048; /* Start of ephemeral SAPs*/ + + +/* + * ATM Connection Identifier + */ +struct spans_atm_conn { + spans_addr con_dst; /* Destination ATM address */ + spans_addr con_src; /* Source ATM address */ + spans_sap con_dsap; /* Destination SAP */ + spans_sap con_ssap; /* Source SAP */ +}; + + +/* + * Connection Resources + */ +struct spans_resrc { + u_int rsc_peak; /* Peak bandwidth (Kbps) */ + u_int rsc_mean; /* Mean bandwidth (Kbps) */ + u_int rsc_burst; /* Mean burst (Kb) */ +}; + + +/* + * ATM Adaptation Layer (AAL) Types + */ +enum spans_aal { + SPANS_AAL0 = 0, /* NULL AAL */ + SPANS_AAL1 = 1, /* AAL 1 */ + SPANS_AAL2 = 2, /* AAL 2 */ + SPANS_AAL3 = 3, /* AAL 3 */ + SPANS_AAL4 = 4, /* AAL 4 */ + SPANS_AAL5 = 5 /* AAL 5 */ +}; + + +/* + * Result Codes + */ +enum spans_result { + SPANS_OK = 0, /* Success */ + SPANS_FAIL = 1, /* Failure */ + SPANS_NOVPVC = 2, /* No VP/VC */ + SPANS_NORSC = 3, /* No resources */ + SPANS_BADDEST = 4 /* Bad destination */ +}; + + +/* + * Message Types + */ +enum spans_msgtype { + /* + * SPANS UNI message types + */ + SPANS_STAT_REQ = 0, /* Status request */ + SPANS_STAT_IND = 1, /* Status indication */ + SPANS_STAT_RSP = 2, /* Status response */ + SPANS_OPEN_REQ = 3, /* Open request */ + SPANS_OPEN_IND = 4, /* Open indication */ + SPANS_OPEN_RSP = 5, /* Open response */ + SPANS_OPEN_CNF = 6, /* Open confirmation */ + SPANS_CLOSE_REQ = 7, /* Close request */ + SPANS_CLOSE_IND = 8, /* Close indication */ + SPANS_CLOSE_RSP = 9, /* Close response */ + SPANS_CLOSE_CNF = 10, /* Close confirmation */ + SPANS_RCLOSE_REQ = 11, /* Reverse close request */ + SPANS_RCLOSE_IND = 12, /* Reverse close indication */ + SPANS_RCLOSE_RSP = 13, /* Reverse close response */ + SPANS_RCLOSE_CNF = 14, /* Reverse close confirmation */ + SPANS_MULTI_REQ = 15, /* Multicast request */ + SPANS_MULTI_IND = 16, /* Multicast indication */ + SPANS_MULTI_RSP = 17, /* Multicast response */ + SPANS_MULTI_CNF = 18, /* Multicast confirmation */ + SPANS_ADD_REQ = 19, /* Add request */ + SPANS_ADD_IND = 20, /* Add indication */ + SPANS_ADD_RSP = 21, /* Add response */ + SPANS_ADD_CNF = 22, /* Add confirmation */ + SPANS_JOIN_REQ = 23, /* Join request */ + SPANS_JOIN_CNF = 24, /* Join confirmation */ + SPANS_LEAVE_REQ = 25, /* Leave request */ + SPANS_LEAVE_CNF = 26, /* Leave confirmation */ + + /* + * SPANS NNI message types + */ + SPANS_NSAP_IND = 99, /* NSAP routing message */ + SPANS_MAP_IND = 100, /* Topology message */ + SPANS_SETUP_REQ = 101, /* Setup request */ + SPANS_SETUP_RSP = 102, /* Setup response */ + SPANS_CHANGE_REQ = 103, /* Change request */ + SPANS_CHANGE_RSP = 104, /* Change response */ + SPANS_RELOC_REQ = 105, /* Relocation request */ + SPANS_RELOC_RSP = 106, /* Relocation response */ + SPANS_HELLO_IND = 107, /* Hello message */ + + SPANS_VCIR_IND = 108, /* VCI range indication */ + SPANS_QUERY_REQ = 110, /* Conn. state query request */ + SPANS_QUERY_RSP = 111 /* Conn. state query response */ +}; + + +/* + * Query types + */ +enum spans_query_type { + SPANS_QUERY_NORMAL, /* Normal--respond */ + SPANS_QUERY_DEBUG, /* Debug--respond with state */ + SPANS_QUERY_END_TO_END /* Not implemented */ +}; + + +/* + * SPANS connection states + */ +enum spans_conn_state { + SPANS_CONN_OPEN, /* Connection is open */ + SPANS_CONN_OPEN_PEND, /* Connection is being opened */ + SPANS_CONN_CLOSE_PEND, /* Connection is being closed */ + SPANS_CONN_CLOSED /* Connection does not exist */ +}; + + +/* + * Message Parameters + * + * There is a separate message parameter structure for each + * message type. + */ +struct spans_parm_stat_req { + u_long streq_es_epoch; /* End system epoch */ +}; + +struct spans_parm_stat_ind { + u_long stind_sw_epoch; /* Switch epoch */ + spans_addr stind_es_addr; /* End system ATM address */ + spans_addr stind_sw_addr; /* Switch ATM address */ +}; + +struct spans_parm_stat_rsp { + u_long strsp_es_epoch; /* End system epoch */ + spans_addr strsp_es_addr; /* End system ATM address */ +}; + +struct spans_parm_open_req { + spans_atm_conn opreq_conn; /* Connection identity */ + spans_aal opreq_aal; /* AAL type */ + spans_resrc opreq_desrsrc; /* Desired resources */ + spans_resrc opreq_minrsrc; /* Minimum resources */ + spans_vpvc_pref opreq_vpvc; /* VPI/VCI preference */ +}; + +struct spans_parm_open_ind { + spans_atm_conn opind_conn; /* Connection identity */ + spans_aal opind_aal; /* AAL type */ + spans_resrc opind_desrsrc; /* Desired resources */ + spans_resrc opind_minrsrc; /* Minimum resources */ + spans_vpvc_pref opind_vpvc; /* VPI/VCI preference */ +}; + +struct spans_parm_open_rsp { + spans_atm_conn oprsp_conn; /* Connection identity */ + spans_result oprsp_result; /* Open result */ + spans_resrc oprsp_rsrc; /* Allocated resources */ + spans_vpvc oprsp_vpvc; /* Allocated VPI/VCI */ +}; + +struct spans_parm_open_cnf { + spans_atm_conn opcnf_conn; /* Connection identity */ + spans_result opcnf_result; /* Open result */ + spans_resrc opcnf_rsrc; /* Allocated resources */ + spans_vpvc opcnf_vpvc; /* Allocated VPI/VCI */ +}; + +struct spans_parm_close_req { + spans_atm_conn clreq_conn; /* Connection identity */ +}; + +struct spans_parm_close_ind { + spans_atm_conn clind_conn; /* Connection identity */ +}; + +struct spans_parm_close_rsp { + spans_atm_conn clrsp_conn; /* Connection identity */ + spans_result clrsp_result; /* Close result */ +}; + +struct spans_parm_close_cnf { + spans_atm_conn clcnf_conn; /* Connection identity */ + spans_result clcnf_result; /* Close result */ +}; + +struct spans_parm_rclose_req { + spans_atm_conn rcreq_conn; /* Connection identity */ +}; + +struct spans_parm_rclose_ind { + spans_atm_conn rcind_conn; /* Connection identity */ +}; + +struct spans_parm_rclose_rsp { + spans_atm_conn rcrsp_conn; /* Connection identity */ + spans_result rcrsp_result; /* Rclose result */ +}; + +struct spans_parm_rclose_cnf { + spans_atm_conn rccnf_conn; /* Connection identity */ + spans_result rccnf_result; /* Rclose result */ +}; + +struct spans_parm_multi_req { + spans_atm_conn mureq_conn; /* Connection identity */ + spans_aal mureq_aal; /* AAL type */ + spans_resrc mureq_desrsrc; /* Desired resources */ + spans_resrc mureq_minrsrc; /* Minimum resources */ + spans_vpvc mureq_vpvc; /* VPI/VCI preference */ +}; + +struct spans_parm_multi_ind { + spans_atm_conn muind_conn; /* Connection identity */ + spans_aal muind_aal; /* AAL type */ + spans_resrc muind_desrsrc; /* Desired resources */ + spans_resrc muind_minrsrc; /* Minimum resources */ + spans_vpvc muind_vpvc; /* VPI/VCI preference */ +}; + +struct spans_parm_multi_rsp { + spans_atm_conn mursp_conn; /* Connection identity */ + spans_result mursp_result; /* Multi result */ + spans_resrc mursp_rsrc; /* Allocated resources */ + spans_vpvc mursp_vpvc; /* Allocated VPI/VCI */ +}; + +struct spans_parm_multi_cnf { + spans_atm_conn mucnf_conn; /* Connection identity */ + spans_result mucnf_result; /* Multi result */ + spans_resrc mucnf_rsrc; /* Allocated resources */ + spans_vpvc mucnf_vpvc; /* Allocated VPI/VCI */ +}; + +struct spans_parm_add_req { + spans_atm_conn adreq_desconn; /* Desired connection identity */ + spans_atm_conn adreq_xstconn; /* Existing connection identity */ +}; + +struct spans_parm_add_ind { + spans_atm_conn adind_desconn; /* Desired connection identity */ + spans_atm_conn adind_xstconn; /* Existing connection identity */ +}; + +struct spans_parm_add_rsp { + spans_atm_conn adrsp_conn; /* Connection identity */ + spans_result adrsp_result; /* Add result */ + spans_resrc adrsp_rsrc; /* Allocated resources */ +}; + +struct spans_parm_add_cnf { + spans_atm_conn adcnf_conn; /* Connection identity */ + spans_result adcnf_result; /* Add result */ + spans_resrc adcnf_rsrc; /* Allocated resources */ +}; + +struct spans_parm_join_req { + spans_addr jnreq_addr; /* Group address */ +}; + +struct spans_parm_join_cnf { + spans_addr jncnf_addr; /* Group address */ + spans_result jncnf_result; /* Join result */ +}; + +struct spans_parm_leave_req { + spans_addr lvreq_addr; /* Group address */ +}; + +struct spans_parm_leave_cnf { + spans_addr lvcnf_addr; /* Group address */ + spans_result lvcnf_result; /* Leave result */ +}; + +struct spans_parm_vcir_ind { + u_int vrind_min; /* Lowest VCI available */ + u_int vrind_max; /* Highest VCI available */ +}; + +struct spans_parm_query_req { + spans_atm_conn qyreq_conn; /* Conn. being queried */ + spans_query_type qyreq_type; /* Query type */ +}; + +struct spans_parm_query_rsp { + spans_atm_conn qyrsp_conn; /* Conn. being queried */ + spans_query_type qyrsp_type; /* Query type */ + spans_conn_state qyrsp_state; /* Conn. state */ + u_int qyrsp_data; /* Extra state data */ +}; + + +/* + * Message Body + */ +union spans_msgbody switch (spans_msgtype mb_type) { + +case SPANS_STAT_REQ: spans_parm_stat_req mb_stat_req; +case SPANS_STAT_IND: spans_parm_stat_ind mb_stat_ind; +case SPANS_STAT_RSP: spans_parm_stat_rsp mb_stat_rsp; +case SPANS_OPEN_REQ: spans_parm_open_req mb_open_req; +case SPANS_OPEN_IND: spans_parm_open_ind mb_open_ind; +case SPANS_OPEN_RSP: spans_parm_open_rsp mb_open_rsp; +case SPANS_OPEN_CNF: spans_parm_open_cnf mb_open_cnf; +case SPANS_CLOSE_REQ: spans_parm_close_req mb_close_req; +case SPANS_CLOSE_IND: spans_parm_close_ind mb_close_ind; +case SPANS_CLOSE_RSP: spans_parm_close_rsp mb_close_rsp; +case SPANS_CLOSE_CNF: spans_parm_close_cnf mb_close_cnf; +case SPANS_RCLOSE_REQ: spans_parm_rclose_req mb_rclose_req; +case SPANS_RCLOSE_IND: spans_parm_rclose_ind mb_rclose_ind; +case SPANS_RCLOSE_RSP: spans_parm_rclose_rsp mb_rclose_rsp; +case SPANS_RCLOSE_CNF: spans_parm_rclose_cnf mb_rclose_cnf; +case SPANS_MULTI_REQ: spans_parm_multi_req mb_multi_req; +case SPANS_MULTI_IND: spans_parm_multi_ind mb_multi_ind; +case SPANS_MULTI_RSP: spans_parm_multi_rsp mb_multi_rsp; +case SPANS_MULTI_CNF: spans_parm_multi_cnf mb_multi_cnf; +case SPANS_ADD_REQ: spans_parm_add_req mb_add_req; +case SPANS_ADD_IND: spans_parm_add_ind mb_add_ind; +case SPANS_ADD_RSP: spans_parm_add_rsp mb_add_rsp; +case SPANS_ADD_CNF: spans_parm_add_cnf mb_add_cnf; +case SPANS_JOIN_REQ: spans_parm_join_req mb_join_req; +case SPANS_JOIN_CNF: spans_parm_join_cnf mb_join_cnf; +case SPANS_LEAVE_REQ: spans_parm_leave_req mb_leave_req; +case SPANS_LEAVE_CNF: spans_parm_leave_cnf mb_leave_cnf; +case SPANS_VCIR_IND: spans_parm_vcir_ind mb_vcir_ind; +case SPANS_QUERY_REQ: spans_parm_query_req mb_query_req; +case SPANS_QUERY_RSP: spans_parm_query_rsp mb_query_rsp; +}; + + +/* + * Message Format + */ +struct spans_msg { + spans_version sm_vers; + spans_msgbody sm_body; +}; + +#ifdef RPC_HDR +%#define sm_type sm_body.mb_type +%#define sm_stat_req sm_body.spans_msgbody_u.mb_stat_req +%#define sm_stat_ind sm_body.spans_msgbody_u.mb_stat_ind +%#define sm_stat_rsp sm_body.spans_msgbody_u.mb_stat_rsp +%#define sm_open_req sm_body.spans_msgbody_u.mb_open_req +%#define sm_open_ind sm_body.spans_msgbody_u.mb_open_ind +%#define sm_open_rsp sm_body.spans_msgbody_u.mb_open_rsp +%#define sm_open_cnf sm_body.spans_msgbody_u.mb_open_cnf +%#define sm_close_req sm_body.spans_msgbody_u.mb_close_req +%#define sm_close_ind sm_body.spans_msgbody_u.mb_close_ind +%#define sm_close_rsp sm_body.spans_msgbody_u.mb_close_rsp +%#define sm_close_cnf sm_body.spans_msgbody_u.mb_close_cnf +%#define sm_rclose_req sm_body.spans_msgbody_u.mb_rclose_req +%#define sm_rclose_ind sm_body.spans_msgbody_u.mb_rclose_ind +%#define sm_rclose_rsp sm_body.spans_msgbody_u.mb_rclose_rsp +%#define sm_rclose_cnf sm_body.spans_msgbody_u.mb_rclose_cnf +%#define sm_multi_req sm_body.spans_msgbody_u.mb_multi_req +%#define sm_multi_ind sm_body.spans_msgbody_u.mb_multi_ind +%#define sm_multi_rsp sm_body.spans_msgbody_u.mb_multi_rsp +%#define sm_multi_cnf sm_body.spans_msgbody_u.mb_multi_cnf +%#define sm_add_req sm_body.spans_msgbody_u.mb_add_req +%#define sm_add_ind sm_body.spans_msgbody_u.mb_add_ind +%#define sm_add_rsp sm_body.spans_msgbody_u.mb_add_rsp +%#define sm_add_cnf sm_body.spans_msgbody_u.mb_add_cnf +%#define sm_join_req sm_body.spans_msgbody_u.mb_join_req +%#define sm_join_cnf sm_body.spans_msgbody_u.mb_join_cnf +%#define sm_leave_req sm_body.spans_msgbody_u.mb_leave_req +%#define sm_leave_cnf sm_body.spans_msgbody_u.mb_leave_cnf +%#define sm_vcir_ind sm_body.spans_msgbody_u.mb_vcir_ind +%#define sm_query_req sm_body.spans_msgbody_u.mb_query_req +%#define sm_query_rsp sm_body.spans_msgbody_u.mb_query_rsp +#endif + +#ifdef RPC_HDR +%#endif /* _SPANS_SPANS_XDR_H */ +#endif diff --git a/sys/netatm/uni/Makefile b/sys/netatm/uni/Makefile new file mode 100644 index 0000000..5b769b4 --- /dev/null +++ b/sys/netatm/uni/Makefile @@ -0,0 +1,93 @@ +# +# +# =================================== +# 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: Makefile,v 1.6 1998/08/26 23:29:17 mks Exp $ +# +# + +# +# ATM Forum UNI Support +# --------------------- +# +# Source directory Makefile +# +# + +DEFS= + +UNI_HDRS= uni.h +UNI_SRCS= uni_load.c +UNI_OBJS= uni_load.o + +SIG_HDRS= unisig.h unisig_decode.h unisig_mbuf.h \ + unisig_msg.h unisig_print.h unisig_var.h +SIG_SRCS= unisig_decode.c unisig_encode.c unisig_if.c \ + unisig_mbuf.c unisig_msg.c \ + unisig_print.c unisig_proto.c \ + unisig_sigmgr_state.c unisig_subr.c \ + unisig_util.c unisig_vc_state.c +SIG_OBJS= unisig_decode.o unisig_encode.o unisig_if.o \ + unisig_mbuf.o unisig_msg.o \ + unisig_print.o unisig_proto.o \ + unisig_sigmgr_state.o unisig_subr.o \ + unisig_util.o unisig_vc_state.o + +SAAL_HDRS= sscop.h sscop_misc.h sscop_pdu.h sscop_var.h \ + sscf_uni.h sscf_uni_var.h +SAAL_SRCS= sscop.c sscop_lower.c sscop_pdu.c sscop_sigaa.c \ + sscop_sigcpcs.c sscop_subr.c sscop_timer.c sscop_upper.c \ + qsaal1_sigaa.c qsaal1_sigcpcs.c qsaal1_subr.c \ + q2110_sigaa.c q2110_sigcpcs.c q2110_subr.c \ + sscf_uni.c sscf_uni_lower.c sscf_uni_upper.c +SAAL_OBJS= sscop.o sscop_lower.o sscop_pdu.o sscop_sigaa.o \ + sscop_sigcpcs.o sscop_subr.o sscop_timer.o sscop_upper.o \ + qsaal1_sigaa.o qsaal1_sigcpcs.o qsaal1_subr.o \ + q2110_sigaa.o q2110_sigcpcs.o q2110_subr.o \ + sscf_uni.o sscf_uni_lower.o sscf_uni_upper.o + +IP_HDRS= uniip_var.h +IP_SRCS= uniip.c uniarp.c uniarp_cache.c uniarp_input.c \ + uniarp_output.c uniarp_timer.c uniarp_vcm.c +IP_OBJS= uniip.o uniarp.o uniarp_cache.o uniarp_input.o \ + uniarp_output.o uniarp_timer.o uniarp_vcm.o + +HDRS= $(UNI_HDRS) $(SIG_HDRS) $(SAAL_HDRS) $(IP_HDRS) +SRCS= $(UNI_SRCS) $(SIG_SRCS) $(SAAL_SRCS) $(IP_SRCS) +OBJS= $(UNI_OBJS) $(SIG_OBJS) $(SAAL_OBJS) $(IP_OBJS) +MOD= uni_mod.o + +OBJDIR= ../../`../../config/mkobjname -d`/uni + +all $(OBJS) $(MOD) config install clean depend lint load unload: + @if [ -d $(OBJDIR) ]; then \ + echo "cd $(OBJDIR); $(MAKE) $@"; \ + cd $(OBJDIR); \ + $(MAKE) $(MFLAGS) DEFS='$(DEFS)' HDRS='$(HDRS)' SRCS='$(SRCS)' OBJS='$(OBJS)' $@; \ + exit $$?; \ + else \ + echo "Object directory \"$(OBJDIR)\" does not exist."; \ + exit 1; \ + fi + diff --git a/sys/netatm/uni/q2110_sigaa.c b/sys/netatm/uni/q2110_sigaa.c new file mode 100644 index 0000000..357df71 --- /dev/null +++ b/sys/netatm/uni/q2110_sigaa.c @@ -0,0 +1,516 @@ +/* + * + * =================================== + * 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: q2110_sigaa.c,v 1.6 1998/08/26 23:29:18 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * ITU-T Q.2110 - Process AA-signals (SAP_SSCOP) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: q2110_sigaa.c,v 1.6 1998/08/26 23:29:18 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static void sscop_resreq_ready __P((struct sscop *, int, int)); +static void sscop_resrsp_inresyn __P((struct sscop *, int, int)); +static void sscop_recrsp_recovrsp __P((struct sscop *, int, int)); +static void sscop_recrsp_inrecov __P((struct sscop *, int, int)); + + +/* + * Stack command state lookup tables + */ +/* SSCOP_INIT */ +static void (*sscop_init_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + sscop_init_inst, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_OUTRECOV */ + NULL, /* SOS_RECOVRSP */ + NULL, /* SOS_INRECOV */ + NULL, /* SOS_READY */ + NULL /* SOS_TERM */ +}; + +/* SSCOP_TERM */ +static void (*sscop_term_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + sscop_term_all, /* SOS_INST */ + sscop_term_all, /* SOS_IDLE */ + sscop_term_all, /* SOS_OUTCONN */ + sscop_term_all, /* SOS_INCONN */ + sscop_term_all, /* SOS_OUTDISC */ + sscop_term_all, /* SOS_OUTRESYN */ + sscop_term_all, /* SOS_INRESYN */ + sscop_term_all, /* SOS_OUTRECOV */ + sscop_term_all, /* SOS_RECOVRSP */ + sscop_term_all, /* SOS_INRECOV */ + sscop_term_all, /* SOS_READY */ + sscop_term_all /* SOS_TERM */ +}; + +/* SSCOP_ESTABLISH_REQ */ +static void (*sscop_estreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + sscop_estreq_idle, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + sscop_estreq_idle, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_OUTRECOV */ + NULL, /* SOS_RECOVRSP */ + NULL, /* SOS_INRECOV */ + NULL, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_ESTABLISH_RSP */ +static void (*sscop_estrsp_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + sscop_estrsp_inconn, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_OUTRECOV */ + NULL, /* SOS_RECOVRSP */ + NULL, /* SOS_INRECOV */ + NULL, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RELEASE_REQ */ +static void (*sscop_relreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + sscop_relreq_outconn, /* SOS_OUTCONN */ + sscop_relreq_inconn, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + sscop_relreq_outconn, /* SOS_OUTRESYN */ + sscop_relreq_outconn, /* SOS_INRESYN */ + sscop_relreq_ready, /* SOS_OUTRECOV */ + sscop_relreq_outconn, /* SOS_RECOVRSP */ + sscop_relreq_outconn, /* SOS_INRECOV */ + sscop_relreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_DATA_REQ */ +static void (*sscop_datreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + sscop_aa_noop_1, /* SOS_OUTRECOV */ + NULL, /* SOS_RECOVRSP */ + NULL, /* SOS_INRECOV */ + sscop_datreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RESYNC_REQ */ +static void (*sscop_resreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + sscop_resreq_ready, /* SOS_OUTRECOV */ + sscop_resreq_ready, /* SOS_RECOVRSP */ + sscop_resreq_ready, /* SOS_INRECOV */ + sscop_resreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RESYNC_RSP */ +static void (*sscop_resrsp_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + sscop_resrsp_inresyn, /* SOS_INRESYN */ + NULL, /* SOS_OUTRECOV */ + NULL, /* SOS_RECOVRSP */ + NULL, /* SOS_INRECOV */ + NULL, /* SOS_READY */ + sscop_aa_noop_0 /* SOS_TERM */ +}; + +/* SSCOP_RECOVER_RSP */ +static void (*sscop_recrsp_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_OUTRECOV */ + sscop_recrsp_recovrsp, /* SOS_RECOVRSP */ + sscop_recrsp_inrecov, /* SOS_INRECOV */ + NULL, /* SOS_READY */ + sscop_aa_noop_0 /* SOS_TERM */ +}; + +/* SSCOP_UNITDATA_REQ */ +static void (*sscop_udtreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + sscop_udtreq_all, /* SOS_IDLE */ + sscop_udtreq_all, /* SOS_OUTCONN */ + sscop_udtreq_all, /* SOS_INCONN */ + sscop_udtreq_all, /* SOS_OUTDISC */ + sscop_udtreq_all, /* SOS_OUTRESYN */ + sscop_udtreq_all, /* SOS_INRESYN */ + sscop_udtreq_all, /* SOS_OUTRECOV */ + sscop_udtreq_all, /* SOS_RECOVRSP */ + sscop_udtreq_all, /* SOS_INRECOV */ + sscop_udtreq_all, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RETRIEVE_REQ */ +static void (*sscop_retreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_OUTRECOV */ + NULL, /* SOS_RECOVRSP */ + NULL, /* SOS_INRECOV */ + NULL, /* SOS_READY */ + NULL /* SOS_TERM */ +}; + + +/* + * Stack command lookup table + */ +void (*(*sscop_q2110_aatab[SSCOP_CMD_SIZE])) + __P((struct sscop *, int, int)) = { + NULL, + sscop_init_tab, + sscop_term_tab, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sscop_estreq_tab, + NULL, + sscop_estrsp_tab, + NULL, + sscop_relreq_tab, + NULL, + NULL, + sscop_datreq_tab, + NULL, + sscop_resreq_tab, + NULL, + sscop_resrsp_tab, + NULL, + NULL, + sscop_recrsp_tab, + sscop_udtreq_tab, + NULL, + sscop_retreq_tab, + NULL, + NULL +}; + + +/* + * SSCOP_RESYNC_REQ / SOS_READY Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_resreq_ready(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Initialize receiver window + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + + /* + * Send first RS PDU + */ + sop->so_connctl = 1; + SEQ_INCR(sop->so_sendconn, 1); + (void) sscop_send_rs(sop); + + /* + * Drain transmit and receive queues + */ + sscop_xmit_drain(sop); + sscop_rcvr_drain(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for RSAK + */ + sop->so_state = SOS_OUTRESYN; + + return; +} + + +/* + * SSCOP_RESYNC_RSP / SOS_INRESYN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 unused + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_resrsp_inresyn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * Clear transmitter buffers + */ + q2110_clear_xmit(sop); + + /* + * Initialize state variables + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + q2110_init_state(sop); + + /* + * Send RSAK PDU + */ + (void) sscop_send_rsak(sop); + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * Back to data transfer state + */ + sop->so_state = SOS_READY; + + return; +} + + +/* + * SSCOP_RECOVER_RSP / SOS_RECOVRSP Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 unused + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_recrsp_recovrsp(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * Clear transmitter buffers, if not done earlier + */ + if (sop->so_flags & SOF_NOCLRBUF) + q2110_clear_xmit(sop); + + /* + * Initialize state variables + */ + q2110_init_state(sop); + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * Back to data transfer state + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * SSCOP_RECOVER_RSP / SOS_INRECOV Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 unused + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_recrsp_inrecov(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * Clear transmitter buffers, if not done earlier + */ + if (sop->so_flags & SOF_NOCLRBUF) + q2110_clear_xmit(sop); + + /* + * Initialize state variables + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + q2110_init_state(sop); + + /* + * Send ERAK PDU + */ + (void) sscop_send_erak(sop); + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * Back to data transfer state + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + diff --git a/sys/netatm/uni/q2110_sigcpcs.c b/sys/netatm/uni/q2110_sigcpcs.c new file mode 100644 index 0000000..0fa5555 --- /dev/null +++ b/sys/netatm/uni/q2110_sigcpcs.c @@ -0,0 +1,1760 @@ +/* + * + * =================================== + * 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: q2110_sigcpcs.c,v 1.7 1998/08/26 23:29:18 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * ITU-T Q.2110 - Process CPCS-signals (SSCOP PDUs) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: q2110_sigcpcs.c,v 1.7 1998/08/26 23:29:18 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static void sscop_bgn_outconn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_bgn_inconn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_bgn_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_bgrej_outrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_end_outrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_end_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_endak_outrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rs_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rs_inresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rs_outrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rs_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_er_error __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_er_idle __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_er_outrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_er_recovrsp __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_er_inrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_er_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_erak_error __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_erak_idle __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_erak_outrecov __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_sd_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_poll_ready __P((struct sscop *, KBuffer *, caddr_t)); + + +/* + * PDU type state lookup tables + */ +/* BGN PDU */ +static void (*sscop_bgn_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_bgn_idle, /* SOS_IDLE */ + sscop_bgn_outconn, /* SOS_OUTCONN */ + sscop_bgn_inconn, /* SOS_INCONN */ + sscop_bgn_outdisc, /* SOS_OUTDISC */ + sscop_bgn_outresyn, /* SOS_OUTRESYN */ + sscop_bgn_inresyn, /* SOS_INRESYN */ + sscop_bgn_inresyn, /* SOS_OUTRECOV */ + sscop_bgn_inresyn, /* SOS_RECOVRSP */ + sscop_bgn_inresyn, /* SOS_INRECOV */ + sscop_bgn_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* BGAK PDU */ +static void (*sscop_bgak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_bgak_idle, /* SOS_IDLE */ + sscop_bgak_outconn, /* SOS_OUTCONN */ + sscop_bgak_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_bgak_error, /* SOS_INRESYN */ + sscop_bgak_error, /* SOS_OUTRECOV */ + sscop_bgak_error, /* SOS_RECOVRSP */ + sscop_bgak_error, /* SOS_INRECOV */ + sscop_noop, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* BGREJ PDU */ +static void (*sscop_bgrej_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_bgrej_error, /* SOS_IDLE */ + sscop_bgrej_outconn, /* SOS_OUTCONN */ + sscop_bgrej_inconn, /* SOS_INCONN */ + sscop_endak_outdisc, /* SOS_OUTDISC */ + sscop_bgrej_outresyn, /* SOS_OUTRESYN */ + sscop_bgrej_inconn, /* SOS_INRESYN */ + sscop_bgrej_outrecov, /* SOS_OUTRECOV */ + sscop_bgrej_inconn, /* SOS_RECOVRSP */ + sscop_bgrej_inconn, /* SOS_INRECOV */ + sscop_bgrej_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* END PDU */ +static void (*sscop_end_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_end_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_end_inconn, /* SOS_INCONN */ + sscop_end_outdisc, /* SOS_OUTDISC */ + sscop_end_inconn, /* SOS_OUTRESYN */ + sscop_end_inconn, /* SOS_INRESYN */ + sscop_end_outrecov, /* SOS_OUTRECOV */ + sscop_end_inconn, /* SOS_RECOVRSP */ + sscop_end_inconn, /* SOS_INRECOV */ + sscop_end_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* ENDAK PDU */ +static void (*sscop_endak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_noop, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_endak_inconn, /* SOS_INCONN */ + sscop_endak_outdisc, /* SOS_OUTDISC */ + sscop_endak_inconn, /* SOS_OUTRESYN */ + sscop_endak_inconn, /* SOS_INRESYN */ + sscop_endak_outrecov, /* SOS_OUTRECOV */ + sscop_endak_inconn, /* SOS_RECOVRSP */ + sscop_endak_inconn, /* SOS_INRECOV */ + sscop_endak_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* RS PDU */ +static void (*sscop_rs_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_rs_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_rs_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_rs_outresyn, /* SOS_OUTRESYN */ + sscop_rs_inresyn, /* SOS_INRESYN */ + sscop_rs_outrecov, /* SOS_OUTRECOV */ + sscop_rs_outrecov, /* SOS_RECOVRSP */ + sscop_rs_outrecov, /* SOS_INRECOV */ + sscop_rs_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* RSAK PDU */ +static void (*sscop_rsak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_rsak_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_rsak_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_rsak_outresyn, /* SOS_OUTRESYN */ + sscop_rsak_error, /* SOS_INRESYN */ + sscop_rsak_error, /* SOS_OUTRECOV */ + sscop_rsak_error, /* SOS_RECOVRSP */ + sscop_rsak_error, /* SOS_INRECOV */ + sscop_noop, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* ER PDU */ +static void (*sscop_er_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_er_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_er_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_er_error, /* SOS_INRESYN */ + sscop_er_outrecov, /* SOS_OUTRECOV */ + sscop_er_recovrsp, /* SOS_RECOVRSP */ + sscop_er_inrecov, /* SOS_INRECOV */ + sscop_er_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* ERAK PDU */ +static void (*sscop_erak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_erak_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_erak_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_erak_error, /* SOS_INRESYN */ + sscop_erak_outrecov, /* SOS_OUTRECOV */ + sscop_noop, /* SOS_RECOVRSP */ + sscop_erak_error, /* SOS_INRECOV */ + sscop_noop, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* SD PDU */ +static void (*sscop_sd_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_sd_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_sd_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_sd_inconn, /* SOS_INRESYN */ + sscop_noop, /* SOS_OUTRECOV */ + sscop_noop, /* SOS_RECOVRSP */ + sscop_sd_inconn, /* SOS_INRECOV */ + sscop_sd_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* POLL PDU */ +static void (*sscop_poll_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_poll_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_poll_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_poll_inconn, /* SOS_INRESYN */ + sscop_noop, /* SOS_OUTRECOV */ + sscop_noop, /* SOS_RECOVRSP */ + sscop_poll_inconn, /* SOS_INRECOV */ + sscop_poll_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* STAT PDU */ +static void (*sscop_stat_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_stat_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_stat_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_stat_inconn, /* SOS_INRESYN */ + sscop_noop, /* SOS_OUTRECOV */ + sscop_stat_inconn, /* SOS_RECOVRSP */ + sscop_stat_inconn, /* SOS_INRECOV */ + sscop_stat_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* USTAT PDU */ +static void (*sscop_ustat_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_ustat_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_ustat_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_ustat_inconn, /* SOS_INRESYN */ + sscop_noop, /* SOS_OUTRECOV */ + sscop_ustat_inconn, /* SOS_RECOVRSP */ + sscop_ustat_inconn, /* SOS_INRECOV */ + sscop_ustat_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* UD PDU */ +static void (*sscop_ud_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_ud_all, /* SOS_IDLE */ + sscop_ud_all, /* SOS_OUTCONN */ + sscop_ud_all, /* SOS_INCONN */ + sscop_ud_all, /* SOS_OUTDISC */ + sscop_ud_all, /* SOS_OUTRESYN */ + sscop_ud_all, /* SOS_INRESYN */ + sscop_ud_all, /* SOS_OUTRECOV */ + sscop_ud_all, /* SOS_RECOVRSP */ + sscop_ud_all, /* SOS_INRECOV */ + sscop_ud_all, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* MD PDU */ +static void (*sscop_md_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_md_all, /* SOS_IDLE */ + sscop_md_all, /* SOS_OUTCONN */ + sscop_md_all, /* SOS_INCONN */ + sscop_md_all, /* SOS_OUTDISC */ + sscop_md_all, /* SOS_OUTRESYN */ + sscop_md_all, /* SOS_INRESYN */ + sscop_md_all, /* SOS_OUTRECOV */ + sscop_md_all, /* SOS_RECOVRSP */ + sscop_md_all, /* SOS_INRECOV */ + sscop_md_all, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + + +/* + * PDU type lookup table + */ +void (*(*sscop_q2110_pdutab[])) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, + sscop_bgn_tab, + sscop_bgak_tab, + sscop_end_tab, + sscop_endak_tab, + sscop_rs_tab, + sscop_rsak_tab, + sscop_bgrej_tab, + sscop_sd_tab, + sscop_er_tab, + sscop_poll_tab, + sscop_stat_tab, + sscop_ustat_tab, + sscop_ud_tab, + sscop_md_tab, + sscop_erak_tab +}; + + +/* + * BGN PDU / SOS_OUTCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_bgn_outconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err; + + /* + * If retransmitted BGN, ignore it + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + return; + } + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize state variables + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + q2110_init_state(sop); + + /* + * Return an ACK to peer + */ + (void) sscop_send_bgak(sop); + + /* + * Notify user of connection establishment + */ + STACK_CALL(SSCOP_ESTABLISH_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * OK, we're ready for data + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * BGN PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_bgn_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err; + + /* + * If retransmitted BGN, ignore it + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + return; + } + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + /* + * First, tell user current connection has been released + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Now, tell user of new connection establishment + */ + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + return; +} + + +/* + * BGN PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_bgn_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err; + + /* + * If retransmitted BGN, just ACK it again + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + (void) sscop_send_bgak(sop); + return; + } + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + /* + * Clear out appropriate queues + */ + q2110_prep_retrieve(sop); + + /* + * Tell user current connection has been released + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Tell user of incoming connection + */ + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user's response + */ + sop->so_state = SOS_INCONN; + + return; +} + + +/* + * BGREJ PDU / SOS_OUTRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_bgrej_outrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Report protocol error + */ + sscop_bgrej_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Clear receiver buffer + */ + sscop_rcvr_drain(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * END PDU / SOS_OUTRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_end_outrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct end_pdu *ep = (struct end_pdu *)trlr; + int err, source; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Get Source value + */ + if (ep->end_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Clear receiver buffer + */ + sscop_rcvr_drain(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * END PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_end_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct end_pdu *ep = (struct end_pdu *)trlr; + int err, source; + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Get Source value + */ + if (ep->end_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Clear out appropriate queues + */ + q2110_prep_retrieve(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * ENDAK PDU / SOS_OUTRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_endak_outrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Report protocol error + */ + sscop_endak_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Clear receiver buffer + */ + sscop_rcvr_drain(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * RS PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rs_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct rs_pdu *rp = (struct rs_pdu *)trlr; + int err; + + /* + * If retransmitted RS, ignore it + */ + if (sscop_is_rexmit(sop, rp->rs_nsq)) { + KB_FREEALL(m); + return; + } + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize state variables + */ + SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr)); + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + q2110_init_state(sop); + + /* + * Free PDU buffers + */ + KB_FREEALL(m); + + /* + * Return an ACK to peer + */ + (void) sscop_send_rsak(sop); + + /* + * Notify user of connection resynchronization + */ + STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * OK, we're ready for data + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * RS PDU / SOS_INRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rs_inresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct rs_pdu *rp = (struct rs_pdu *)trlr; + + /* + * If retransmitted RS, ignore it + */ + if (sscop_is_rexmit(sop, rp->rs_nsq)) { + KB_FREEALL(m); + return; + } + + /* + * Report error condition + */ + sscop_rs_error(sop, m, trlr); + + return; +} + + +/* + * RS PDU / SOS_OUTRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rs_outrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct rs_pdu *rp = (struct rs_pdu *)trlr; + int err; + + /* + * If retransmitted RS, report an error + */ + if (sscop_is_rexmit(sop, rp->rs_nsq)) { + sscop_rs_error(sop, m, trlr); + return; + } + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr)); + + /* + * Notify user of connection resynchronization + */ + STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Clear receiver buffer + */ + sscop_rcvr_drain(sop); + + /* + * Wait for user response + */ + sop->so_state = SOS_INRESYN; + + return; +} + + +/* + * RS PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rs_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct rs_pdu *rp = (struct rs_pdu *)trlr; + int err; + + /* + * If retransmitted RS, just ACK it + */ + if (sscop_is_rexmit(sop, rp->rs_nsq)) { + KB_FREEALL(m); + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + sscop_send_rsak(sop); + return; + } + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr)); + + /* + * Notify user of connection resynchronization + */ + STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Clear out appropriate queues + */ + q2110_prep_retrieve(sop); + + /* + * Wait for user response + */ + sop->so_state = SOS_INRESYN; + + return; +} + +/* + * ER PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_er_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'L'); + KB_FREEALL(m); + + return; +} + + +/* + * ER PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_er_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_er_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + return; +} + + +/* + * ER PDU / SOS_OUTRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_er_outrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct er_pdu *ep = (struct er_pdu *)trlr; + int err; + + /* + * If retransmitted ER, report an error + */ + if (sscop_is_rexmit(sop, ep->er_nsq)) { + sscop_er_error(sop, m, trlr); + return; + } + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(ep->er_nmr)); + + /* + * Initialize receiver window + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + + /* + * Free PDU buffers + */ + KB_FREEALL(m); + + /* + * Acknowledge ER + */ + (void) sscop_send_erak(sop); + + /* + * Deliver any outstanding data to user + */ + q2110_deliver_data(sop); + + /* + * Notify user of connection recovery + */ + STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user response + */ + sop->so_state = SOS_RECOVRSP; + + return; +} + + +/* + * ER PDU / SOS_RECOVRSP Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_er_recovrsp(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct er_pdu *ep = (struct er_pdu *)trlr; + + /* + * If retransmitted ER, just ACK it + */ + if (sscop_is_rexmit(sop, ep->er_nsq)) { + KB_FREEALL(m); + (void) sscop_send_erak(sop); + return; + } + + /* + * Report error condition + */ + sscop_er_error(sop, m, trlr); + + return; +} + + +/* + * ER PDU / SOS_INRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_er_inrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct er_pdu *ep = (struct er_pdu *)trlr; + + /* + * If retransmitted ER, just ignore it + */ + if (sscop_is_rexmit(sop, ep->er_nsq)) { + KB_FREEALL(m); + return; + } + + /* + * Report error condition + */ + sscop_er_error(sop, m, trlr); + + return; +} + + +/* + * ER PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_er_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct er_pdu *ep = (struct er_pdu *)trlr; + int err; + + /* + * If retransmitted ER, just ACK it + */ + if (sscop_is_rexmit(sop, ep->er_nsq)) { + KB_FREEALL(m); + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + sscop_send_erak(sop); + return; + } + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(ep->er_nmr)); + + /* + * Free PDU buffers + */ + KB_FREEALL(m); + + /* + * Clear out appropriate queues + */ + q2110_prep_recovery(sop); + + /* + * Deliver any outstanding data to user + */ + q2110_deliver_data(sop); + + /* + * Notify user of connection recovery + */ + STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user response + */ + sop->so_state = SOS_INRECOV; + + return; +} + + +/* + * ERAK PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_erak_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'M'); + KB_FREEALL(m); + + return; +} + + +/* + * ERAK PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_erak_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_erak_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + return; +} + + +/* + * ERAK PDU / SOS_OUTRECOV Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_erak_outrecov(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct erak_pdu *ep = (struct erak_pdu *)trlr; + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(ep->erak_nmr)); + + /* + * Free PDU buffers + */ + KB_FREEALL(m); + + /* + * Deliver any outstanding data to user + */ + q2110_deliver_data(sop); + + /* + * Notify user of connection recovery + */ + STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user response + */ + sop->so_state = SOS_RECOVRSP; + + return; +} + + +/* + * SD PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_sd_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct sd_pdu *sp = (struct sd_pdu *)trlr; + struct pdu_hdr *php; + KBuffer *n; + sscop_seq ns; + int err, space; + + /* + * Get PDU sequence number + */ + SEQ_SET(ns, ntohl(sp->sd_ns)); + + /* + * Ensure that the sequence number fits within the window + */ + if (SEQ_GEQ(ns, sop->so_rcvmax, sop->so_rcvnext)) { + /* + * It doesn't, drop received data + */ + KB_FREEALL(m); + + /* + * If next highest PDU hasn't reached window end yet, + * then send a USTAT to inform transmitter of this gap + */ + if (SEQ_LT(sop->so_rcvhigh, sop->so_rcvmax, sop->so_rcvnext)) { + (void) sscop_send_ustat(sop, sop->so_rcvmax); + sop->so_rcvhigh = sop->so_rcvmax; + } + return; + } + + /* + * If this is the next in-sequence PDU, hand it to user + */ + if (ns == sop->so_rcvnext) { + STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, ns, err); + if (err) { + KB_FREEALL(m); + return; + } + + /* + * Bump next expected sequence number + */ + SEQ_INCR(sop->so_rcvnext, 1); + + /* + * Slide receive window down + */ + SEQ_INCR(sop->so_rcvmax, 1); + + /* + * Is this the highest sequence PDU we've received?? + */ + if (ns == sop->so_rcvhigh) { + /* + * Yes, bump the limit and exit + */ + sop->so_rcvhigh = sop->so_rcvnext; + return; + } + + /* + * This is a retransmitted PDU, so see if we have + * more in-sequence PDUs already queued up + */ + while ((php = sop->so_recv_hd) && + (php->ph_ns == sop->so_rcvnext)) { + + /* + * Yup we do, so remove next PDU from queue and + * pass it up to the user as well + */ + sop->so_recv_hd = php->ph_recv_lk; + if (sop->so_recv_hd == NULL) + sop->so_recv_tl = NULL; + STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)php->ph_buf, php->ph_ns, + err); + if (err) { + /* + * Should never happen, but... + */ + KB_FREEALL(php->ph_buf); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Bump next expected sequence number + */ + SEQ_INCR(sop->so_rcvnext, 1); + + /* + * Slide receive window down + */ + SEQ_INCR(sop->so_rcvmax, 1); + } + + /* + * Finished with data delivery... + */ + return; + } + + /* + * We're gonna have to queue this PDU, so find space + * for the PDU header + */ + KB_HEADROOM(m, space); + + /* + * If there's not enough room in the received buffer, + * allocate & link a new buffer for the header + */ + if (space < sizeof(struct pdu_hdr)) { + + KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER); + if (n == NULL) { + KB_FREEALL(m); + return; + } + KB_HEADSET(n, sizeof(struct pdu_hdr)); + KB_LEN(n) = 0; + KB_LINKHEAD(n, m); + m = n; + } + + /* + * Build PDU header + * + * We can at least assume/require that the start of + * the user data is aligned. Also note that we don't + * include this header in the buffer len/offset fields. + */ + KB_DATASTART(m, php, struct pdu_hdr *); + php--; + php->ph_ns = ns; + php->ph_buf = m; + + /* + * Insert PDU into the receive queue + */ + if (sscop_recv_insert(sop, php)) { + /* + * Oops, a duplicate sequence number PDU is already on + * the queue, somethings wrong here. + */ + sscop_maa_error(sop, 'Q'); + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Go into recovery mode + */ + q2110_error_recovery(sop); + + return; + } + + /* + * Are we at the high-water mark?? + */ + if (ns == sop->so_rcvhigh) { + /* + * Yes, just bump the mark + */ + SEQ_INCR(sop->so_rcvhigh, 1); + + return; + } + + /* + * Are we beyond the high-water mark?? + */ + if (SEQ_GT(ns, sop->so_rcvhigh, sop->so_rcvnext)) { + /* + * Yes, then there's a missing PDU, so inform the transmitter + */ + (void) sscop_send_ustat(sop, ns); + + /* + * Update high-water mark + */ + sop->so_rcvhigh = SEQ_ADD(ns, 1); + } + + return; +} + + +/* + * POLL PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_poll_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct poll_pdu *pp = (struct poll_pdu *)trlr; + sscop_seq nps; + + NTOHL(pp->poll_ns); + + /* + * If the poll sequence number is less than highest number + * we've already seen, something's wrong + */ + if (SEQ_LT(pp->poll_ns, sop->so_rcvhigh, sop->so_rcvnext)) { + /* + * Record error condition + */ + sscop_maa_error(sop, 'Q'); + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Go into recovery mode + */ + q2110_error_recovery(sop); + + return; + } + + /* + * Set a new "next highest" sequence number expected + */ + if (SEQ_LT(pp->poll_ns, sop->so_rcvmax, sop->so_rcvnext)) + SEQ_SET(sop->so_rcvhigh, pp->poll_ns); + else + sop->so_rcvhigh = sop->so_rcvmax; + + /* + * Return a STAT PDU to peer + */ + SEQ_SET(nps, ntohl(pp->poll_nps)); + KB_FREEALL(m); + (void) sscop_send_stat(sop, nps); + + return; +} + diff --git a/sys/netatm/uni/q2110_subr.c b/sys/netatm/uni/q2110_subr.c new file mode 100644 index 0000000..4c6036b --- /dev/null +++ b/sys/netatm/uni/q2110_subr.c @@ -0,0 +1,239 @@ +/* + * + * =================================== + * 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: q2110_subr.c,v 1.1 1998/04/07 23:15:20 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * ITU-T Q.2110 - Subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: q2110_subr.c,v 1.1 1998/04/07 23:15:20 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Conditionally Clear Transmission Queues + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +q2110_clear_xmit(sop) + struct sscop *sop; +{ + /* + * Only clear queues if 'Clear Buffers' == No + */ + if (sop->so_flags & SOF_NOCLRBUF) + sscop_xmit_drain(sop); +} + + +/* + * Initialize Data Transfer State Variables + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +q2110_init_state(sop) + struct sscop *sop; +{ + /* + * Initialize for entry into Data Transfer Ready state + */ + sop->so_send = 0; + sop->so_pollsend = 0; + sop->so_ack = 0; + sop->so_pollack = 1; + sop->so_polldata = 0; + sop->so_rcvhigh = 0; + sop->so_rcvnext = 0; +} + + +/* + * Prepare Queues for Data Retrieval + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +q2110_prep_retrieve(sop) + struct sscop *sop; +{ + /* + * If 'Clear Buffers' == No, just clear retransmit queue, + * else clear all transmission queues + */ + if (sop->so_flags & SOF_NOCLRBUF) { + sop->so_rexmit_hd = NULL; + sop->so_rexmit_tl = NULL; + } else + sscop_xmit_drain(sop); + + /* + * Clear receiver queue + */ + sscop_rcvr_drain(sop); +} + + +/* + * Prepare Queues for Error Recovery + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +q2110_prep_recovery(sop) + struct sscop *sop; +{ + /* + * If 'Clear Buffers' == No, just clear retransmit queue, + * else clear all transmission queues + */ + if (sop->so_flags & SOF_NOCLRBUF) { + sop->so_rexmit_hd = NULL; + sop->so_rexmit_tl = NULL; + } else + sscop_xmit_drain(sop); +} + + +/* + * Conditionally Deliver Received Data to User + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +q2110_deliver_data(sop) + struct sscop *sop; +{ + /* + * If 'Clear Buffers' == No, give data to user + */ + if (sop->so_flags & SOF_NOCLRBUF) { + /* + * We don't support 'Clear Buffers' == No, so don't bother + */ + } + + /* + * Clear receiver queue + */ + sscop_rcvr_drain(sop); +} + + +/* + * Enter Connection Recovery Mode + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +q2110_error_recovery(sop) + struct sscop *sop; +{ + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Initialize receiver window + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + + /* + * Send first ER PDU + */ + sop->so_connctl = 1; + SEQ_INCR(sop->so_sendconn, 1); + (void) sscop_send_er(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Clear out appropriate queues + */ + q2110_prep_recovery(sop); + + /* + * Wait for ERAK + */ + sop->so_state = SOS_OUTRECOV; + + return; +} + diff --git a/sys/netatm/uni/qsaal1_sigaa.c b/sys/netatm/uni/qsaal1_sigaa.c new file mode 100644 index 0000000..f3b0097 --- /dev/null +++ b/sys/netatm/uni/qsaal1_sigaa.c @@ -0,0 +1,518 @@ +/* + * + * =================================== + * 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: qsaal1_sigaa.c,v 1.7 1998/08/26 23:29:18 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * ITU-T Q.SAAL1 - Process AA-signals (SAP_SSCOP) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: qsaal1_sigaa.c,v 1.7 1998/08/26 23:29:18 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static void sscop_estreq_ready __P((struct sscop *, int, int)); +static void sscop_datreq_outconn __P((struct sscop *, int, int)); +static void sscop_resreq_ready __P((struct sscop *, int, int)); +static void sscop_resrsp_inresyn __P((struct sscop *, int, int)); +static void sscop_resrsp_conresyn __P((struct sscop *, int, int)); + + +/* + * Stack command state lookup tables + */ +/* SSCOP_INIT */ +static void (*sscop_init_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + sscop_init_inst, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + NULL, /* SOS_READY */ + NULL /* SOS_TERM */ +}; + +/* SSCOP_TERM */ +static void (*sscop_term_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + sscop_term_all, /* SOS_INST */ + sscop_term_all, /* SOS_IDLE */ + sscop_term_all, /* SOS_OUTCONN */ + sscop_term_all, /* SOS_INCONN */ + sscop_term_all, /* SOS_OUTDISC */ + sscop_term_all, /* SOS_OUTRESYN */ + sscop_term_all, /* SOS_INRESYN */ + sscop_term_all, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_term_all, /* SOS_READY */ + sscop_term_all /* SOS_TERM */ +}; + +/* SSCOP_ESTABLISH_REQ */ +static void (*sscop_estreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + sscop_estreq_idle, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + sscop_estreq_ready, /* SOS_OUTDISC */ + sscop_estreq_ready, /* SOS_OUTRESYN */ + sscop_estreq_ready, /* SOS_INRESYN */ + sscop_estreq_ready, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_estreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_ESTABLISH_RSP */ +static void (*sscop_estrsp_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + sscop_estrsp_inconn, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_aa_noop_1, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RELEASE_REQ */ +static void (*sscop_relreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + sscop_relreq_outconn, /* SOS_OUTCONN */ + sscop_relreq_inconn, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + sscop_relreq_outconn, /* SOS_OUTRESYN */ + sscop_relreq_ready, /* SOS_INRESYN */ + sscop_relreq_outconn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_relreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_DATA_REQ */ +static void (*sscop_datreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + sscop_datreq_outconn, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + sscop_datreq_ready, /* SOS_INRESYN */ + NULL, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_datreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RESYNC_REQ */ +static void (*sscop_resreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + NULL, /* SOS_INRESYN */ + NULL, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_resreq_ready, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + +/* SSCOP_RESYNC_RSP */ +static void (*sscop_resrsp_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + NULL, /* SOS_IDLE */ + NULL, /* SOS_OUTCONN */ + NULL, /* SOS_INCONN */ + NULL, /* SOS_OUTDISC */ + NULL, /* SOS_OUTRESYN */ + sscop_resrsp_inresyn, /* SOS_INRESYN */ + sscop_resrsp_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + NULL, /* SOS_READY */ + sscop_aa_noop_0 /* SOS_TERM */ +}; + +/* SSCOP_UNITDATA_REQ */ +static void (*sscop_udtreq_tab[SOS_NUMSTATES]) + __P((struct sscop *, int, int)) = { + NULL, /* SOS_INST */ + sscop_udtreq_all, /* SOS_IDLE */ + sscop_udtreq_all, /* SOS_OUTCONN */ + sscop_udtreq_all, /* SOS_INCONN */ + sscop_udtreq_all, /* SOS_OUTDISC */ + sscop_udtreq_all, /* SOS_OUTRESYN */ + sscop_udtreq_all, /* SOS_INRESYN */ + sscop_udtreq_all, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_udtreq_all, /* SOS_READY */ + sscop_aa_noop_1 /* SOS_TERM */ +}; + + +/* + * Stack command lookup table + */ +void (*(*sscop_qsaal_aatab[SSCOP_CMD_SIZE])) + __P((struct sscop *, int, int)) = { + NULL, + sscop_init_tab, + sscop_term_tab, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sscop_estreq_tab, + NULL, + sscop_estrsp_tab, + NULL, + sscop_relreq_tab, + NULL, + NULL, + sscop_datreq_tab, + NULL, + sscop_resreq_tab, + NULL, + sscop_resrsp_tab, + NULL, + NULL, + NULL, + sscop_udtreq_tab, + NULL, + NULL, + NULL, + NULL +}; + + +/* + * SSCOP_ESTABLISH_REQ / SOS_READY Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 buffer release parameter + * + * Returns: + * none + * + */ +static void +sscop_estreq_ready(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * We currently only support BR=YES + */ + if (arg2 != SSCOP_BR_YES) { + sscop_abort(sop, "sscop: BR != YES\n"); + return; + } + + /* + * Stop poll timer + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Stop lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = 0; + + /* + * Initialize receiver window + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + + /* + * Send first BGN PDU + */ + sop->so_connctl = 1; + (void) sscop_send_bgn(sop, SSCOP_SOURCE_USER); + + /* + * Reset transmitter state + */ + qsaal1_reset_xmit(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for BGAK + */ + sop->so_state = SOS_OUTCONN; + + return; +} + + +/* + * SSCOP_DATA_REQ / SOS_OUTCONN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing assured user data + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_datreq_outconn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + KBuffer *m = (KBuffer *)arg1; + + /* + * We must have a buffer (even if it contains no data) + */ + if (m == NULL) { + sscop_abort(sop, "sscop_datreq_outconn: no buffer\n"); + return; + } + + /* + * Only accept data here if in the middle of an SSCOP-initiated + * session reestablishment + */ + if ((sop->so_flags & SOF_REESTAB) == 0) { + KB_FREEALL(m); + sscop_abort(sop, "sscop_datreq_outconn: data not allowed\n"); + return; + } + + /* + * Place data at end of transmission queue + */ + KB_QNEXT(m) = NULL; + if (sop->so_xmit_hd == NULL) + sop->so_xmit_hd = m; + else + KB_QNEXT(sop->so_xmit_tl) = m; + sop->so_xmit_tl = m; + + /* + * Note that the transmit queues need to be serviced + */ + sop->so_flags |= SOF_XMITSRVC; + + return; +} + + +/* + * SSCOP_RESYNC_REQ / SOS_READY Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_resreq_ready(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Stop poll timer + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Stop lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = 0; + + /* + * Send first RS PDU + */ + sop->so_connctl = 1; + (void) sscop_send_rs(sop); + + /* + * Reset transmitter state + */ + qsaal1_reset_xmit(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for RSAK + */ + sop->so_state = SOS_OUTRESYN; + + return; +} + + +/* + * SSCOP_RESYNC_RSP / SOS_INRESYN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 unused + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_resrsp_inresyn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * Send RSAK PDU + */ + (void) sscop_send_rsak(sop); + + /* + * Back to data transfer state + */ + sop->so_state = SOS_READY; + + return; +} + + +/* + * SSCOP_RESYNC_RSP / SOS_CONRESYN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 unused + * arg2 unused + * + * Returns: + * none + * + */ +static void +sscop_resrsp_conresyn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * Send RSAK PDU + */ + (void) sscop_send_rsak(sop); + + /* + * Back to waiting for peer's RSAK + */ + sop->so_state = SOS_OUTRESYN; + + return; +} + diff --git a/sys/netatm/uni/qsaal1_sigcpcs.c b/sys/netatm/uni/qsaal1_sigcpcs.c new file mode 100644 index 0000000..1d62165 --- /dev/null +++ b/sys/netatm/uni/qsaal1_sigcpcs.c @@ -0,0 +1,1545 @@ +/* + * + * =================================== + * 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: qsaal1_sigcpcs.c,v 1.7 1998/04/07 23:21:03 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * ITU-T Q.SAAL1 - Process CPCS-signals (SSCOP PDUs) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: qsaal1_sigcpcs.c,v 1.7 1998/04/07 23:21:03 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static void sscop_bgn_outconn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_end_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_end_conresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_end_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_endak_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rs_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rs_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_rsak_conresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_sd_inresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_sd_conresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_sd_process __P((struct sscop *, KBuffer *, caddr_t, int)); +static void sscop_sd_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_sdp_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_poll_inresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_poll_conresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_poll_ready __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_stat_conresyn __P((struct sscop *, KBuffer *, caddr_t)); +static void sscop_ustat_conresyn __P((struct sscop *, KBuffer *, caddr_t)); + + +/* + * PDU type state lookup tables + */ +/* BGN PDU */ +static void (*sscop_bgn_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_bgn_idle, /* SOS_IDLE */ + sscop_bgn_outconn, /* SOS_OUTCONN */ + sscop_noop, /* SOS_INCONN */ + sscop_bgn_outdisc, /* SOS_OUTDISC */ + sscop_bgn_outresyn, /* SOS_OUTRESYN */ + sscop_bgn_inresyn, /* SOS_INRESYN */ + sscop_bgn_outresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_bgn_inresyn, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* BGAK PDU */ +static void (*sscop_bgak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_bgak_idle, /* SOS_IDLE */ + sscop_bgak_outconn, /* SOS_OUTCONN */ + sscop_bgak_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_bgak_error, /* SOS_OUTRESYN */ + sscop_bgak_error, /* SOS_INRESYN */ + sscop_bgak_error, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_noop, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* BGREJ PDU */ +static void (*sscop_bgrej_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_bgrej_error, /* SOS_IDLE */ + sscop_bgrej_outconn, /* SOS_OUTCONN */ + sscop_bgrej_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_bgrej_outresyn, /* SOS_OUTRESYN */ + sscop_bgrej_ready, /* SOS_INRESYN */ + sscop_bgrej_outresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_bgrej_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* END PDU */ +static void (*sscop_end_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_end_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_end_inconn, /* SOS_INCONN */ + sscop_end_outdisc, /* SOS_OUTDISC */ + sscop_end_outresyn, /* SOS_OUTRESYN */ + sscop_end_ready, /* SOS_INRESYN */ + sscop_end_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_end_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* ENDAK PDU */ +static void (*sscop_endak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_noop, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_endak_inconn, /* SOS_INCONN */ + sscop_endak_outdisc, /* SOS_OUTDISC */ + sscop_endak_outresyn, /* SOS_OUTRESYN */ + sscop_endak_ready, /* SOS_INRESYN */ + sscop_endak_outresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_endak_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* RS PDU */ +static void (*sscop_rs_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_rs_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_rs_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_rs_outresyn, /* SOS_OUTRESYN */ + sscop_noop, /* SOS_INRESYN */ + sscop_noop, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_rs_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* RSAK PDU */ +static void (*sscop_rsak_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_rsak_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_rsak_error, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_rsak_outresyn, /* SOS_OUTRESYN */ + sscop_rsak_error, /* SOS_INRESYN */ + sscop_rsak_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_rsak_error, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* SD PDU */ +static void (*sscop_sd_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_sd_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_sd_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_sd_ready, /* SOS_OUTRESYN */ + sscop_sd_inresyn, /* SOS_INRESYN */ + sscop_sd_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_sd_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* SDP PDU */ +static void (*sscop_sdp_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_sd_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_sd_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_sdp_ready, /* SOS_OUTRESYN */ + sscop_sd_inresyn, /* SOS_INRESYN */ + sscop_sd_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_sdp_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* POLL PDU */ +static void (*sscop_poll_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_poll_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_poll_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_poll_ready, /* SOS_OUTRESYN */ + sscop_poll_inresyn, /* SOS_INRESYN */ + sscop_poll_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_poll_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* STAT PDU */ +static void (*sscop_stat_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_stat_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_stat_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_stat_ready, /* SOS_INRESYN */ + sscop_stat_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_stat_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* USTAT PDU */ +static void (*sscop_ustat_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_ustat_idle, /* SOS_IDLE */ + sscop_noop, /* SOS_OUTCONN */ + sscop_ustat_inconn, /* SOS_INCONN */ + sscop_noop, /* SOS_OUTDISC */ + sscop_noop, /* SOS_OUTRESYN */ + sscop_ustat_ready, /* SOS_INRESYN */ + sscop_ustat_conresyn, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_ustat_ready, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* UD PDU */ +static void (*sscop_ud_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_ud_all, /* SOS_IDLE */ + sscop_ud_all, /* SOS_OUTCONN */ + sscop_ud_all, /* SOS_INCONN */ + sscop_ud_all, /* SOS_OUTDISC */ + sscop_ud_all, /* SOS_OUTRESYN */ + sscop_ud_all, /* SOS_INRESYN */ + sscop_ud_all, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_ud_all, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + +/* MD PDU */ +static void (*sscop_md_tab[SOS_NUMSTATES]) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, /* SOS_INST */ + sscop_md_all, /* SOS_IDLE */ + sscop_md_all, /* SOS_OUTCONN */ + sscop_md_all, /* SOS_INCONN */ + sscop_md_all, /* SOS_OUTDISC */ + sscop_md_all, /* SOS_OUTRESYN */ + sscop_md_all, /* SOS_INRESYN */ + sscop_md_all, /* SOS_CONRESYN */ + NULL, /* invalid */ + NULL, /* invalid */ + sscop_md_all, /* SOS_READY */ + sscop_noop /* SOS_TERM */ +}; + + +/* + * PDU type lookup table + */ +void (*(*sscop_qsaal_pdutab[])) + __P((struct sscop *, KBuffer *, caddr_t)) = { + NULL, + sscop_bgn_tab, + sscop_bgak_tab, + sscop_end_tab, + sscop_endak_tab, + sscop_rs_tab, + sscop_rsak_tab, + sscop_bgrej_tab, + sscop_sd_tab, + sscop_sdp_tab, + sscop_poll_tab, + sscop_stat_tab, + sscop_ustat_tab, + sscop_ud_tab, + sscop_md_tab, + NULL +}; + + +/* + * BGN PDU / SOS_OUTCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_bgn_outconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + /* + * Notify user of connection establishment + */ + if (sop->so_flags & SOF_REESTAB) { + KB_FREEALL(m); + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_bgn_outconn: stack memory\n"); + return; + } + sop->so_flags &= ~SOF_REESTAB; + } else { + STACK_CALL(SSCOP_ESTABLISH_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "sscop_bgn_outconn: stack memory\n"); + return; + } + } + + /* + * Return an ACK to peer + */ + (void) sscop_send_bgak(sop); + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Reset receiver variables + */ + qsaal1_reset_rcvr(sop); + + /* + * Start polling timer + */ + sscop_set_poll(sop); + + /* + * Start lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * OK, we're ready for data + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * END PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_end_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct end_pdu *ep = (struct end_pdu *)trlr; + int err, source; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Get Source value + */ + if (ep->end_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "sscop_end_outresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * END PDU / SOS_CONRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_end_conresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Free up buffers + */ + KB_FREEALL(m); + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_end_conresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * END PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_end_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct end_pdu *ep = (struct end_pdu *)trlr; + int err, source; + + /* + * Stop poll timer + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Stop lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = 0; + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Get Source value + */ + if (ep->end_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "sscop_end_ready: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * ENDAK PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_endak_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Report protocol error + */ + sscop_endak_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_endak_outresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * RS PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rs_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Notify user of resynchronization + */ + STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "sscop_rs_outresyn: stack memory\n"); + return; + } + + /* + * Reset receiver state variables + */ + qsaal1_reset_rcvr(sop); + + /* + * Wait for both peer and user responses + */ + sop->so_state = SOS_CONRESYN; + + return; +} + + +/* + * RS PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rs_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Notify user of resynchronization + */ + STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "sscop_rs_ready: stack memory\n"); + return; + } + + /* + * Reset receiver state variables + */ + qsaal1_reset_rcvr(sop); + + /* + * Wait for user response + */ + sop->so_state = SOS_INRESYN; + + return; +} + + +/* + * RSAK PDU / SOS_CONRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_rsak_conresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Notify user of resynchronization completion + */ + STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "sscop_rsak_conresyn: stack memory\n"); + return; + } + + /* + * Start the polling timer + */ + sscop_set_poll(sop); + + /* + * Start lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * Continue waiting for user response + */ + sop->so_state = SOS_INRESYN; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * SD PDU / SOS_INRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_sd_inresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop poll timer + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Stop lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = 0; + + /* + * Record error condition + */ + sscop_sd_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_sd_inresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * SD PDU / SOS_CONRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_sd_conresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Record error condition + */ + sscop_sd_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_sd_conresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * SD/SDP PDU Common Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU user data buffer chain + * trlr pointer to PDU trailer + * type PDU type (SD or SDP) + * + * Returns: + * none + * + */ +static void +sscop_sd_process(sop, m, trlr, type) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; + int type; +{ + struct sd_pdu *sp; + struct sdp_pdu *spp; + struct poll_pdu poll; + struct pdu_hdr *php; + KBuffer *n; + sscop_seq ns, nps; + int err, space; + + /* + * Get PDU sequence number(s) + */ + if (type == PT_SD) { + sp = (struct sd_pdu *)trlr; + SEQ_SET(ns, ntohl(sp->sd_ns)); + SEQ_SET(nps, 0); + } else { + spp = (struct sdp_pdu *)trlr; + SEQ_SET(ns, ntohl(spp->sdp_ns)); + SEQ_SET(nps, ntohl(spp->sdp_nps)); + } + + /* + * Ensure that the sequence number fits within the window + */ + if (SEQ_GEQ(ns, sop->so_rcvmax, sop->so_rcvnext)) { + KB_FREEALL(m); + return; + } + + /* + * If this is the next in-sequence PDU, hand it to user + */ + if (ns == sop->so_rcvnext) { + STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, ns, err); + if (err) { + KB_FREEALL(m); + return; + } + + /* + * Bump next expected sequence number + */ + SEQ_INCR(sop->so_rcvnext, 1); + + /* + * Slide receive window down + */ + SEQ_INCR(sop->so_rcvmax, 1); + + /* + * Is this the highest sequence PDU we've received?? + */ + if (ns == sop->so_rcvhigh) { + /* + * Yes, bump the limit and exit + */ + sop->so_rcvhigh = sop->so_rcvnext; + if (type == PT_SDP) + goto dopoll; + return; + } + + /* + * This is a retransmitted PDU, so see if we have + * more in-sequence PDUs already queued up + */ + while ((php = sop->so_recv_hd) && + (php->ph_ns == sop->so_rcvnext)) { + + /* + * Yup we do, so remove next PDU from queue and + * pass it up to the user as well + */ + sop->so_recv_hd = php->ph_recv_lk; + if (sop->so_recv_hd == NULL) + sop->so_recv_tl = NULL; + STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)php->ph_buf, php->ph_ns, + err); + if (err) { + /* + * Should never happen, but... + */ + KB_FREEALL(php->ph_buf); + sscop_abort(sop, + "sscop_sd_process: stack memory\n"); + return; + } + + /* + * Bump next expected sequence number + */ + SEQ_INCR(sop->so_rcvnext, 1); + + /* + * Slide receive window down + */ + SEQ_INCR(sop->so_rcvmax, 1); + } + + /* + * Finished with data...see if we need to poll + */ + if (type == PT_SDP) + goto dopoll; + return; + } + + /* + * We're gonna have to queue this PDU, so find space + * for the PDU header + */ + KB_HEADROOM(m, space); + + /* + * If there's not enough room in the received buffer, + * allocate & link a new buffer for the header + */ + if (space < sizeof(struct pdu_hdr)) { + + KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER); + if (n == NULL) { + KB_FREEALL(m); + return; + } + KB_HEADSET(n, sizeof(struct pdu_hdr)); + KB_LEN(n) = 0; + KB_LINKHEAD(n, m); + m = n; + } + + /* + * Build PDU header + * + * We can at least assume/require that the start of + * the user data is aligned. Also note that we don't + * include this header in the buffer len/offset fields. + */ + KB_DATASTART(m, php, struct pdu_hdr *); + php--; + php->ph_ns = ns; + php->ph_buf = m; + + /* + * Insert PDU into the receive queue + */ + if (sscop_recv_insert(sop, php)) { + /* + * Oops, a duplicate sequence number PDU is already on + * the queue, somethings wrong here. + */ + sscop_maa_error(sop, 'Q'); + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Reestablish a new connection + */ + qsaal1_reestablish(sop); + + return; + } + + /* + * Are we at the high-water mark?? + */ + if (ns == sop->so_rcvhigh) { + /* + * Yes, just bump the mark + */ + SEQ_INCR(sop->so_rcvhigh, 1); + + if (type == PT_SDP) + goto dopoll; + return; + } + + /* + * Are we beyond the high-water mark?? + */ + if (SEQ_GT(ns, sop->so_rcvhigh, sop->so_rcvnext)) { + /* + * Yes, then there's a missing PDU, so inform the transmitter + */ + if (type == PT_SD) + (void) sscop_send_ustat(sop, ns); + + /* + * Update high-water mark + */ + sop->so_rcvhigh = SEQ_ADD(ns, 1); + } + + if (type == PT_SD) + return; + +dopoll: + /* + * Do the "poll" part of an SDP PDU + */ + poll.poll_nps = htonl(nps); + poll.poll_ns = htonl((PT_POLL << PT_TYPE_SHIFT) | ns); + sscop_poll_ready(sop, NULL, (caddr_t)&poll); + return; +} + + +/* + * SD PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_sd_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + /* + * Just call common SD/SDP processor + */ + sscop_sd_process(sop, m, trlr, PT_SD); + + return; +} + + +/* + * SDP PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_sdp_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + /* + * Just call common SD/SDP processor + */ + sscop_sd_process(sop, m, trlr, PT_SDP); + + return; +} + + +/* + * POLL PDU / SOS_INRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_poll_inresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop poll timer + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Stop lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = 0; + + /* + * Report protocol error + */ + sscop_poll_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_poll_inresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * POLL PDU / SOS_CONRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_poll_conresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Record error condition + */ + sscop_poll_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_poll_conresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * POLL PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_poll_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct poll_pdu *pp = (struct poll_pdu *)trlr; + sscop_seq nps; + + NTOHL(pp->poll_ns); + + /* + * If the poll sequence number is less than highest number + * we've already seen, something's wrong - so attempt to + * reestablish a new connection. + */ + if (SEQ_LT(pp->poll_ns, sop->so_rcvhigh, sop->so_rcvnext)) { + /* + * Record error condition + */ + sscop_maa_error(sop, 'Q'); + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Reestablish a new connection + */ + qsaal1_reestablish(sop); + + return; + } + + /* + * Set a new "next highest" sequence number expected + */ + if (SEQ_LT(pp->poll_ns, sop->so_rcvmax, sop->so_rcvnext)) + SEQ_SET(sop->so_rcvhigh, pp->poll_ns); + else + sop->so_rcvhigh = sop->so_rcvmax; + + /* + * Return a STAT PDU to peer + */ + SEQ_SET(nps, ntohl(pp->poll_nps)); + KB_FREEALL(m); + (void) sscop_send_stat(sop, nps); + + return; +} + + +/* + * STAT PDU / SOS_CONRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_stat_conresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Record error condition + */ + sscop_stat_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_stat_conresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * USTAT PDU / SOS_CONRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +static void +sscop_ustat_conresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Record error condition + */ + sscop_ustat_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "sscop_ustat_conresyn: stack memory\n"); + return; + } + + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + diff --git a/sys/netatm/uni/qsaal1_subr.c b/sys/netatm/uni/qsaal1_subr.c new file mode 100644 index 0000000..ed4b43c --- /dev/null +++ b/sys/netatm/uni/qsaal1_subr.c @@ -0,0 +1,206 @@ +/* + * + * =================================== + * 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: qsaal1_subr.c,v 1.6 1998/04/07 23:21:17 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * ITU-T Q.SAAL1 - Subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: qsaal1_subr.c,v 1.6 1998/04/07 23:21:17 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Re-establish a new SSCOP Connection + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +qsaal1_reestablish(sop) + struct sscop *sop; +{ + + /* + * Stop polling timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Note that we're reestablishing a connection + */ + sop->so_flags |= SOF_REESTAB; + + /* + * Send first BGN PDU + */ + sop->so_connctl = 1; + (void) sscop_send_bgn(sop, SSCOP_SOURCE_SSCOP); + + /* + * Reset transmit variables + */ + qsaal1_reset_xmit(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for BGAK + */ + sop->so_state = SOS_OUTCONN; + + return; +} + + +/* + * Reset connection's transmitter state + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +qsaal1_reset_xmit(sop) + struct sscop *sop; +{ + + /* + * Drain the transmission queues + */ + sscop_xmit_drain(sop); + + /* + * Reset transmit variables + */ + SEQ_SET(sop->so_send, 0); + SEQ_SET(sop->so_pollsend, 0); + SEQ_SET(sop->so_ack, 0); + SEQ_SET(sop->so_pollack, 0); + if (sop->so_state != SOS_INCONN) + SEQ_SET(sop->so_sendmax, 0); + sop->so_polldata = 0; + + return; +} + + +/* + * Reset connection's receiver state + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +qsaal1_reset_rcvr(sop) + struct sscop *sop; +{ + + /* + * Drain the receiver queues + */ + sscop_rcvr_drain(sop); + + /* + * Reset transmit variables + */ + SEQ_SET(sop->so_rcvnext, 0); + SEQ_SET(sop->so_rcvhigh, 0); + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + + return; +} + + +/* + * Clear connection's connection data + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +qsaal1_clear_connection(sop) + struct sscop *sop; +{ + + /* + * Can we clear transmit buffers ?? + */ + if ((sop->so_flags & SOF_NOCLRBUF) == 0) { + /* + * Yes, drain the transmission queues + */ + sscop_xmit_drain(sop); + } + + /* + * Clear service required flag + */ + sop->so_flags &= ~SOF_XMITSRVC; + + /* + * Drain receive queue buffers + */ + sscop_rcvr_drain(sop); + + return; +} + diff --git a/sys/netatm/uni/sscf_uni.c b/sys/netatm/uni/sscf_uni.c new file mode 100644 index 0000000..b5fd7fb --- /dev/null +++ b/sys/netatm/uni/sscf_uni.c @@ -0,0 +1,317 @@ +/* + * + * =================================== + * 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: sscf_uni.c,v 1.6 1998/03/24 21:10:38 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * Signalling AAL SSCF at the UNI + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscf_uni.c,v 1.6 1998/03/24 21:10:38 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscf_uni.h> +#include <netatm/uni/sscf_uni_var.h> + + +/* + * Global variables + */ +int sscf_uni_vccnt = 0; + +/* + * Local functions + */ +static int sscf_uni_inst __P((struct stack_defn **, Atm_connvc *)); + +/* + * Local variables + */ +static struct sp_info sscf_uni_pool = { + "sscf uni pool", /* si_name */ + sizeof(struct univcc), /* si_blksiz */ + 5, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + +static struct stack_defn sscf_uni_service = { + NULL, + SAP_SSCF_UNI, + 0, + sscf_uni_inst, + sscf_uni_lower, + sscf_uni_upper, + 0 +}; + +static struct t_atm_cause sscf_uni_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_TEMPORARY_FAILURE, + {0, 0, 0, 0} +}; + + +/* + * Initialize SSCF UNI processing + * + * This will be called during module loading. We will register our stack + * service and wait for someone to talk to us. + * + * Arguments: + * none + * + * Returns: + * 0 initialization was successful + * errno initialization failed - reason indicated + * + */ +int +sscf_uni_start() +{ + int err = 0; + + /* + * Register stack service + */ + if (err = atm_stack_register(&sscf_uni_service)) + goto done; + +done: + return (err); +} + + +/* + * Terminate SSCF UNI processing + * + * This will be called just prior to unloading the module from memory. All + * signalling instances should have been terminated by now, so we just free + * up all of our resources. + * + * Called at splnet. + * + * Arguments: + * none + * + * Returns: + * 0 termination was successful + * errno termination failed - reason indicated + * + */ +int +sscf_uni_stop() +{ + /* + * Any connections still exist?? + */ + if (sscf_uni_vccnt) { + + /* + * Yes, can't stop yet + */ + return (EBUSY); + } + + /* + * Deregister the stack service + */ + (void) atm_stack_deregister(&sscf_uni_service); + + /* + * Free our storage pools + */ + atm_release_pool(&sscf_uni_pool); + + return (0); +} + + +/* + * SSCF_UNI Stack Instantiation + * + * Called at splnet. + * + * Arguments: + * ssp pointer to array of stack definition pointers for connection + * ssp[0] points to upper layer's stack service definition + * ssp[1] points to this layer's stack service definition + * ssp[2] points to lower layer's stack service definition + * cvp pointer to connection vcc for this stack + * + * Returns: + * 0 instantiation successful + * errno instantiation failed - reason indicated + * + */ +static int +sscf_uni_inst(ssp, cvp) + struct stack_defn **ssp; + Atm_connvc *cvp; +{ + struct stack_defn *sdp_up = ssp[0], + *sdp_me = ssp[1], + *sdp_low = ssp[2]; + struct univcc *uvp; + int err; + + ATM_DEBUG2("sscf_uni_inst: ssp=0x%x, cvp=0x%x\n", ssp, cvp); + + /* + * Validate lower SAP + */ + if (sdp_low->sd_sap != SAP_SSCOP) + return (EINVAL); + + /* + * Allocate our control block + */ + uvp = (struct univcc *)atm_allocate(&sscf_uni_pool); + if (uvp == NULL) + return (ENOMEM); + + uvp->uv_ustate = UVU_INST; + uvp->uv_lstate = UVL_INST; + uvp->uv_connvc = cvp; + uvp->uv_toku = sdp_up->sd_toku; + uvp->uv_upper = sdp_up->sd_upper; + sscf_uni_vccnt++; + + /* + * Store my token into service definition + */ + sdp_me->sd_toku = uvp; + + /* + * Update and save input buffer headroom + */ + HEADIN(cvp, 0, 0); + /* uvp->uv_headin = cvp->cvc_attr.headin; */ + + /* + * Pass instantiation down the stack + */ + err = sdp_low->sd_inst(ssp + 1, cvp); + if (err) { + /* + * Lower layer instantiation failed, free our resources + */ + atm_free((caddr_t)uvp); + sscf_uni_vccnt--; + return (err); + } + + /* + * Save and update output buffer headroom + */ + /* uvp->uv_headout = cvp->cvc_attr.headout; */ + HEADOUT(cvp, 0, 0); + + /* + * Save lower layer's interface info + */ + uvp->uv_lower = sdp_low->sd_lower; + uvp->uv_tokl = sdp_low->sd_toku; + + return (0); +} + + +/* + * Abort an SSCF_UNI connection + * + * Called when an unrecoverable or "should never happen" error occurs. + * We just log a message and request the signalling manager to abort the + * connection. + * + * Arguments: + * uvp pointer to univcc control block + * msg pointer to error message + * + * Returns: + * none + * + */ +void +sscf_uni_abort(uvp, msg) + struct univcc *uvp; + char *msg; +{ + /* + * Log error message + */ + log(LOG_ERR, msg); + + /* + * Set termination states + */ + uvp->uv_ustate = UVU_TERM; + uvp->uv_lstate = UVL_TERM; + + /* + * Tell Connection Manager to abort this connection + */ + (void) atm_cm_abort(uvp->uv_connvc, &sscf_uni_cause); +} + + +/* + * Print an SSCF PDU + * + * Arguments: + * uvp pointer to univcc control block + * m pointer to pdu buffer chain + * msg pointer to message string + * + * Returns: + * none + * + */ +void +sscf_uni_pdu_print(uvp, m, msg) + struct univcc *uvp; + KBuffer *m; + char *msg; +{ + char buf[128]; + struct vccb *vcp; + + vcp = uvp->uv_connvc->cvc_vcc; + sprintf(buf, "sscf_uni %s: vcc=(%d,%d)\n", + msg, vcp->vc_vpi, vcp->vc_vci); + atm_pdu_print(m, buf); +} + diff --git a/sys/netatm/uni/sscf_uni.h b/sys/netatm/uni/sscf_uni.h new file mode 100644 index 0000000..cf24446 --- /dev/null +++ b/sys/netatm/uni/sscf_uni.h @@ -0,0 +1,47 @@ +/* + * + * =================================== + * 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: sscf_uni.h,v 1.2 1997/05/06 22:19:34 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCF UNI protocol definitions + * + */ + +#ifndef _UNI_SSCF_UNI_H +#define _UNI_SSCF_UNI_H + +/* + * SSCF_UNI API definitions + */ +#define SSCF_UNI_ESTIND_YES 1 /* Allow new ESTABLISH_IND */ +#define SSCF_UNI_ESTIND_NO 2 /* Disallow new ESTABLISH_IND */ + +#endif /* _UNI_SSCF_UNI_H */ diff --git a/sys/netatm/uni/sscf_uni_lower.c b/sys/netatm/uni/sscf_uni_lower.c new file mode 100644 index 0000000..fe2d839 --- /dev/null +++ b/sys/netatm/uni/sscf_uni_lower.c @@ -0,0 +1,379 @@ +/* + * + * =================================== + * 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: sscf_uni_lower.c,v 1.6 1998/04/07 23:23:26 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCF UNI - SSCF_UNI SAP interface processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscf_uni_lower.c,v 1.6 1998/04/07 23:23:26 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscf_uni.h> +#include <netatm/uni/sscf_uni_var.h> + + +/* + * Local variables + */ +static struct sscop_parms sscf_uni_sscop_parms = { + 4096, /* sp_maxinfo */ + 4096, /* sp_maxuu */ + 4, /* sp_maxcc */ + 25, /* sp_maxpd */ + 1 * ATM_HZ, /* sp_timecc */ + 2 * ATM_HZ, /* sp_timekeep */ + 7 * ATM_HZ, /* sp_timeresp */ + 1 * ATM_HZ, /* sp_timepoll */ + 15 * ATM_HZ, /* sp_timeidle */ + 80 /* sp_rcvwin */ +}; + + +/* + * SSCF_UNI Lower Stack Command Handler + * + * This function will receive all of the stack commands issued from the + * layer above SSCF UNI (ie. Q.2931). + * + * Arguments: + * cmd stack command code + * tok session token + * arg1 command specific argument + * arg2 command specific argument + * + * Returns: + * none + * + */ +void +sscf_uni_lower(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + struct univcc *uvp = (struct univcc *)tok; + Atm_connvc *cvp = uvp->uv_connvc; + enum sscop_vers vers; + int err; + + ATM_DEBUG5("sscf_uni_lower: cmd=0x%x, uvp=0x%x, ustate=%d, arg1=0x%x, arg2=0x%x\n", + cmd, (int)uvp, uvp->uv_ustate, arg1, arg2); + + switch (cmd) { + + case SSCF_UNI_INIT: + /* + * Validate state + */ + if (uvp->uv_ustate != UVU_INST) { + log(LOG_ERR, "sscf_uni_lower: SSCF_INIT in ustate=%d\n", + uvp->uv_ustate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + break; + } + + /* + * Validate UNI version + */ + if ((enum uni_vers)arg1 == UNI_VERS_3_0) + vers = SSCOP_VERS_QSAAL; + else if ((enum uni_vers)arg1 == UNI_VERS_3_1) + vers = SSCOP_VERS_Q2110; + else { + sscf_uni_abort(uvp, "sscf_uni: bad version\n"); + break; + } + uvp->uv_vers = (enum uni_vers)arg1; + + /* + * Make ourselves ready and pass on the INIT + */ + uvp->uv_ustate = UVU_RELEASED; + uvp->uv_lstate = UVL_IDLE; + + STACK_CALL(SSCOP_INIT, uvp->uv_lower, uvp->uv_tokl, cvp, + (int)vers, (int)&sscf_uni_sscop_parms, err); + if (err) { + /* + * Should never happen + */ + sscf_uni_abort(uvp, "sscf_uni: INIT failure\n"); + } + break; + + case SSCF_UNI_TERM: + /* + * Set termination states + */ + uvp->uv_ustate = UVU_TERM; + uvp->uv_lstate = UVL_TERM; + + /* + * Pass the TERM down the stack + */ + STACK_CALL(SSCOP_TERM, uvp->uv_lower, uvp->uv_tokl, cvp, + 0, 0, err); + if (err) { + /* + * Should never happen + */ + sscf_uni_abort(uvp, "sscf_uni: TERM failure\n"); + return; + } + atm_free((caddr_t)uvp); + sscf_uni_vccnt--; + break; + + case SSCF_UNI_ESTABLISH_REQ: + /* + * Validation based on user state + */ + switch (uvp->uv_ustate) { + + case UVU_RELEASED: + case UVU_PRELEASE: + /* + * Establishing a new connection + */ + uvp->uv_ustate = UVU_PACTIVE; + uvp->uv_lstate = UVL_OUTCONN; + STACK_CALL(SSCOP_ESTABLISH_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + SSCOP_UU_NULL, SSCOP_BR_YES, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVU_ACTIVE: + /* + * Resynchronizing a connection + */ + uvp->uv_ustate = UVU_PACTIVE; + if (uvp->uv_vers == UNI_VERS_3_0) { + uvp->uv_lstate = UVL_OUTCONN; + STACK_CALL(SSCOP_ESTABLISH_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + SSCOP_UU_NULL, SSCOP_BR_YES, err); + } else { + uvp->uv_lstate = UVL_OUTRESYN; + STACK_CALL(SSCOP_RESYNC_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + SSCOP_UU_NULL, 0, err); + } + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVU_TERM: + /* Ignore */ + break; + + case UVU_INST: + case UVU_PACTIVE: + default: + log(LOG_ERR, "sscf_uni_lower: cmd=0x%x, ustate=%d\n", + cmd, uvp->uv_ustate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCF_UNI_RELEASE_REQ: + /* + * Validate re-establishment parameter + */ + switch (arg1) { + + case SSCF_UNI_ESTIND_YES: + uvp->uv_flags &= ~UVF_NOESTIND; + break; + + case SSCF_UNI_ESTIND_NO: + uvp->uv_flags |= UVF_NOESTIND; + break; + + default: + sscf_uni_abort(uvp, "sscf_uni: bad estind value\n"); + return; + } + + /* + * Validation based on user state + */ + switch (uvp->uv_ustate) { + + case UVU_RELEASED: + /* + * Releasing a non-existant connection + */ + STACK_CALL(SSCF_UNI_RELEASE_CNF, uvp->uv_upper, + uvp->uv_toku, cvp, + 0, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVU_PACTIVE: + case UVU_ACTIVE: + /* + * Releasing a connection + */ + uvp->uv_ustate = UVU_PRELEASE; + uvp->uv_lstate = UVL_OUTDISC; + STACK_CALL(SSCOP_RELEASE_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVU_TERM: + /* Ignore */ + break; + + case UVU_INST: + case UVU_PRELEASE: + default: + log(LOG_ERR, "sscf_uni_lower: cmd=0x%x, ustate=%d\n", + cmd, uvp->uv_ustate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCF_UNI_DATA_REQ: +#ifdef notdef + sscf_uni_pdu_print(uvp, (KBuffer *)arg1, "DATA_REQ"); +#endif + + /* + * Validation based on user state + */ + switch (uvp->uv_ustate) { + + case UVU_ACTIVE: + /* + * Send assured data on connection + */ + STACK_CALL(SSCOP_DATA_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + arg1, 0, err); + if (err) { + KB_FREEALL((KBuffer *)arg1); + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVU_RELEASED: + case UVU_TERM: + /* + * Release supplied buffers and ignore + */ + KB_FREEALL((KBuffer *)arg1); + break; + + case UVU_INST: + case UVU_PACTIVE: + case UVU_PRELEASE: + default: + KB_FREEALL((KBuffer *)arg1); + log(LOG_ERR, "sscf_uni_lower: cmd=0x%x, ustate=%d\n", + cmd, uvp->uv_ustate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCF_UNI_UNITDATA_REQ: +#ifdef notdef + sscf_uni_pdu_print(uvp, (KBuffer *)arg1, "UNITDATA_REQ"); +#endif + + /* + * Validation based on user state + */ + switch (uvp->uv_ustate) { + + case UVU_RELEASED: + case UVU_PACTIVE: + case UVU_PRELEASE: + case UVU_ACTIVE: + /* + * Send unassured data on connection + */ + STACK_CALL(SSCOP_UNITDATA_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + arg1, 0, err); + if (err) { + KB_FREEALL((KBuffer *)arg1); + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVU_TERM: + /* + * Release supplied buffers and ignore + */ + KB_FREEALL((KBuffer *)arg1); + break; + + case UVU_INST: + default: + KB_FREEALL((KBuffer *)arg1); + log(LOG_ERR, "sscf_uni_lower: cmd=0x%x, ustate=%d\n", + cmd, uvp->uv_ustate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + default: + log(LOG_ERR, "sscf_uni_lower: unknown cmd 0x%x, uvp=0x%x\n", + cmd, (int)uvp); + } + + return; +} + diff --git a/sys/netatm/uni/sscf_uni_upper.c b/sys/netatm/uni/sscf_uni_upper.c new file mode 100644 index 0000000..2febb5c --- /dev/null +++ b/sys/netatm/uni/sscf_uni_upper.c @@ -0,0 +1,625 @@ +/* + * + * =================================== + * 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: sscf_uni_upper.c,v 1.7 1998/06/29 22:15:31 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCF UNI - SSCOP SAP interface processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscf_uni_upper.c,v 1.7 1998/06/29 22:15:31 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscf_uni.h> +#include <netatm/uni/sscf_uni_var.h> + + +/* + * SSCF_UNI Upper Stack Command Handler + * + * This function will receive all of the stack commands issued from the + * layer below SSCF UNI (ie. SSCOP). + * + * Arguments: + * cmd stack command code + * tok session token + * arg1 command specific argument + * arg2 command specific argument + * + * Returns: + * none + * + */ +void +sscf_uni_upper(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + struct univcc *uvp = (struct univcc *)tok; + Atm_connvc *cvp = uvp->uv_connvc; + int err; + + ATM_DEBUG5("sscf_uni_upper: cmd=0x%x, uvp=0x%x, lstate=%d, arg1=0x%x, arg2=0x%x\n", + cmd, (int)uvp, uvp->uv_lstate, arg1, arg2); + + switch (cmd) { + + case SSCOP_ESTABLISH_IND: + /* + * We don't support SSCOP User-to-User data, so just + * get rid of any supplied to us + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_READY: + if (uvp->uv_vers != UNI_VERS_3_0) { + goto seqerr; + } + goto doestind; + + case UVL_IDLE: + /* + * Incoming connection establishment request + */ + + /* + * If user doesn't want any more incoming sessions + * accepted, then refuse request + */ + if (uvp->uv_flags & UVF_NOESTIND) { + STACK_CALL(SSCOP_RELEASE_REQ, uvp->uv_lower, + uvp->uv_tokl, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, + "sscf_uni: stack memory\n"); + return; + } + break; + } + +doestind: + /* + * Tell sscop we've accepted the new connection + */ + uvp->uv_lstate = UVL_READY; + STACK_CALL(SSCOP_ESTABLISH_RSP, uvp->uv_lower, + uvp->uv_tokl, cvp, + SSCOP_UU_NULL, SSCOP_BR_YES, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + + /* + * Now notify the user of the new connection + */ + uvp->uv_ustate = UVU_ACTIVE; + STACK_CALL(SSCF_UNI_ESTABLISH_IND, uvp->uv_upper, + uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + default: +seqerr: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_ESTABLISH_CNF: + /* + * We don't support SSCOP User-to-User data, so just + * get rid of any supplied to us + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_OUTCONN: + /* + * Outgoing connection establishment completed + */ + + /* + * Tell the user that the connection is established + */ + uvp->uv_ustate = UVU_ACTIVE; + uvp->uv_lstate = UVL_READY; + STACK_CALL(SSCF_UNI_ESTABLISH_CNF, uvp->uv_upper, + uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + case UVL_READY: + default: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_RELEASE_IND: + /* + * We don't support SSCOP User-to-User data, so just + * get rid of any supplied to us + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_OUTCONN: + case UVL_OUTRESYN: + case UVL_READY: + /* + * Peer requesting connection termination + */ + + /* + * Notify the user that the connection + * has been terminated + */ + uvp->uv_ustate = UVU_RELEASED; + uvp->uv_lstate = UVL_IDLE; + STACK_CALL(SSCF_UNI_RELEASE_IND, uvp->uv_upper, + uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_INRESYN: + case UVL_RECOVERY: + default: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_RELEASE_CNF: + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_OUTDISC: + /* + * Peer acknowledging connection termination + */ + + /* + * Notify the user that the connection + * termination is completed + */ + uvp->uv_ustate = UVU_RELEASED; + uvp->uv_lstate = UVL_IDLE; + STACK_CALL(SSCF_UNI_RELEASE_CNF, uvp->uv_upper, + uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + case UVL_READY: + default: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_DATA_IND: +#ifdef notdef + sscf_uni_pdu_print(uvp, (KBuffer *)arg1, "DATA_IND"); +#endif + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_READY: + /* + * Incoming assured data from peer + */ + + /* + * Pass the data up to the user + */ + STACK_CALL(SSCF_UNI_DATA_IND, uvp->uv_upper, + uvp->uv_toku, cvp, + arg1, 0, err); + if (err) { + KB_FREEALL((KBuffer *)arg1); + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + KB_FREEALL((KBuffer *)arg1); + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + default: + KB_FREEALL((KBuffer *)arg1); + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_RESYNC_IND: + /* + * We don't support SSCOP User-to-User data, so just + * get rid of any supplied to us + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_READY: + /* + * Incoming connection resynchronization request + */ + + /* + * Send resynch acknowledgement to sscop + */ + STACK_CALL(SSCOP_RESYNC_RSP, uvp->uv_lower, + uvp->uv_tokl, cvp, + 0, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + + if (uvp->uv_vers != UNI_VERS_3_0) { + + /* + * Notify the user that the connection + * has been resynced + */ + STACK_CALL(SSCF_UNI_ESTABLISH_IND, + uvp->uv_upper, uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, + "sscf_uni: stack memory\n"); + return; + } + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + default: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_RESYNC_CNF: + /* + * Not supported in version 3.0 + */ + if (uvp->uv_vers == UNI_VERS_3_0) { + sscf_uni_abort(uvp, + "sscf_uni: SSCOP_RESYNC_CNF in 3.0\n"); + return; + } + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_OUTRESYN: + /* + * Peer acknowledging connection resynchronization + */ + + /* + * Now notify the user that the connection + * has been resynced + */ + uvp->uv_ustate = UVU_ACTIVE; + uvp->uv_lstate = UVL_READY; + STACK_CALL(SSCF_UNI_ESTABLISH_CNF, uvp->uv_upper, + uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_INRESYN: + case UVL_RECOVERY: + case UVL_READY: + default: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_RECOVER_IND: + /* + * Not supported in version 3.0 + */ + if (uvp->uv_vers == UNI_VERS_3_0) { + sscf_uni_abort(uvp, + "sscf_uni: SSCOP_RECOVER_IND in 3.0\n"); + return; + } + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_READY: + /* + * Recover connection due to internal problems + */ + + /* + * Send recovery acknowledgement to sscop + */ + STACK_CALL(SSCOP_RECOVER_RSP, uvp->uv_lower, + uvp->uv_tokl, cvp, + 0, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + + /* + * Now notify the user that the connection + * has been recovered + */ + STACK_CALL(SSCF_UNI_ESTABLISH_IND, uvp->uv_upper, + uvp->uv_toku, cvp, + SSCOP_UU_NULL, 0, err); + if (err) { + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + break; + + case UVL_INST: + case UVL_IDLE: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + default: + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_UNITDATA_IND: +#ifdef notdef + sscf_uni_pdu_print(uvp, (KBuffer *)arg1, "UNITDATA_IND"); +#endif + + /* + * Validation based on sscop state + */ + switch (uvp->uv_lstate) { + + case UVL_IDLE: + case UVL_OUTCONN: + case UVL_INCONN: + case UVL_OUTDISC: + case UVL_OUTRESYN: + case UVL_INRESYN: + case UVL_RECOVERY: + case UVL_READY: + /* + * Incoming unassured data from peer + */ + + /* + * Pass the data up to the user + */ + STACK_CALL(SSCF_UNI_UNITDATA_IND, uvp->uv_upper, + uvp->uv_toku, cvp, + arg1, 0, err); + if (err) { + KB_FREEALL((KBuffer *)arg1); + sscf_uni_abort(uvp, "sscf_uni: stack memory\n"); + return; + } + break; + + case UVL_TERM: + /* + * Ignoring everything + */ + KB_FREEALL((KBuffer *)arg1); + break; + + case UVL_INST: + default: + KB_FREEALL((KBuffer *)arg1); + log(LOG_ERR, "sscf_uni_upper: cmd=0x%x, lstate=%d\n", + cmd, uvp->uv_lstate); + sscf_uni_abort(uvp, "sscf_uni: sequence err\n"); + } + break; + + case SSCOP_RETRIEVE_IND: + case SSCOP_RETRIEVECMP_IND: + /* + * Not supported + */ + default: + log(LOG_ERR, "sscf_uni_upper: unknown cmd 0x%x, uvp=0x%x\n", + cmd, (int)uvp); + } + + return; +} + diff --git a/sys/netatm/uni/sscf_uni_var.h b/sys/netatm/uni/sscf_uni_var.h new file mode 100644 index 0000000..ffed7de --- /dev/null +++ b/sys/netatm/uni/sscf_uni_var.h @@ -0,0 +1,115 @@ +/* + * + * =================================== + * 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: sscf_uni_var.h,v 1.5 1998/02/19 20:22:05 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCF UNI protocol control blocks + * + */ + +#ifndef _UNI_SSCF_UNI_VAR_H +#define _UNI_SSCF_UNI_VAR_H + +/* + * Structure containing information for each SSCF UNI connection. + */ +struct univcc { + u_char uv_ustate; /* SSCF-User state (see below) */ + u_char uv_lstate; /* SSCF-SSCOP state (see below) */ + u_short uv_flags; /* Connection flags (see below) */ + enum uni_vers uv_vers; /* UNI version */ + + /* Stack variables */ + Atm_connvc *uv_connvc; /* Connection vcc for this stack */ + void *uv_toku; /* Stack upper layer's token */ + void *uv_tokl; /* Stack lower layer's token */ + void (*uv_upper) /* Stack upper layer's interface */ + __P((int, void *, int, int)); + void (*uv_lower) /* Stack lower layer's interface */ + __P((int, void *, int, int)); +}; + +/* + * SSCF to SAAL User (Q.2931) Interface States + */ +#define UVU_INST 0 /* Instantiated, waiting for INIT */ +#define UVU_RELEASED 1 /* Connection released */ +#define UVU_PACTIVE 2 /* Awaiting connection establishment */ +#define UVU_PRELEASE 3 /* Awaiting connection release */ +#define UVU_ACTIVE 4 /* Connection established */ +#define UVU_TERM 5 /* Waiting for TERM */ + +/* + * SSCF to SSCOP Interface States + */ +#define UVL_INST 0 /* Instantiated, waiting for INIT */ +#define UVL_IDLE 1 /* Idle */ +#define UVL_OUTCONN 2 /* Outgoing connection pending */ +#define UVL_INCONN 3 /* Incoming connection pending */ +#define UVL_OUTDISC 4 /* Outgoing disconnection pending */ +#define UVL_OUTRESYN 5 /* Outgoing resynchronization pending */ +#define UVL_INRESYN 6 /* Incoming resynchornization pending */ +#define UVL_RECOVERY 8 /* Recovery pending */ +#define UVL_READY 10 /* Data transfer ready */ +#define UVL_TERM 11 /* Waiting for TERM */ + +/* + * Connection Flags + */ +#define UVF_NOESTIND 0x0001 /* Don't process ESTABLISH_IND */ + + +#ifdef ATM_KERNEL +/* + * Global function declarations + */ + /* sscf_uni.c */ +int sscf_uni_start __P((void)); +int sscf_uni_stop __P((void)); +void sscf_uni_abort __P((struct univcc *, char *)); +void sscf_uni_pdu_print __P((struct univcc *, KBuffer *, + char *)); + + /* sscf_uni_lower.c */ +void sscf_uni_lower __P((int, void *, int, int)); + + /* sscf_uni_upper.c */ +void sscf_uni_upper __P((int, void *, int, int)); + + +/* + * External variables + */ +extern int sscf_uni_vccnt; + +#endif /* ATM_KERNEL */ + +#endif /* _UNI_SSCF_UNI_VAR_H */ diff --git a/sys/netatm/uni/sscop.c b/sys/netatm/uni/sscop.c new file mode 100644 index 0000000..d65118b --- /dev/null +++ b/sys/netatm/uni/sscop.c @@ -0,0 +1,409 @@ +/* + * + * =================================== + * 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: sscop.c,v 1.6 1998/03/24 21:10:43 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * Service Specific Connection Oriented Protocol (SSCOP) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop.c,v 1.6 1998/03/24 21:10:43 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Global variables + */ +int sscop_vccnt = 0; + +struct sscop *sscop_head = NULL; + +struct sscop_stat sscop_stat = {0}; + +struct atm_time sscop_timer = {0, 0}; + +struct sp_info sscop_pool = { + "sscop pool", /* si_name */ + sizeof(struct sscop), /* si_blksiz */ + 5, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + + +/* + * Local functions + */ +static int sscop_inst __P((struct stack_defn **, Atm_connvc *)); + + +/* + * Local variables + */ +static struct stack_defn sscop_service = { + NULL, + SAP_SSCOP, + 0, + sscop_inst, + sscop_lower, + sscop_upper, + 0 +}; + +static struct t_atm_cause sscop_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_TEMPORARY_FAILURE, + {0, 0, 0, 0} +}; + +static u_char sscop_maa_log[MAA_ERROR_COUNT] = { + 1, /* A */ + 1, /* B */ + 1, /* C */ + 1, /* D */ + 1, /* E */ + 1, /* F */ + 1, /* G */ + 1, /* H */ + 1, /* I */ + 1, /* J */ + 1, /* K */ + 1, /* L */ + 1, /* M */ + 0, /* N */ + 0, /* O */ + 0, /* P */ + 1, /* Q */ + 1, /* R */ + 1, /* S */ + 1, /* T */ + 1, /* U */ + 0, /* V */ + 0, /* W */ + 0, /* X */ + 1 /* INVAL */ +}; + + +/* + * Initialize SSCOP processing + * + * This will be called during module loading. We will register our stack + * service and wait for someone to talk to us. + * + * Arguments: + * none + * + * Returns: + * 0 initialization was successful + * errno initialization failed - reason indicated + * + */ +int +sscop_start() +{ + int err = 0; + + /* + * Register stack service + */ + if (err = atm_stack_register(&sscop_service)) + goto done; + + /* + * Start up timer + */ + atm_timeout(&sscop_timer, ATM_HZ/SSCOP_HZ, sscop_timeout); + +done: + return (err); +} + + +/* + * Terminate SSCOP processing + * + * This will be called just prior to unloading the module from memory. All + * signalling instances should have been terminated by now, so we just free + * up all of our resources. + * + * Called at splnet. + * + * Arguments: + * none + * + * Returns: + * 0 termination was successful + * errno termination failed - reason indicated + * + */ +int +sscop_stop() +{ + int err = 0; + + /* + * Any connections still exist?? + */ + if (sscop_vccnt) { + + /* + * Yes, can't stop yet + */ + return (EBUSY); + } + + /* + * Stop our timer + */ + (void) atm_untimeout(&sscop_timer); + + /* + * Deregister the stack service + */ + (void) atm_stack_deregister(&sscop_service); + + /* + * Free our storage pools + */ + atm_release_pool(&sscop_pool); + +done: + return (err); +} + + +/* + * SSCOP Stack Instantiation + * + * Called at splnet. + * + * Arguments: + * ssp pointer to array of stack definition pointers for connection + * ssp[0] points to upper layer's stack service definition + * ssp[1] points to this layer's stack service definition + * ssp[2] points to lower layer's stack service definition + * cvp pointer to connection vcc for this stack + * + * Returns: + * 0 instantiation successful + * errno instantiation failed - reason indicated + * + */ +static int +sscop_inst(ssp, cvp) + struct stack_defn **ssp; + Atm_connvc *cvp; +{ + struct stack_defn *sdp_up = ssp[0], + *sdp_me = ssp[1], + *sdp_low = ssp[2]; + struct sscop *sop; + int err; + + ATM_DEBUG2("sscop_inst: ssp=0x%x, cvp=0x%x\n", ssp, cvp); + + /* + * Validate lower SAP + */ + if ((sdp_low->sd_sap & SAP_CLASS_MASK) != SAP_CPCS) + return (EINVAL); + + /* + * Allocate our control block + */ + sop = (struct sscop *)atm_allocate(&sscop_pool); + if (sop == NULL) + return (ENOMEM); + + sop->so_state = SOS_INST; + sop->so_connvc = cvp; + sop->so_toku = sdp_up->sd_toku; + sop->so_upper = sdp_up->sd_upper; + + /* + * Store my token into service definition + */ + sdp_me->sd_toku = sop; + + /* + * Update and save input buffer headroom + */ + HEADIN(cvp, sizeof(struct pdu_hdr), 0); + /* sop->so_headin = cvp->cvc_attr.headin; */ + + /* + * Pass instantiation down the stack + */ + err = sdp_low->sd_inst(ssp + 1, cvp); + if (err) { + /* + * Lower layer instantiation failed, free our resources + */ + atm_free((caddr_t)sop); + return (err); + } + + /* + * Link in connection block + */ + LINK2TAIL(sop, struct sscop, sscop_head, so_next); + sscop_vccnt++; + sscop_stat.sos_connects++; + + /* + * Save and update output buffer headroom + */ + sop->so_headout = cvp->cvc_attr.headout; + HEADOUT(cvp, sizeof(struct pdu_hdr), 0); + + /* + * Save lower layer's interface info + */ + sop->so_lower = sdp_low->sd_lower; + sop->so_tokl = sdp_low->sd_toku; + + /* + * Initialize version (until INIT received) + */ + sop->so_vers = SSCOP_VERS_Q2110; + + return (0); +} + + +/* + * Report Management Error + * + * Called to report an error to the layer management entity. + * + * Arguments: + * sop pointer to sscop control block + * code error code + * + * Returns: + * none + * + */ +void +sscop_maa_error(sop, code) + struct sscop *sop; + int code; +{ + int i; + + /* + * Validate error code + */ + if ((code < MAA_ERROR_MIN) || + (code > MAA_ERROR_MAX)) + code = MAA_ERROR_INVAL; + i = code - MAA_ERROR_MIN; + + /* + * Bump statistics counters + */ + sscop_stat.sos_maa_error[i]++; + + /* + * Log error message + */ + if (sscop_maa_log[i] != 0) { + struct vccb *vcp = sop->so_connvc->cvc_vcc; + struct atm_pif *pip = vcp->vc_pif; + + log(LOG_ERR, + "sscop_maa_error: intf=%s%d vpi=%d vci=%d code=%c state=%d\n", + pip->pif_name, pip->pif_unit, + vcp->vc_vpi, vcp->vc_vci, code, sop->so_state); + } +} + + +/* + * Abort an SSCOP connection + * + * Called when an unrecoverable or "should never happen" error occurs. + * We log a message, send an END PDU to our peer and request the signalling + * manager to abort the connection. + * + * Arguments: + * sop pointer to sscop control block + * msg pointer to error message + * + * Returns: + * none + * + */ +void +sscop_abort(sop, msg) + struct sscop *sop; + char *msg; +{ + Atm_connvc *cvp = sop->so_connvc; + + /* + * Log and count error + */ + log(LOG_ERR, msg); + sscop_stat.sos_aborts++; + + /* + * Send an END PDU as a courtesy to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Set termination state + */ + sop->so_state = SOS_TERM; + + /* + * Flush all of our queues + */ + sscop_xmit_drain(sop); + sscop_rcvr_drain(sop); + + /* + * Tell Connection Manager to abort this connection + */ + (void) atm_cm_abort(cvp, &sscop_cause); +} + diff --git a/sys/netatm/uni/sscop.h b/sys/netatm/uni/sscop.h new file mode 100644 index 0000000..2f786b3 --- /dev/null +++ b/sys/netatm/uni/sscop.h @@ -0,0 +1,80 @@ +/* + * + * =================================== + * 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: sscop.h,v 1.4 1998/08/26 23:29:19 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP protocol definitions + * + */ + +#ifndef _UNI_SSCOP_H +#define _UNI_SSCOP_H + +/* + * SSCOP Version + */ +enum sscop_vers { + SSCOP_VERS_QSAAL, /* Version = Q.SAAL1 */ + SSCOP_VERS_Q2110 /* Version = Q.2110 */ +}; + + +/* + * SSCOP API definitions + */ +#define SSCOP_UU_NULL 0 /* User-to-User Info = null */ +#define SSCOP_RN_TOTAL -1 /* Retrieval Number = Total */ +#define SSCOP_RN_UNKNOWN -2 /* Retrieval Number = Unknown */ +#define SSCOP_BR_YES 1 /* Buffer Release = Yes */ +#define SSCOP_BR_NO 2 /* Buffer Release = No */ +#define SSCOP_SOURCE_SSCOP 1 /* Source = SSCOP */ +#define SSCOP_SOURCE_USER 2 /* Source = User */ +#define SSCOP_SOURCE_LAST 3 /* Source = from last END */ + + +/* + * Connection parameters for an SSCOP entity. + * Passed via an SSCOP_INIT stack call argument. + */ +struct sscop_parms { + u_short sp_maxinfo; /* k - max information field size */ + u_short sp_maxuu; /* j - max SSCOP-UU field size */ + short sp_maxcc; /* MaxCC - max value of VT(CC) */ + short sp_maxpd; /* MaxPD - max value of VT(PD) */ + u_short sp_timecc; /* Timer_CC value (ticks) */ + u_short sp_timekeep; /* Timer_KEEPALIVE value (ticks) */ + u_short sp_timeresp; /* Timer_NO-RESPONSE value (ticks) */ + u_short sp_timepoll; /* Timer_POLL value (ticks) */ + u_short sp_timeidle; /* Timer_IDLE value (ticks) */ + short sp_rcvwin; /* Receiver window size */ +}; + +#endif /* _UNI_SSCOP_H */ diff --git a/sys/netatm/uni/sscop_lower.c b/sys/netatm/uni/sscop_lower.c new file mode 100644 index 0000000..f76f702 --- /dev/null +++ b/sys/netatm/uni/sscop_lower.c @@ -0,0 +1,349 @@ +/* + * + * =================================== + * 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: sscop_lower.c,v 1.6 1998/04/07 23:21:28 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP - SSCOP SAP interface processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_lower.c,v 1.6 1998/04/07 23:21:28 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local variables + */ +/* + * Stack commands with arg1 containing an buffer pointer + */ +static u_char sscop_buf1[] = { + 0, + 0, /* SSCOP_INIT */ + 0, /* SSCOP_TERM */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, /* SSCOP_ESTABLISH_REQ */ + 0, + 1, /* SSCOP_ESTABLISH_RSP */ + 0, + 1, /* SSCOP_RELEASE_REQ */ + 0, + 0, + 1, /* SSCOP_DATA_REQ */ + 0, + 1, /* SSCOP_RESYNC_REQ */ + 0, + 0, /* SSCOP_RESYNC_RSP */ + 0, + 0, + 0, /* SSCOP_RECOVER_RSP */ + 1, /* SSCOP_UNITDATA_REQ */ + 0, + 0, /* SSCOP_RETRIEVE_REQ */ + 0, + 0 +}; + + +/* + * SSCOP Lower Stack Command Handler + * + * This function will receive all of the stack commands issued from the + * layer above SSCOP (ie. using the SSCOP SAP). The appropriate processing + * function will be determined based on the received stack command and the + * current sscop control block state. + * + * Arguments: + * cmd stack command code + * tok session token + * arg1 command specific argument + * arg2 command specific argument + * + * Returns: + * none + * + */ +void +sscop_lower(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + struct sscop *sop = (struct sscop *)tok; + void (**stab) __P((struct sscop *, int, int)); + void (*func) __P((struct sscop *, int, int)); + int val; + + ATM_DEBUG5("sscop_lower: cmd=0x%x, sop=0x%x, state=%d, arg1=0x%x, arg2=0x%x\n", + cmd, (int)sop, sop->so_state, arg1, arg2); + + /* + * Validate stack command + */ + val = cmd & STKCMD_VAL_MASK; + if (((u_int)cmd < (u_int)SSCOP_CMD_MIN) || + ((u_int)cmd > (u_int)SSCOP_CMD_MAX) || + ((stab = (sop->so_vers == SSCOP_VERS_QSAAL ? + sscop_qsaal_aatab[val] : + sscop_q2110_aatab[val])) == NULL)) { + log(LOG_ERR, "sscop_lower: unknown cmd 0x%x, sop=0x%x\n", + cmd, (int)sop); + return; + } + + /* + * Validate sscop state + */ + if (sop->so_state > SOS_MAXSTATE) { + log(LOG_ERR, "sscop_lower: invalid state sop=0x%x, state=%d\n", + (int)sop, sop->so_state); + /* + * Release possible buffer + */ + if (sscop_buf1[val]) { + if (arg1) + KB_FREEALL((KBuffer *)arg1); + } + return; + } + + /* + * Validate command/state combination + */ + func = stab[sop->so_state]; + if (func == NULL) { + log(LOG_ERR, + "sscop_lower: invalid cmd/state: sop=0x%x, cmd=0x%x, state=%d\n", + (int)sop, cmd, sop->so_state); + /* + * Release possible buffer + */ + if (sscop_buf1[val]) { + if (arg1) + KB_FREEALL((KBuffer *)arg1); + } + return; + } + + /* + * Call event processing function + */ + (*func)(sop, arg1, arg2); + + return; +} + + +/* + * No-op Processor (no buffers) + * + * Arguments: + * sop pointer to sscop connection block + * arg1 command-specific argument + * arg2 command-specific argument + * + * Returns: + * none + * + */ +void +sscop_aa_noop_0(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + /* + * Nothing to do + */ + return; +} + + +/* + * No-op Processor (arg1 == buffer) + * + * Arguments: + * sop pointer to sscop connection block + * arg1 command-specific argument (buffer pointer) + * arg2 command-specific argument + * + * Returns: + * none + * + */ +void +sscop_aa_noop_1(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * Just free buffer chain + */ + if (arg1) + KB_FREEALL((KBuffer *)arg1); + + return; +} + + +/* + * SSCOP_INIT / SOS_INST Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 command specific argument + * arg2 command specific argument + * + * Returns: + * none + * + */ +void +sscop_init_inst(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + int err; + + /* + * Make ourselves ready and pass on the INIT + */ + sop->so_state = SOS_IDLE; + + /* + * Validate SSCOP version to use + */ + switch ((enum sscop_vers)arg1) { + case SSCOP_VERS_QSAAL: + break; + + case SSCOP_VERS_Q2110: + break; + + default: + sscop_abort(sop, "sscop: bad version\n"); + return; + } + sop->so_vers = (enum sscop_vers)arg1; + + /* + * Copy SSCOP connection parameters to use + */ + sop->so_parm = *(struct sscop_parms *)arg2; + + /* + * Initialize lower layers + */ + STACK_CALL(CPCS_INIT, sop->so_lower, sop->so_tokl, sop->so_connvc, + 0, 0, err); + if (err) { + /* + * Should never happen + */ + sscop_abort(sop, "sscop: INIT failure\n"); + return; + } + return; +} + + +/* + * SSCOP_TERM / SOS_* Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 command specific argument + * arg2 command specific argument + * + * Returns: + * none + * + */ +void +sscop_term_all(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + int err; + + /* + * Set termination state + */ + sop->so_state = SOS_TERM; + + /* + * Pass the TERM down the stack + */ + STACK_CALL(CPCS_TERM, sop->so_lower, sop->so_tokl, sop->so_connvc, + 0, 0, err); + if (err) { + /* + * Should never happen + */ + sscop_abort(sop, "sscop: TERM failure\n"); + return; + } + + /* + * Unlink and free the connection block + */ + UNLINK(sop, struct sscop, sscop_head, so_next); + atm_free((caddr_t)sop); + sscop_vccnt--; + return; +} + diff --git a/sys/netatm/uni/sscop_misc.h b/sys/netatm/uni/sscop_misc.h new file mode 100644 index 0000000..4348cef --- /dev/null +++ b/sys/netatm/uni/sscop_misc.h @@ -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: sscop_misc.h,v 1.4 1998/08/26 23:29:19 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP miscellaneous definitions + * + */ + +#ifndef _UNI_SSCOP_MISC_H +#define _UNI_SSCOP_MISC_H + +/* + * SSCOP command definitions + */ +#define SSCOP_CMD_MIN SSCOP_INIT /* Minimum SSCOP CMD value */ +#define SSCOP_CMD_MAX SSCOP_RETRIEVECMP_IND /* Maximum SSCOP CMD value */ +#define SSCOP_CMD_SIZE 36 /* Size of command lookup table */ + + +/* + * Management Errors + */ +#define MAA_ERROR_MIN 'A' +#define MAA_ERROR_MAX 'X' +#define MAA_ERROR_INVAL (MAA_ERROR_MAX + 1) +#define MAA_ERROR_COUNT (MAA_ERROR_MAX - MAA_ERROR_MIN + 2) + + +/* + * SSCOP Sequence Numbers + * + * SSCOP sequence numbers are 24 bit integers using modulo arithmetic. + * The macros below must be used to modify and compare such numbers. + * Comparison of sequence numbers is always relative to some base number (b). + */ +typedef u_int sscop_seq; + +#define SEQ_MOD 0xffffff +#define SEQ_VAL(v) ((v) & SEQ_MOD) +#define SEQ_SET(s,v) ((s) = SEQ_VAL(v)) +#define SEQ_ADD(s,v) (SEQ_VAL((s) + (v))) +#define SEQ_SUB(s,v) (SEQ_VAL((s) - (v))) +#define SEQ_INCR(s,v) ((s) = SEQ_VAL((s) + (v))) +#define SEQ_DECR(s,v) ((s) = SEQ_VAL((s) - (v))) +#define SEQ_EQ(x,y) (SEQ_VAL(x) == SEQ_VAL(y)) +#define SEQ_NEQ(x,y) (SEQ_VAL(x) != SEQ_VAL(y)) +#define SEQ_LT(x,y,b) (SEQ_VAL((x) - (b)) < SEQ_VAL((y) - (b))) +#define SEQ_LEQ(x,y,b) (SEQ_VAL((x) - (b)) <= SEQ_VAL((y) - (b))) +#define SEQ_GT(x,y,b) (SEQ_VAL((x) - (b)) > SEQ_VAL((y) - (b))) +#define SEQ_GEQ(x,y,b) (SEQ_VAL((x) - (b)) >= SEQ_VAL((y) - (b))) + + +/* + * SSCOP Timers + * + * All of the SSCOP timer fields are maintained in terms of clock ticks. + * The timers tick 2 times per second. + */ +#define SSCOP_HZ 2 /* SSCOP ticks per second */ + +#define SSCOP_T_NUM 4 /* Number of timers per connection */ + +#define SSCOP_T_POLL 0 /* Timer_POLL / Timer_KEEP-ALIVE */ +#define SSCOP_T_NORESP 1 /* Timer_NO-RESPONSE */ +#define SSCOP_T_CC 2 /* Timer_CC */ +#define SSCOP_T_IDLE 3 /* Timer_IDLE */ + +#endif /* _UNI_SSCOP_MISC_H */ diff --git a/sys/netatm/uni/sscop_pdu.c b/sys/netatm/uni/sscop_pdu.c new file mode 100644 index 0000000..b9aec03 --- /dev/null +++ b/sys/netatm/uni/sscop_pdu.c @@ -0,0 +1,1237 @@ +/* + * + * =================================== + * 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: sscop_pdu.c,v 1.6 1998/04/07 23:21:36 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP - PDU subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_pdu.c,v 1.6 1998/04/07 23:21:36 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + +/* + * Local functions + */ +static KBuffer * sscop_stat_init __P((struct sscop *)); +static KBuffer * sscop_stat_add __P((sscop_seq, KBuffer *)); +static int sscop_stat_end __P((struct sscop *, sscop_seq, + KBuffer *, KBuffer *)); +static int sscop_recv_locate __P((struct sscop *, sscop_seq, + struct pdu_hdr **)); + + +/* + * Build and send BGN PDU + * + * A BGN PDU will be constructed and passed down the protocol stack. + * The SSCOP-UU/N(UU) field is not supported. + * + * Arguments: + * sop pointer to sscop connection control block + * source originator of BGN PDU (Q.SAAL1 only) + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_bgn(sop, source) + struct sscop *sop; + int source; +{ + KBuffer *m; + struct bgn_pdu *bp; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct bgn_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct bgn_pdu))); + KB_LEN(m) = sizeof(struct bgn_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, bp, struct bgn_pdu *); + *(int *)&bp->bgn_rsvd[0] = 0; + if (sop->so_vers != SSCOP_VERS_QSAAL) + bp->bgn_nsq = sop->so_sendconn; + bp->bgn_nmr = + htonl((PT_BGN << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvmax)); + if ((sop->so_vers == SSCOP_VERS_QSAAL) && + (source == SSCOP_SOURCE_SSCOP)) + bp->bgn_type |= PT_SOURCE_SSCOP; + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send BGAK PDU + * + * A BGAK PDU will be constructed and passed down the protocol stack. + * The SSCOP-UU/N(UU) field is not supported. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_bgak(sop) + struct sscop *sop; +{ + KBuffer *m; + struct bgak_pdu *bp; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct bgak_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct bgak_pdu))); + KB_LEN(m) = sizeof(struct bgak_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, bp, struct bgak_pdu *); + bp->bgak_rsvd = 0; + bp->bgak_nmr = + htonl((PT_BGAK << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvmax)); + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send BGREJ PDU + * + * A BGREJ PDU will be constructed and passed down the protocol stack. + * The SSCOP-UU/N(UU) field is not supported. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_bgrej(sop) + struct sscop *sop; +{ + KBuffer *m; + struct bgrej_pdu *bp; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct bgrej_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct bgrej_pdu))); + KB_LEN(m) = sizeof(struct bgrej_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, bp, struct bgrej_pdu *); + bp->bgrej_rsvd2 = 0; + *(u_int *)&bp->bgrej_type = htonl((PT_BGREJ << PT_TYPE_SHIFT) | 0); + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send END PDU + * + * An END PDU will be constructed and passed down the protocol stack. + * The SSCOP-UU/N(UU) field is not supported. + * + * Arguments: + * sop pointer to sscop connection control block + * source originator of END PDU + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_end(sop, source) + struct sscop *sop; + int source; +{ + KBuffer *m; + struct end_pdu *ep; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct end_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct end_pdu))); + KB_LEN(m) = sizeof(struct end_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, ep, struct end_pdu *); + ep->end_rsvd2 = 0; + *(u_int *)&ep->end_type = htonl((PT_END << PT_TYPE_SHIFT) | 0); + if (source == SSCOP_SOURCE_SSCOP) { + ep->end_type |= PT_SOURCE_SSCOP; + sop->so_flags |= SOF_ENDSSCOP; + } else if (source == SSCOP_SOURCE_USER) + sop->so_flags &= ~SOF_ENDSSCOP; + else if ((source == SSCOP_SOURCE_LAST) && + (sop->so_flags & SOF_ENDSSCOP)) + ep->end_type |= PT_SOURCE_SSCOP; + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send ENDAK PDU + * + * An ENDAK PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_endak(sop) + struct sscop *sop; +{ + KBuffer *m; + struct endak_q2110_pdu *e2p; + struct endak_qsaal_pdu *esp; + int err, size; + + + /* + * Get size of PDU + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) + size = sizeof(struct endak_qsaal_pdu); + else + size = sizeof(struct endak_q2110_pdu); + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, size, KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, KB_BFRLEN(m) - size)); + KB_LEN(m) = size; + + /* + * Build PDU + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) { + KB_DATASTART(m, esp, struct endak_qsaal_pdu *); + *(u_int *)&esp->endak_type = + htonl((PT_ENDAK << PT_TYPE_SHIFT) | 0); + } else { + KB_DATASTART(m, e2p, struct endak_q2110_pdu *); + e2p->endak_rsvd2 = 0; + *(u_int *)&e2p->endak_type = + htonl((PT_ENDAK << PT_TYPE_SHIFT) | 0); + } + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send RS PDU + * + * A RS PDU will be constructed and passed down the protocol stack. + * The SSCOP-UU/N(UU) field is not supported. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_rs(sop) + struct sscop *sop; +{ + KBuffer *m; + struct rs_pdu *rp; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct rs_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct rs_pdu))); + KB_LEN(m) = sizeof(struct rs_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, rp, struct rs_pdu *); + *(int *)&rp->rs_rsvd[0] = 0; + if (sop->so_vers != SSCOP_VERS_QSAAL) { + rp->rs_nsq = sop->so_sendconn; + rp->rs_nmr = htonl((PT_RS << PT_TYPE_SHIFT) | + SEQ_VAL(sop->so_rcvmax)); + } else { + rp->rs_nmr = htonl((PT_RS << PT_TYPE_SHIFT) | 0); + } + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send RSAK PDU + * + * An RSAK PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_rsak(sop) + struct sscop *sop; +{ + KBuffer *m; + struct rsak_q2110_pdu *r2p; + struct rsak_qsaal_pdu *rsp; + int err, size; + + + /* + * Get size of PDU + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) + size = sizeof(struct rsak_qsaal_pdu); + else + size = sizeof(struct rsak_q2110_pdu); + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, size, KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, KB_BFRLEN(m) - size)); + KB_LEN(m) = size; + + /* + * Build PDU + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) { + KB_DATASTART(m, rsp, struct rsak_qsaal_pdu *); + *(u_int *)&rsp->rsaks_type = + htonl((PT_RSAK << PT_TYPE_SHIFT) | 0); + } else { + KB_DATASTART(m, r2p, struct rsak_q2110_pdu *); + r2p->rsak_rsvd = 0; + r2p->rsak_nmr = htonl((PT_RSAK << PT_TYPE_SHIFT) | + SEQ_VAL(sop->so_rcvmax)); + } + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send ER PDU + * + * An ER PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_er(sop) + struct sscop *sop; +{ + KBuffer *m; + struct er_pdu *ep; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct er_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct er_pdu))); + KB_LEN(m) = sizeof(struct er_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, ep, struct er_pdu *); + *(int *)&ep->er_rsvd[0] = 0; + ep->er_nsq = sop->so_sendconn; + ep->er_nmr = htonl((PT_ER << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvmax)); + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send ERAK PDU + * + * An ERAK PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_erak(sop) + struct sscop *sop; +{ + KBuffer *m; + struct erak_pdu *ep; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct erak_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct erak_pdu))); + KB_LEN(m) = sizeof(struct erak_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, ep, struct erak_pdu *); + ep->erak_rsvd = 0; + ep->erak_nmr = htonl((PT_ERAK << PT_TYPE_SHIFT) | + SEQ_VAL(sop->so_rcvmax)); + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send POLL PDU + * + * A POLL PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_poll(sop) + struct sscop *sop; +{ + KBuffer *m; + struct poll_pdu *pp; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct poll_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct poll_pdu))); + KB_LEN(m) = sizeof(struct poll_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, pp, struct poll_pdu *); + pp->poll_nps = htonl(SEQ_VAL(sop->so_pollsend)); + pp->poll_ns = htonl((PT_POLL << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_send)); + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * STAT PDU Construction - Initialize for new PDU + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * addr pointer to initialized buffer + * 0 unable to allocate buffer + * + */ +static KBuffer * +sscop_stat_init(sop) + struct sscop *sop; +{ + KBuffer *m; + +#define STAT_INIT_SIZE (sizeof(struct stat_pdu) + 2 * sizeof(sscop_seq)) + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, STAT_INIT_SIZE, KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (0); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, sop->so_headout < (KB_BFRLEN(m) - STAT_INIT_SIZE) ? + sop->so_headout : 0); + KB_LEN(m) = 0; + + return (m); +#undef STAT_INIT_SIZE +} + + +/* + * STAT PDU Construction - Add List Element + * + * Arguments: + * elem sequence number to add to list + * m pointer to current buffer + * + * Returns: + * addr pointer to current buffer (updated) + * 0 buffer allocation failure + * + */ +static KBuffer * +sscop_stat_add(elem, m) + sscop_seq elem; + KBuffer *m; +{ + KBuffer *n; + sscop_seq *sp; + int space; + + /* + * See if new element will fit in current buffer + */ + KB_TAILROOM(m, space); + if (space < sizeof(elem)) { + + /* + * Nope, so get another buffer + */ + KB_ALLOC(n, sizeof(elem), KB_F_NOWAIT, KB_T_DATA); + if (n == NULL) + return (0); + + /* + * Link in new buffer + */ + KB_LINK(n, m); + KB_LEN(n) = 0; + m = n; + } + + /* + * Add new element + */ + KB_DATAEND(m, sp, sscop_seq *); + *sp = htonl(elem); + KB_LEN(m) += sizeof (elem); + return (m); +} + + +/* + * STAT PDU Construction - Add Trailer and Send + * + * Arguments: + * sop pointer to sscop connection control block + * nps received poll sequence number (POLL.N(PS)) + * head pointer to head of buffer chain + * m pointer to current (last) buffer + * + * Returns: + * 0 STAT successfully sent + * else unable to send STAT or truncated STAT was sent - buffer freed + * + */ +static int +sscop_stat_end(sop, nps, head, m) + struct sscop *sop; + sscop_seq nps; + KBuffer *head; + KBuffer *m; +{ + struct stat_pdu *sp; + KBuffer *n; + int err, space, trunc = 0; + + /* + * See if PDU trailer will fit in current buffer + */ + KB_TAILROOM(m, space); + if (space < sizeof(struct stat_pdu)) { + + /* + * Doesn't fit, so get another buffer + */ + KB_ALLOC(n, sizeof(struct stat_pdu), KB_F_NOWAIT, KB_T_DATA); + if (n == NULL) { + /* + * Out of buffers - truncate elements and send + * what we can, but tell caller that we can't + * send any more segments. + */ + trunc = 1; + do { + KB_LEN(m) -= sizeof(sscop_seq); + space += sizeof(sscop_seq); + } while (space < sizeof(struct stat_pdu)); + } else { + /* + * Link in new buffer + */ + KB_LINK(n, m); + KB_LEN(n) = 0; + m = n; + } + } + + /* + * Build PDU trailer + */ + KB_DATAEND(m, sp, struct stat_pdu *); + sp->stat_nps = htonl(nps); + sp->stat_nmr = htonl(sop->so_rcvmax); + sp->stat_nr = htonl(sop->so_rcvnext); + sp->stat_type = PT_STAT; + KB_LEN(m) += sizeof(struct stat_pdu); + + /* + * Finally, send the STAT + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)head, 0, err); + + if (err) { + /* + * We lie about the STACK_CALL failing... + */ + KB_FREEALL(head); + } + + if (trunc) + return (1); + else + return (0); +} + + +/* + * Check for PDU in Receive Queue + * + * A receive queue will be searched for an SD PDU matching the requested + * sequence number. The caller must supply a pointer to the address of the + * PDU in the particular receive queue at which to begin the search. This + * function will update that pointer as it traverses the queue. + * + * Arguments: + * sop pointer to sscop connection control block + * seq sequence number of PDU to locate + * currp address of pointer to PDU in receive queue to start search + * + * Returns: + * 0 reqeusted PDU not in receive queue + * 1 requested PDU located in receive queue + * + */ +static int +sscop_recv_locate(sop, seq, currp) + struct sscop *sop; + sscop_seq seq; + struct pdu_hdr **currp; +{ + sscop_seq cs; + + /* + * Search queue until we know the answer + */ + while (1) { + /* + * If we're at the end of the queue, the PDU isn't there + */ + if (*currp == NULL) + return (0); + + /* + * Get the current PDU sequence number + */ + cs = (*currp)->ph_ns; + + /* + * See if we're at the requested PDU + */ + if (seq == cs) + return (1); + + /* + * If we're past the requested seq number, + * the PDU isn't there + */ + if (SEQ_LT(seq, cs, sop->so_rcvnext)) + return (0); + + /* + * Go to next PDU and keep looking + */ + *currp = (*currp)->ph_recv_lk; + } +} + + +/* + * Build and send STAT PDU + * + * A STAT PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * nps received poll sequence number (POLL.N(PS)) + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send complete pdu + * + */ +int +sscop_send_stat(sop, nps) + struct sscop *sop; + sscop_seq nps; +{ + KBuffer *head, *curr, *n; + struct pdu_hdr *rq = sop->so_recv_hd; + sscop_seq i; + sscop_seq vrh = sop->so_rcvhigh; + sscop_seq vrr = sop->so_rcvnext; + int len = 0; + + /* + * Initialize for start of STAT PDU + */ + head = sscop_stat_init(sop); + if (head == NULL) + return (1); + curr = head; + + /* + * Start with first PDU not yet received + */ + i = vrr; + + /* + * Keep looping until we get to last sent PDU + */ + while (i != vrh) { + + /* + * Find next missing PDU + */ + while (SEQ_LT(i, vrh, vrr) && sscop_recv_locate(sop, i, &rq)) { + SEQ_INCR(i, 1); + } + + /* + * Add odd (start of missing gap) STAT element + */ + n = sscop_stat_add(i, curr); + if (n == NULL) { + goto nobufs; + } + curr = n; + len++; + + /* + * Have we reached the last sent PDU sequence number?? + */ + if (i == vrh) { + /* + * Yes, then we're done, send STAT + */ + break; + } + + /* + * Have we reached the max STAT size yet?? + */ + if (len >= PDU_MAX_ELEM) { + /* + * Yes, send this STAT segment + */ + if (sscop_stat_end(sop, nps, head, curr)) { + return (1); + } + + /* + * Start a new segment + */ + head = sscop_stat_init(sop); + if (head == NULL) + return (1); + curr = head; + + /* + * Restart missing gap + */ + curr = sscop_stat_add(i, curr); + if (curr == NULL) { + KB_FREEALL(head); + return (1); + } + len = 1; + } + + /* + * Now find the end of the missing gap + */ + do { + SEQ_INCR(i, 1); + } while (SEQ_LT(i, vrh, vrr) && + (sscop_recv_locate(sop, i, &rq) == 0)); + + /* + * Add even (start of received gap) STAT element + */ + n = sscop_stat_add(i, curr); + if (n == NULL) { + goto nobufs; + } + curr = n; + len++; + } + + /* + * Finally, send the STAT PDU (or last STAT segment) + */ + if (sscop_stat_end(sop, nps, head, curr)) { + return (1); + } + + return (0); + +nobufs: + /* + * Send a truncated STAT PDU + */ + sscop_stat_end(sop, nps, head, curr); + + return (1); +} + + +/* + * Build and send USTAT PDU + * + * A USTAT PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * ns sequence number for second list element + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu + * + */ +int +sscop_send_ustat(sop, ns) + struct sscop *sop; + sscop_seq ns; +{ + KBuffer *m; + struct ustat_pdu *up; + int err; + + + /* + * Get buffer for PDU + */ + KB_ALLOCPKT(m, sizeof(struct ustat_pdu), KB_F_NOWAIT, KB_T_HEADER); + if (m == NULL) + return (1); + + /* + * Setup buffer controls + */ + KB_HEADSET(m, MIN(sop->so_headout, + KB_BFRLEN(m) - sizeof(struct ustat_pdu))); + KB_LEN(m) = sizeof(struct ustat_pdu); + + /* + * Build PDU + */ + KB_DATASTART(m, up, struct ustat_pdu *); + up->ustat_le1 = htonl(SEQ_VAL(sop->so_rcvhigh)); + up->ustat_le2 = htonl(SEQ_VAL(ns)); + up->ustat_nmr = htonl(SEQ_VAL(sop->so_rcvmax)); + up->ustat_nr = + htonl((PT_USTAT << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvnext)); + + /* + * Send PDU towards peer + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + + if (err) + KB_FREEALL(m); + + return (err); +} + + +/* + * Build and send UD PDU + * + * A UD PDU will be constructed and passed down the protocol stack. + * + * Arguments: + * sop pointer to sscop connection control block + * m pointer to user data buffer chain + * + * Returns: + * 0 PDU successfully built and passed down the stack + * else unable to build or send pdu (buffer released) + * + */ +int +sscop_send_ud(sop, m) + struct sscop *sop; + KBuffer *m; +{ + KBuffer *ml, *n; + int len = 0, err; + int pad, trlen, space; + u_char *cp; + + /* + * Count data and get to last buffer in chain + */ + for (ml = m; ; ml = KB_NEXT(ml)) { + len += KB_LEN(ml); + if (KB_NEXT(ml) == NULL) + break; + } + + /* + * Verify data length + */ + if (len > sop->so_parm.sp_maxinfo) { + KB_FREEALL(m); + sscop_abort(sop, "sscop: maximum unitdata size exceeded\n"); + return (1); + } + + /* + * Figure out how much padding we'll need + */ + pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len; + trlen = pad + sizeof(struct ud_pdu); + + /* + * Get space for PDU trailer and padding + */ + KB_TAILROOM(ml, space); + if (space < trlen) { + /* + * Allocate & link buffer for pad and trailer + */ + KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER); + if (n == NULL) + return (1); + + KB_LEN(n) = 0; + KB_LINK(n, ml); + ml = n; + } + + /* + * Build the PDU trailer + * + * Since we can't be sure of alignment in the buffers, we + * have to move this a byte at a time. + */ + KB_DATAEND(ml, cp, u_char *); + cp += pad; + *cp++ = (pad << PT_PAD_SHIFT) | PT_UD; + KM_ZERO(cp, 3); + KB_LEN(ml) += trlen; + + /* + * Now pass PDU down the stack + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + return (1); + } + + return (0); +} + + +/* + * Print an SSCOP PDU + * + * Arguments: + * sop pointer to sscop connection control block + * m pointer to pdu buffer chain + * msg pointer to message string + * + * Returns: + * none + * + */ +void +sscop_pdu_print(sop, m, msg) + struct sscop *sop; + KBuffer *m; + char *msg; +{ + char buf[128]; + struct vccb *vcp; + + vcp = sop->so_connvc->cvc_vcc; + sprintf(buf, "sscop %s: vcc=(%d,%d)\n", msg, vcp->vc_vpi, vcp->vc_vci); + atm_pdu_print(m, buf); +} + diff --git a/sys/netatm/uni/sscop_pdu.h b/sys/netatm/uni/sscop_pdu.h new file mode 100644 index 0000000..387a602 --- /dev/null +++ b/sys/netatm/uni/sscop_pdu.h @@ -0,0 +1,317 @@ +/* + * + * =================================== + * 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: sscop_pdu.h,v 1.3 1997/05/06 22:20:15 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP Protocol Data Unit (PDU) definitions + * + */ + +#ifndef _UNI_SSCOP_PDU_H +#define _UNI_SSCOP_PDU_H + +/* + * SSCOP PDU Constants + */ +#define PDU_MIN_LEN 4 /* Minimum PDU length */ +#define PDU_LEN_MASK 3 /* PDU length must be 32-bit aligned */ +#define PDU_ADDR_MASK 3 /* PDUs must be 32-bit aligned */ +#define PDU_SEQ_MASK 0x00ffffff /* Mask for 24-bit sequence values */ +#define PDU_MAX_INFO 65528 /* Maximum length of PDU info field */ +#define PDU_MAX_UU 65524 /* Maximum length of SSCOP-UU field */ +#define PDU_MAX_STAT 65520 /* Maximum length of STAT list */ +#define PDU_MAX_ELEM 67 /* Maximum elements sent in STAT */ +#define PDU_PAD_ALIGN 4 /* I-field padding alignment */ + + +/* + * PDU Queueing Header + * + * There will be a queueing header tacked on to the front of each + * buffer chain that is placed on any of the sscop SD PDU queues (not + * including the SD transmission queue). Note that this header will + * not be included in the buffer data length/offset fields. + */ +struct pdu_hdr { + union { + struct pdu_hdr *phu_pack_lk; /* Pending ack queue link */ + struct pdu_hdr *phu_recv_lk; /* Receive queue link */ + } ph_u; + struct pdu_hdr *ph_rexmit_lk; /* Retranmit queue link */ + sscop_seq ph_ns; /* SD.N(S) - SD's sequence number */ + sscop_seq ph_nps; /* SD.N(PS) - SD's poll sequence */ + KBuffer *ph_buf; /* Pointer to containing buffer */ +}; +#define ph_pack_lk ph_u.phu_pack_lk +#define ph_recv_lk ph_u.phu_recv_lk + + +/* + * SSCOP PDU formats + * + * N.B. - all SSCOP PDUs are trailer oriented (don't ask me...) + */ + +/* + * PDU Type Fields + */ +#define PT_PAD_MASK 0xc0 /* Pad length mask */ +#define PT_PAD_SHIFT 6 /* Pad byte shift count */ +#define PT_SOURCE_SSCOP 0x10 /* Source = SSCOP */ +#define PT_TYPE_MASK 0x0f /* Type mask */ +#define PT_TYPE_MAX 0x0f /* Maximum pdu type */ +#define PT_TYPE_SHIFT 24 /* Type word shift count */ + +#define PT_BGN 0x01 /* Begin */ +#define PT_BGAK 0x02 /* Begin Acknowledge */ +#define PT_BGREJ 0x07 /* Begin Reject */ +#define PT_END 0x03 /* End */ +#define PT_ENDAK 0x04 /* End Acknowledge */ +#define PT_RS 0x05 /* Resynchronization */ +#define PT_RSAK 0x06 /* Resynchronization Acknowledge */ +#define PT_ER 0x09 /* Error Recovery */ +#define PT_ERAK 0x0f /* Error Recovery Acknowledge */ +#define PT_SD 0x08 /* Sequenced Data */ +#define PT_SDP 0x09 /* Sequenced Data with Poll */ +#define PT_POLL 0x0a /* Status Request */ +#define PT_STAT 0x0b /* Solicited Status Response */ +#define PT_USTAT 0x0c /* Unsolicited Status Response */ +#define PT_UD 0x0d /* Unnumbered Data */ +#define PT_MD 0x0e /* Management Data */ + +/* + * Begin PDU + */ +struct bgn_pdu { + u_char bgn_rsvd[3]; /* Reserved */ + u_char bgn_nsq; /* N(SQ) */ + union { + u_char bgnu_type; /* PDU type, etc */ + sscop_seq bgnu_nmr; /* N(MR) */ + } bgn_u; +}; +#define bgn_type bgn_u.bgnu_type +#define bgn_nmr bgn_u.bgnu_nmr + +/* + * Begin Acknowledge PDU + */ +struct bgak_pdu { + int bgak_rsvd; /* Reserved */ + union { + u_char bgaku_type; /* PDU type, etc */ + sscop_seq bgaku_nmr; /* N(MR) */ + } bgak_u; +}; +#define bgak_type bgak_u.bgaku_type +#define bgak_nmr bgak_u.bgaku_nmr + +/* + * Begin Reject PDU + */ +struct bgrej_pdu { + int bgrej_rsvd2; /* Reserved */ + u_char bgrej_type; /* PDU type, etc */ + u_char bgrej_rsvd1[3]; /* Reserved */ +}; + +/* + * End PDU + */ +struct end_pdu { + int end_rsvd2; /* Reserved */ + u_char end_type; /* PDU type, etc */ + u_char end_rsvd1[3]; /* Reserved */ +}; + +/* + * End Acknowledge PDU (Q.2110) + */ +struct endak_q2110_pdu { + int endak_rsvd2; /* Reserved */ + u_char endak_type; /* PDU type, etc */ + u_char endak_rsvd1[3]; /* Reserved */ +}; + +/* + * End Acknowledge PDU (Q.SAAL) + */ +struct endak_qsaal_pdu { + u_char endak_type; /* PDU type, etc */ + u_char endak_rsvd[3]; /* Reserved */ +}; + +/* + * Resynchronization PDU + */ +struct rs_pdu { + char rs_rsvd[3]; /* Reserved */ + u_char rs_nsq; /* N(SQ) */ + union { + u_char rsu_type; /* PDU type, etc */ + sscop_seq rsu_nmr; /* N(MR) */ + } rs_u; +}; +#define rs_type rs_u.rsu_type +#define rs_nmr rs_u.rsu_nmr + +/* + * Resynchronization Acknowledge PDU (Q.2110) + */ +struct rsak_q2110_pdu { + int rsak_rsvd; /* Reserved */ + union { + u_char rsaku_type; /* PDU type, etc */ + sscop_seq rsaku_nmr; /* N(MR) */ + } rsak_u; +}; +#define rsak2_type rsak_u.rsaku_type +#define rsak_nmr rsak_u.rsaku_nmr + +/* + * Resynchronization Acknowledge PDU (Q.SAAL) + */ +struct rsak_qsaal_pdu { + u_char rsaks_type; /* PDU type, etc */ + u_char rsak_rsvd[3]; /* Reserved */ +}; + +/* + * Error Recovery PDU + */ +struct er_pdu { + char er_rsvd[3]; /* Reserved */ + u_char er_nsq; /* N(SQ) */ + union { + u_char eru_type; /* PDU type, etc */ + sscop_seq eru_nmr; /* N(MR) */ + } er_u; +}; +#define er_type er_u.eru_type +#define er_nmr er_u.eru_nmr + +/* + * Error Recovery Acknowledge PDU + */ +struct erak_pdu { + int erak_rsvd; /* Reserved */ + union { + u_char eraku_type; /* PDU type, etc */ + sscop_seq eraku_nmr; /* N(MR) */ + } erak_u; +}; +#define erak_type erak_u.eraku_type +#define erak_nmr erak_u.eraku_nmr + +/* + * Sequenced Data PDU + */ +struct sd_pdu { + union { + u_char sdu_type; /* PDU type, etc */ + sscop_seq sdu_ns; /* N(S) */ + } sd_u; +}; +#define sd_type sd_u.sdu_type +#define sd_ns sd_u.sdu_ns + +/* + * Sequenced Data with Poll PDU + */ +struct sdp_pdu { + sscop_seq sdp_nps; /* N(PS) */ + union { + u_char sdpu_type; /* PDU type, etc */ + sscop_seq sdpu_ns; /* N(S) */ + } sdp_u; +}; +#define sdp_type sdp_u.sdpu_type +#define sdp_ns sdp_u.sdpu_ns + +/* + * Poll PDU + */ +struct poll_pdu { + sscop_seq poll_nps; /* N(PS) */ + union { + u_char pollu_type; /* PDU type, etc */ + sscop_seq pollu_ns; /* N(S) */ + } poll_u; +}; +#define poll_type poll_u.pollu_type +#define poll_ns poll_u.pollu_ns + +/* + * Solicited Status PDU + */ +struct stat_pdu { + sscop_seq stat_nps; /* N(PS) */ + sscop_seq stat_nmr; /* N(MR) */ + union { + u_char statu_type; /* PDU type, etc */ + sscop_seq statu_nr; /* N(R) */ + } stat_u; +}; +#define stat_type stat_u.statu_type +#define stat_nr stat_u.statu_nr + +/* + * Unsolicited Status PDU + */ +struct ustat_pdu { + sscop_seq ustat_le1; /* List element 1 */ + sscop_seq ustat_le2; /* List element 2 */ + sscop_seq ustat_nmr; /* N(MR) */ + union { + u_char ustatu_type; /* PDU type, etc */ + sscop_seq ustatu_nr; /* N(R) */ + } ustat_u; +}; +#define ustat_type ustat_u.ustatu_type +#define ustat_nr ustat_u.ustatu_nr + +/* + * Unit Data PDU + */ +struct ud_pdu { + u_char ud_type; /* PDU type, etc */ + u_char ud_rsvd[3]; /* Reserved */ +}; + +/* + * Management Data PDU + */ +struct md_pdu { + u_char md_type; /* PDU type, etc */ + u_char md_rsvd[3]; /* Reserved */ +}; + +#endif /* _UNI_SSCOP_PDU_H */ diff --git a/sys/netatm/uni/sscop_sigaa.c b/sys/netatm/uni/sscop_sigaa.c new file mode 100644 index 0000000..8f347ce --- /dev/null +++ b/sys/netatm/uni/sscop_sigaa.c @@ -0,0 +1,449 @@ +/* + * + * =================================== + * 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: sscop_sigaa.c,v 1.1 1998/04/07 23:15:11 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP Common - Process AA-signals (SAP_SSCOP) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_sigaa.c,v 1.1 1998/04/07 23:15:11 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * SSCOP_ESTABLISH_REQ / SOS_IDLE Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 buffer release parameter + * + * Returns: + * none + * + */ +void +sscop_estreq_idle(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * We currently only support BR=YES + */ + if (arg2 != SSCOP_BR_YES) { + sscop_abort(sop, "sscop: BR != YES\n"); + return; + } + + /* + * Initialize receiver window + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + + /* + * Send first BGN PDU + */ + sop->so_connctl = 1; + SEQ_INCR(sop->so_sendconn, 1); + (void) sscop_send_bgn(sop, SSCOP_SOURCE_USER); + + /* + * Reset transmitter state + */ + if (sop->so_vers == SSCOP_VERS_Q2110) + q2110_clear_xmit(sop); + else + qsaal1_reset_xmit(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for BGAK + */ + sop->so_state = SOS_OUTCONN; + + return; +} + + +/* + * SSCOP_ESTABLISH_RSP / SOS_INCONN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 buffer release parameter + * + * Returns: + * none + * + */ +void +sscop_estrsp_inconn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * We currently only support BR=YES + */ + if (arg2 != SSCOP_BR_YES) { + sscop_abort(sop, "sscop: BR != YES\n"); + return; + } + + if (sop->so_vers == SSCOP_VERS_Q2110) { + /* + * Clear transmitter buffers + */ + q2110_clear_xmit(sop); + + /* + * Initialize state variables + */ + SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin); + q2110_init_state(sop); + } else { + /* + * Reset transmitter state + */ + qsaal1_reset_xmit(sop); + } + + /* + * Send BGAK PDU + */ + (void) sscop_send_bgak(sop); + + /* + * Start polling timer + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + + /* + * Start lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * OK, we're ready for data + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * SSCOP_RELEASE_REQ / SOS_OUTCONN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 unused + * + * Returns: + * none + * + */ +void +sscop_relreq_outconn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Clear reestablishment flag + */ + sop->so_flags &= ~SOF_REESTAB; + + /* + * Send first END PDU + */ + sop->so_connctl = 1; + (void) sscop_send_end(sop, SSCOP_SOURCE_USER); + + if (sop->so_vers == SSCOP_VERS_QSAAL) + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for ENDAK + */ + sop->so_state = SOS_OUTDISC; + + return; +} + + +/* + * SSCOP_RELEASE_REQ / SOS_INCONN Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 unused + * + * Returns: + * none + * + */ +void +sscop_relreq_inconn(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Return a BGREJ PDU + */ + (void) sscop_send_bgrej(sop); + + /* + * Back to IDLE state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * SSCOP_RELEASE_REQ / SOS_READY Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing SSCOP-UU data + * arg2 unused + * + * Returns: + * none + * + */ +void +sscop_relreq_ready(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + + /* + * We don't support SSCOP-UU data + */ + if (arg1 != SSCOP_UU_NULL) + KB_FREEALL((KBuffer *)arg1); + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Send first END PDU + */ + sop->so_connctl = 1; + (void) sscop_send_end(sop, SSCOP_SOURCE_USER); + + if (sop->so_vers == SSCOP_VERS_Q2110) { + /* + * Clear out appropriate queues + */ + if (sop->so_state == SOS_READY) + q2110_prep_retrieve(sop); + else + sscop_rcvr_drain(sop); + } else { + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + } + + /* + * Set retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + /* + * Wait for ENDAK + */ + sop->so_state = SOS_OUTDISC; + + return; +} + + +/* + * SSCOP_DATA_REQ / SOS_READY Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing assured user data + * arg2 unused + * + * Returns: + * none + * + */ +void +sscop_datreq_ready(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + KBuffer *m = (KBuffer *)arg1; + + /* + * We must have a buffer (even if it contains no data) + */ + if (m == NULL) { + sscop_abort(sop, "sscop_datreq_ready: no buffer\n"); + return; + } + + /* + * Place data at end of transmission queue + */ + KB_QNEXT(m) = NULL; + if (sop->so_xmit_hd == NULL) + sop->so_xmit_hd = m; + else + KB_QNEXT(sop->so_xmit_tl) = m; + sop->so_xmit_tl = m; + + /* + * Service the transmit queues + */ + sscop_service_xmit(sop); + + return; +} + + +/* + * SSCOP_UNITDATA_REQ / SOS_* Command Processor + * + * Arguments: + * sop pointer to sscop connection block + * arg1 pointer to buffer containing unassured user data + * arg2 unused + * + * Returns: + * none + * + */ +void +sscop_udtreq_all(sop, arg1, arg2) + struct sscop *sop; + int arg1; + int arg2; +{ + KBuffer *m = (KBuffer *)arg1; + + /* + * We must have a buffer (even if it contains no data) + */ + if (m == NULL) { + sscop_abort(sop, "sscop_udtreq_all: no buffer\n"); + return; + } + + /* + * Send the data in a UD PDU + */ + (void) sscop_send_ud(sop, m); + + return; +} + diff --git a/sys/netatm/uni/sscop_sigcpcs.c b/sys/netatm/uni/sscop_sigcpcs.c new file mode 100644 index 0000000..a9bf504 --- /dev/null +++ b/sys/netatm/uni/sscop_sigcpcs.c @@ -0,0 +1,2311 @@ +/* + * + * =================================== + * 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: sscop_sigcpcs.c,v 1.2 1998/07/24 20:18:09 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP Common - Process CPCS-signals (SSCOP PDUs) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_sigcpcs.c,v 1.2 1998/07/24 20:18:09 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * No-op Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_noop(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + /* + * Just free PDU + */ + KB_FREEALL(m); + + return; +} + + +/* + * BGN PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgn_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err, source; + + if (sop->so_vers == SSCOP_VERS_Q2110) { + /* + * "Power-up Robustness" option + * + * Accept BGN regardless of BGN.N(SQ) + */ + sop->so_rcvconn = bp->bgn_nsq; + + } else { + /* + * If retransmitted BGN, reject it + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + (void) sscop_send_bgrej(sop); + return; + } + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Get Source value + */ + if (bp->bgn_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Reset receiver state variables + */ + qsaal1_reset_rcvr(sop); + } else + source = 0; + + /* + * Set initial transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + /* + * Pass connection request up to user + */ + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user's response + */ + sop->so_state = SOS_INCONN; + + return; +} + + +/* + * BGN PDU / SOS_OUTDISC Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgn_outdisc(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err, source; + + /* + * If retransmitted BGN, ACK it and send new END + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + (void) sscop_send_bgak(sop); + (void) sscop_send_end(sop, SSCOP_SOURCE_LAST); + return; + } + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Get Source value + */ + if (bp->bgn_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Reset receiver variables + */ + qsaal1_reset_rcvr(sop); + + } else + source = 0; + + /* + * Tell user about incoming connection + */ + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user's response + */ + sop->so_state = SOS_INCONN; + + return; +} + + +/* + * BGN PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgn_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err, source; + + /* + * If retransmitted BGN, ACK it and send new RS + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + (void) sscop_send_bgak(sop); + (void) sscop_send_rs(sop); + return; + } + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Get (possible) Source value + */ + if (bp->bgn_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Reset receiver variables + */ + qsaal1_reset_rcvr(sop); + + } else + source = SSCOP_SOURCE_USER; + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Now tell user of a "new" incoming connection + */ + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user's response + */ + sop->so_state = SOS_INCONN; + + return; +} + + +/* + * BGN PDU / SOS_INRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgn_inresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgn_pdu *bp = (struct bgn_pdu *)trlr; + int err, source; + + /* + * If retransmitted BGN, oops + */ + if (sscop_is_rexmit(sop, bp->bgn_nsq)) { + KB_FREEALL(m); + sscop_maa_error(sop, 'B'); + return; + } + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr)); + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Get (possible) Source value + */ + if (bp->bgn_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Reset receiver variables + */ + qsaal1_reset_rcvr(sop); + + } else { + /* + * Stop possible retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Drain receiver queues + */ + sscop_rcvr_drain(sop); + + /* + * Tell user current connection has been released + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + source = 0; + } + + /* + * Tell user of incoming connection + */ + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Wait for user's response + */ + sop->so_state = SOS_INCONN; + + return; +} + + +/* + * BGAK PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgak_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'C'); + KB_FREEALL(m); + + return; +} + + +/* + * BGAK PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgak_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_bgak_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + return; +} + + +/* + * BGAK PDU / SOS_OUTCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgak_outconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct bgak_pdu *bp = (struct bgak_pdu *)trlr; + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Initialize transmit window + */ + SEQ_SET(sop->so_sendmax, ntohl(bp->bgak_nmr)); + + /* + * Notify user of connection establishment + */ + if (sop->so_flags & SOF_REESTAB) { + KB_FREEALL(m); + STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + sop->so_flags &= ~SOF_REESTAB; + } else { + STACK_CALL(SSCOP_ESTABLISH_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Reset receiver variables + */ + qsaal1_reset_rcvr(sop); + + /* + * Start polling timer + */ + sscop_set_poll(sop); + + /* + * Start lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + } else { + /* + * Initialize state variables + */ + q2110_init_state(sop); + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + } + + /* + * OK, we're ready for data + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * BGREJ PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgrej_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'D'); + KB_FREEALL(m); + return; +} + + +/* + * BGREJ PDU / SOS_OUTCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgrej_outconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int source, uu, err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Clear reestablishment flag + */ + sop->so_flags &= ~SOF_REESTAB; + + KB_FREEALL(m); + m = NULL; + uu = SSCOP_UU_NULL; + source = SSCOP_SOURCE_SSCOP; + } else { + uu = (int)m; + source = SSCOP_SOURCE_USER; + } + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, uu, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * BGREJ PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgrej_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Report protocol error + */ + sscop_bgrej_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * BGREJ PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgrej_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Report protocol error + */ + sscop_bgrej_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * BGREJ PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_bgrej_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Report protocol error + */ + sscop_bgrej_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + } else { + /* + * Clear out appropriate queues + */ + q2110_prep_retrieve(sop); + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * END PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_end_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Return an ENDAK to peer + */ + (void) sscop_send_endak(sop); + + return; +} + + +/* + * END PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_end_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct end_pdu *ep = (struct end_pdu *)trlr; + int err, source; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Get Source value + */ + if (ep->end_type & PT_SOURCE_SSCOP) + source = SSCOP_SOURCE_SSCOP; + else + source = SSCOP_SOURCE_USER; + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, source, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * END PDU / SOS_OUTDISC Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_end_outdisc(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Release buffers + */ + KB_FREEALL(m); + + /* + * Acknowledge END + */ + (void) sscop_send_endak(sop); + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * ENDAK PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_endak_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'F'); + KB_FREEALL(m); + + return; +} + + +/* + * ENDAK PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_endak_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Report protocol error + */ + sscop_endak_error(sop, m, trlr); + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * ENDAK PDU / SOS_OUTDISC Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_endak_outdisc(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Release buffers + */ + KB_FREEALL(m); + + /* + * Notify user of connection termination + */ + STACK_CALL(SSCOP_RELEASE_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * ENDAK PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_endak_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Report protocol error + */ + sscop_endak_error(sop, m, trlr); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + } else { + /* + * Clear out appropriate queues + */ + q2110_prep_retrieve(sop); + } + + /* + * Back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * RS PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_rs_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'J'); + KB_FREEALL(m); + + return; +} + + +/* + * RS PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_rs_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Report error condition + */ + sscop_rs_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + return; +} + + +/* + * RSAK PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_rsak_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'K'); + KB_FREEALL(m); + + return; +} + + +/* + * RSAK PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_rsak_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Report error condition + */ + sscop_rsak_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + return; +} + + +/* + * RSAK PDU / SOS_OUTRESYN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_rsak_outresyn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct rsak_q2110_pdu *rp = (struct rsak_q2110_pdu *)trlr; + int err; + + /* + * Stop retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = 0; + + /* + * Notify user of resynchronization completion + */ + STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku, + sop->so_connvc, 0, 0, err); + if (err) { + KB_FREEALL(m); + sscop_abort(sop, "stack memory\n"); + return; + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Start the polling timer + */ + sscop_set_poll(sop); + + /* + * Start lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + } else { + /* + * Initialize state variables + */ + SEQ_SET(sop->so_sendmax, ntohl(rp->rsak_nmr)); + q2110_init_state(sop); + + /* + * Start data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + } + + /* + * Free buffers + */ + KB_FREEALL(m); + + /* + * Now go back to data transfer state + */ + sop->so_state = SOS_READY; + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; +} + + +/* + * SD PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_sd_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'A'); + KB_FREEALL(m); + + return; +} + + +/* + * SD PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_sd_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_sd_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + return; +} + + +/* + * SD PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_sd_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Record error condition + */ + sscop_sd_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * POLL PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_poll_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'G'); + KB_FREEALL(m); + + return; +} + + +/* + * POLL PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_poll_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Report error condition + */ + sscop_poll_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + return; +} + + +/* + * POLL PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_poll_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Record error condition + */ + sscop_poll_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * STAT PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_stat_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'H'); + KB_FREEALL(m); + + return; +} + + +/* + * STAT PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_stat_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Report error condition + */ + sscop_stat_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + return; +} + + +/* + * STAT PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_stat_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Record error condition + */ + sscop_stat_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * STAT PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_stat_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct stat_pdu *sp = (struct stat_pdu *)trlr; + struct pdu_hdr *php; + KBuffer *m0 = m; + sscop_seq seq1, seq2, opa; + int cnt = 0; + + NTOHL(sp->stat_nps); + NTOHL(sp->stat_nmr); + NTOHL(sp->stat_nr); + + /* + * Validate peer's received poll sequence number + */ + if (SEQ_GT(sop->so_pollack, sp->stat_nps, sop->so_pollack) || + SEQ_GT(sp->stat_nps, sop->so_pollsend, sop->so_pollack)) { + /* + * Bad poll sequence number + */ + sscop_maa_error(sop, 'R'); + goto goterr; + } + + /* + * Validate peer's current receive data sequence number + */ + if (SEQ_GT(sop->so_ack, sp->stat_nr, sop->so_ack) || + SEQ_GT(sp->stat_nr, sop->so_send, sop->so_ack)) { + /* + * Bad data sequence number + */ + sscop_maa_error(sop, 'S'); + goto goterr; + } + + /* + * Free acknowledged PDUs + */ + for (seq1 = sop->so_ack, SEQ_SET(seq2, sp->stat_nr); + SEQ_LT(seq1, seq2, sop->so_ack); + SEQ_INCR(seq1, 1)) { + sscop_pack_free(sop, seq1); + } + + /* + * Update transmit state variables + */ + opa = sop->so_pollack; + sop->so_ack = seq2; + SEQ_SET(sop->so_pollack, sp->stat_nps); + SEQ_SET(sop->so_sendmax, sp->stat_nmr); + + /* + * Get first element in STAT list + */ + while (m && (KB_LEN(m) == 0)) + m = KB_NEXT(m); + if (m == NULL) + goto done; + m = sscop_stat_getelem(m, &seq1); + + /* + * Make sure there's a second element too + */ + if (m == NULL) + goto done; + + /* + * Validate first element (start of missing pdus) + */ + if (SEQ_GT(sop->so_ack, seq1, sop->so_ack) || + SEQ_GEQ(seq1, sop->so_send, sop->so_ack)) { + /* + * Bad element sequence number + */ + sscop_maa_error(sop, 'S'); + goto goterr; + } + + /* + * Loop thru all STAT elements in list + */ + while (m) { + /* + * Get next even element (start of received pdus) + */ + m = sscop_stat_getelem(m, &seq2); + + /* + * Validate seqence number + */ + if (SEQ_GEQ(seq1, seq2, sop->so_ack) || + SEQ_GT(seq2, sop->so_send, sop->so_ack)) { + /* + * Bad element sequence number + */ + sscop_maa_error(sop, 'S'); + goto goterr; + } + + /* + * Process each missing sequence number in this gap + */ + while (SEQ_LT(seq1, seq2, sop->so_ack)) { + /* + * Find corresponding SD PDU on pending ack queue + */ + php = sscop_pack_locate(sop, seq1); + if (php == NULL) { + sscop_maa_error(sop, 'S'); + goto goterr; + } + + /* + * Retransmit this SD PDU only if it was last sent + * during an earlier poll sequence and it's not + * already scheduled for retranmission. + */ + if (SEQ_LT(php->ph_nps, sp->stat_nps, opa) && + (php->ph_rexmit_lk == NULL) && + (sop->so_rexmit_tl != php)) { + /* + * Put PDU on retransmit queue and schedule + * transmit servicing + */ + sscop_rexmit_insert(sop, php); + sop->so_flags |= SOF_XMITSRVC; + cnt++; + } + + /* + * Bump to next sequence number + */ + SEQ_INCR(seq1, 1); + } + + /* + * Now process series of acknowledged PDUs + * + * Get next odd element (start of missing pdus), + * but make sure there is one and that it's valid + */ + if (m == NULL) + goto done; + m = sscop_stat_getelem(m, &seq2); + if (SEQ_GEQ(seq1, seq2, sop->so_ack) || + SEQ_GT(seq2, sop->so_send, sop->so_ack)) { + /* + * Bad element sequence number + */ + sscop_maa_error(sop, 'S'); + goto goterr; + } + + /* + * Process each acked sequence number + */ + while (SEQ_LT(seq1, seq2, sop->so_ack)) { + /* + * Can we clear transmit buffers ?? + */ + if ((sop->so_flags & SOF_NOCLRBUF) == 0) { + /* + * Yes, free acked buffers + */ + sscop_pack_free(sop, seq1); + } + + /* + * Bump to next sequence number + */ + SEQ_INCR(seq1, 1); + } + } + +done: + /* + * Free PDU buffer chain + */ + KB_FREEALL(m0); + + /* + * Report retransmitted PDUs + */ + if (cnt) + sscop_maa_error(sop, 'V'); + + /* + * Record transmit window closed transitions + */ + if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)) { + if (sop->so_flags & SOF_NOCREDIT) { + sop->so_flags &= ~SOF_NOCREDIT; + sscop_maa_error(sop, 'X'); + } + } else { + if ((sop->so_flags & SOF_NOCREDIT) == 0) { + sop->so_flags |= SOF_NOCREDIT; + sscop_maa_error(sop, 'W'); + } + } + + if (sop->so_vers == SSCOP_VERS_QSAAL) + /* + * Restart lost poll/stat timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + else { + /* + * Determine new polling phase + */ + if ((sop->so_timer[SSCOP_T_POLL] != 0) && + ((sop->so_flags & SOF_KEEPALIVE) == 0)) { + /* + * Remain in active phase - reset NO-RESPONSE timer + */ + sop->so_timer[SSCOP_T_NORESP] = + sop->so_parm.sp_timeresp; + + } else if (sop->so_timer[SSCOP_T_IDLE] == 0) { + /* + * Go from transient to idle phase + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + sop->so_timer[SSCOP_T_NORESP] = 0; + sop->so_timer[SSCOP_T_IDLE] = sop->so_parm.sp_timeidle; + } + } + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; + +goterr: + /* + * Protocol/parameter error encountered + */ + + /* + * Free PDU buffer chain + */ + KB_FREEALL(m0); + + if (sop->so_vers == SSCOP_VERS_QSAAL) + /* + * Reestablish a new connection + */ + qsaal1_reestablish(sop); + else + /* + * Initiate error recovery + */ + q2110_error_recovery(sop); + + return; +} + + +/* + * USTAT PDU / Protocol Error + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_ustat_error(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Record error condition + */ + sscop_maa_error(sop, 'I'); + KB_FREEALL(m); + + return; +} + + +/* + * USTAT PDU / SOS_IDLE Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_ustat_idle(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * Report error condition + */ + sscop_ustat_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + return; +} + + +/* + * USTAT PDU / SOS_INCONN Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_ustat_inconn(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Record error condition + */ + sscop_ustat_error(sop, m, trlr); + + /* + * Return an END to peer + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Notify user of connection failure + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + sscop_abort(sop, "stack memory\n"); + return; + } + + /* + * Go back to idle state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * USTAT PDU / SOS_READY Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_ustat_ready(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + struct ustat_pdu *up = (struct ustat_pdu *)trlr; + struct pdu_hdr *php; + sscop_seq seq1, seq2; + + NTOHL(up->ustat_nmr); + NTOHL(up->ustat_nr); + + /* + * Validate peer's current receive data sequence number + */ + if (SEQ_GT(sop->so_ack, up->ustat_nr, sop->so_ack) || + SEQ_GEQ(up->ustat_nr, sop->so_send, sop->so_ack)) { + /* + * Bad data sequence number + */ + goto goterr; + } + + /* + * Free acknowledged PDUs + */ + for (seq1 = sop->so_ack, SEQ_SET(seq2, up->ustat_nr); + SEQ_LT(seq1, seq2, sop->so_ack); + SEQ_INCR(seq1, 1)) { + sscop_pack_free(sop, seq1); + } + + /* + * Update transmit state variables + */ + sop->so_ack = seq2; + SEQ_SET(sop->so_sendmax, up->ustat_nmr); + + /* + * Get USTAT list elements + */ + SEQ_SET(seq1, ntohl(up->ustat_le1)); + SEQ_SET(seq2, ntohl(up->ustat_le2)); + + /* + * Validate elements + */ + if (SEQ_GT(sop->so_ack, seq1, sop->so_ack) || + SEQ_GEQ(seq1, seq2, sop->so_ack) || + SEQ_GEQ(seq2, sop->so_send, sop->so_ack)) { + /* + * Bad element sequence number + */ + goto goterr; + } + + /* + * Process each missing sequence number in this gap + */ + while (SEQ_LT(seq1, seq2, sop->so_ack)) { + /* + * Find corresponding SD PDU on pending ack queue + */ + php = sscop_pack_locate(sop, seq1); + if (php == NULL) { + goto goterr; + } + + /* + * Retransmit this SD PDU if it's not + * already scheduled for retranmission. + */ + if ((php->ph_rexmit_lk == NULL) && + (sop->so_rexmit_tl != php)) { + /* + * Put PDU on retransmit queue and schedule + * transmit servicing + */ + sscop_rexmit_insert(sop, php); + sop->so_flags |= SOF_XMITSRVC; + } + + /* + * Bump to next sequence number + */ + SEQ_INCR(seq1, 1); + } + + /* + * Report retransmitted PDUs + */ + sscop_maa_error(sop, 'V'); + + /* + * Free PDU buffer chain + */ + KB_FREEALL(m); + + /* + * See if transmit queues need servicing + */ + if (sop->so_flags & SOF_XMITSRVC) + sscop_service_xmit(sop); + + return; + +goterr: + /* + * Protocol/parameter error encountered + */ + sscop_maa_error(sop, 'T'); + + /* + * Free PDU buffer chain + */ + KB_FREEALL(m); + + if (sop->so_vers == SSCOP_VERS_QSAAL) + /* + * Reestablish a new connection + */ + qsaal1_reestablish(sop); + else + /* + * Initiate error recovery + */ + q2110_error_recovery(sop); + + return; +} + + +/* + * UD PDU / SOS_* Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_ud_all(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + int err; + + /* + * Pass data up to user + */ + STACK_CALL(SSCOP_UNITDATA_IND, sop->so_upper, sop->so_toku, + sop->so_connvc, (int)m, 0, err); + if (err) + KB_FREEALL(m); + return; +} + + +/* + * MD PDU / SOS_* Processor + * + * Arguments: + * sop pointer to sscop connection block + * m pointer to PDU buffer (without trailer) + * trlr pointer to PDU trailer + * + * Returns: + * none + * + */ +void +sscop_md_all(sop, m, trlr) + struct sscop *sop; + KBuffer *m; + caddr_t trlr; +{ + + /* + * We don't support MD PDUs + */ + KB_FREEALL(m); + return; +} + diff --git a/sys/netatm/uni/sscop_subr.c b/sys/netatm/uni/sscop_subr.c new file mode 100644 index 0000000..2d01229 --- /dev/null +++ b/sys/netatm/uni/sscop_subr.c @@ -0,0 +1,972 @@ +/* + * + * =================================== + * 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: sscop_subr.c,v 1.7 1998/08/26 23:29:19 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP - Subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_subr.c,v 1.7 1998/08/26 23:29:19 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static int sscop_proc_xmit __P((struct sscop *)); + + +/* + * Get Next Element from STAT PDU + * + * Arguments: + * m pointer to current buffer in STAT PDU + * pelem pointer to location to store element value + * + * Returns: + * addr pointer to updated current buffer in STAT PDU + * + */ +KBuffer * +sscop_stat_getelem(m, pelem) + KBuffer *m; + sscop_seq *pelem; +{ + caddr_t cp; + + /* + * Get to start of element + * + * Note that we always ensure that the current buffer has + * at least one byte of the next element. + */ + KB_DATASTART(m, cp, caddr_t); + + /* + * See how much of element is in this buffer + */ + if (KB_LEN(m) >= sizeof(sscop_seq)) { + /* + * Get element from this buffer + */ + if ((int)cp & (sizeof(sscop_seq) - 1)) + KM_COPY(cp, (caddr_t)pelem, sizeof(sscop_seq)); + else + *pelem = *(sscop_seq *)cp; + + /* + * Update buffer controls + */ + KB_HEADADJ(m, -sizeof(sscop_seq)); + } else { + /* + * Get element split between two buffers + */ + int i, j; + + /* + * Copy what's in this buffer + */ + i = KB_LEN(m); + KM_COPY(cp, (caddr_t)pelem, i); + KB_LEN(m) = 0; + + /* + * Now get to next buffer + */ + while (m && (KB_LEN(m) == 0)) + m = KB_NEXT(m); + + /* + * And copy remainder of element + */ + j = sizeof(sscop_seq) - i; + KB_DATASTART(m, cp, caddr_t); + KM_COPY(cp, (caddr_t)pelem + i, j); + + /* + * Update buffer controls + */ + KB_HEADADJ(m, -j); + } + + /* + * Put element (sequence number) into host order + */ + NTOHL(*pelem); + + /* + * Get pointers set for next call + */ + while (m && (KB_LEN(m) == 0)) + m = KB_NEXT(m); + + return (m); +} + + +/* + * Locate SD PDU on Pending Ack Queue + * + * Arguments: + * sop pointer to sscop connection block + * seq sequence number of PDU to locate + * + * Returns: + * addr pointer to located PDU header + * 0 SD PDU sequence number not found + * + */ +struct pdu_hdr * +sscop_pack_locate(sop, seq) + struct sscop *sop; + sscop_seq seq; +{ + struct pdu_hdr *php; + + /* + * Loop thru queue until we either find the PDU or the queue's + * sequence numbers are greater than the PDU's sequence number, + * indicating that the PDU is not on the queue. + */ + for (php = sop->so_pack_hd; php; php = php->ph_pack_lk) { + if (php->ph_ns == seq) + break; + + if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) { + php = NULL; + break; + } + } + + return (php); +} + + +/* + * Free Acknowledged SD PDU + * + * Arguments: + * sop pointer to sscop connection block + * seq sequence number of PDU to free + * + * Returns: + * none + * + */ +void +sscop_pack_free(sop, seq) + struct sscop *sop; + sscop_seq seq; +{ + struct pdu_hdr *php, *prev; + + /* + * Unlink PDU from pending ack queue + * + * First, check for an empty queue + */ + php = sop->so_pack_hd; + if (php == NULL) + return; + + /* + * Now check for PDU at head of queue + */ + if (php->ph_ns == seq) { + if ((sop->so_pack_hd = php->ph_pack_lk) == NULL) + sop->so_pack_tl = NULL; + goto found; + } + + /* + * Otherwise, loop thru queue until we either find the PDU or + * the queue's sequence numbers are greater than the PDU's + * sequence number, indicating that the PDU is not on the queue. + */ + prev = php; + php = php->ph_pack_lk; + while (php) { + if (php->ph_ns == seq) { + if ((prev->ph_pack_lk = php->ph_pack_lk) == NULL) + sop->so_pack_tl = prev; + goto found; + } + + if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) + return; + + prev = php; + php = php->ph_pack_lk; + } + + return; + +found: + /* + * We've got the ack'ed PDU - unlink it from retransmit queue + */ + sscop_rexmit_unlink(sop, php); + + /* + * Free PDU buffers + */ + KB_FREEALL(php->ph_buf); + + return; +} + + +/* + * Insert SD PDU into Retransmit Queue + * + * Arguments: + * sop pointer to sscop connection block + * php pointer to SD PDU header + * + * Returns: + * none + * + */ +void +sscop_rexmit_insert(sop, php) + struct sscop *sop; + struct pdu_hdr *php; +{ + struct pdu_hdr *curr, *next; + sscop_seq seq = php->ph_ns; + + /* + * Check for an empty queue + */ + if ((curr = sop->so_rexmit_hd) == NULL) { + php->ph_rexmit_lk = NULL; + sop->so_rexmit_hd = php; + sop->so_rexmit_tl = php; + return; + } + + /* + * Now see if PDU belongs at head of queue + */ + if (SEQ_LT(seq, curr->ph_ns, sop->so_ack)) { + php->ph_rexmit_lk = curr; + sop->so_rexmit_hd = php; + return; + } + + /* + * Otherwise, loop thru the queue until we find the + * proper insertion point for the PDU + */ + while (next = curr->ph_rexmit_lk) { + if (SEQ_LT(seq, next->ph_ns, sop->so_ack)) { + php->ph_rexmit_lk = next; + curr->ph_rexmit_lk = php; + return; + } + curr = next; + } + + /* + * Insert PDU at end of queue + */ + php->ph_rexmit_lk = NULL; + curr->ph_rexmit_lk = php; + sop->so_rexmit_tl = php; + + return; +} + + +/* + * Unlink SD PDU from Retransmit Queue + * + * Arguments: + * sop pointer to sscop connection block + * php pointer to PDU header to unlink + * + * Returns: + * none + * + */ +void +sscop_rexmit_unlink(sop, php) + struct sscop *sop; + struct pdu_hdr *php; +{ + struct pdu_hdr *curr; + + /* + * See if PDU is on retransmit queue + */ + if ((php->ph_rexmit_lk == NULL) && (sop->so_rexmit_tl != php)) + return; + + /* + * It's here somewhere, so first check for the PDU at the + * head of the queue + */ + if (php == sop->so_rexmit_hd) { + if ((sop->so_rexmit_hd = php->ph_rexmit_lk) == NULL) + sop->so_rexmit_tl = NULL; + php->ph_rexmit_lk = NULL; + return; + } + + /* + * Otherwise, loop thru the queue until we find the PDU + */ + for (curr = sop->so_rexmit_hd; curr; curr = curr->ph_rexmit_lk) { + if (curr->ph_rexmit_lk == php) + break; + } + if (curr) { + if ((curr->ph_rexmit_lk = php->ph_rexmit_lk) == NULL) + sop->so_rexmit_tl = curr; + } else { + log(LOG_ERR, + "sscop_rexmit_unlink: Not found - sop=0x%x, php=0x%x\n", + (int)sop, (int)php); +#ifdef DIAGNOSTIC + panic("sscop_rexmit_unlink: Not found"); +#endif + } + php->ph_rexmit_lk = NULL; + + return; +} + + +/* + * Drain Transmission Queues + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +sscop_xmit_drain(sop) + struct sscop *sop; +{ + KBuffer *m; + struct pdu_hdr *php; + + /* + * Free transmission queue buffers + */ + while (m = sop->so_xmit_hd) { + sop->so_xmit_hd = KB_QNEXT(m); + KB_FREEALL(m); + } + sop->so_xmit_tl = NULL; + + /* + * Free retransmission queue + * + * All retranmission buffers are also on the pending ack + * queue (but not the converse), so we just clear the queue + * pointers here and do all the real work below. + */ + sop->so_rexmit_hd = NULL; + sop->so_rexmit_tl = NULL; + + /* + * Free pending ack queue buffers + */ + while (php = sop->so_pack_hd) { + sop->so_pack_hd = php->ph_pack_lk; + KB_FREEALL(php->ph_buf); + } + sop->so_pack_tl = NULL; + + /* + * Clear service required flag + */ + sop->so_flags &= ~SOF_XMITSRVC; + + return; +} + + +/* + * Insert SD PDU into Receive Queue + * + * Arguments: + * sop pointer to sscop connection block + * php pointer to SD PDU header + * + * Returns: + * 0 PDU successfully inserted into queue + * 1 duplicate sequence number PDU on queue, PDU not inserted + * + */ +int +sscop_recv_insert(sop, php) + struct sscop *sop; + struct pdu_hdr *php; +{ + struct pdu_hdr *curr, *next; + sscop_seq seq = php->ph_ns; + + /* + * Check for an empty queue + */ + if ((curr = sop->so_recv_hd) == NULL) { + php->ph_recv_lk = NULL; + sop->so_recv_hd = php; + sop->so_recv_tl = php; + return (0); + } + + /* + * Now see if PDU belongs at head of queue + */ + if (SEQ_LT(seq, curr->ph_ns, sop->so_rcvnext)) { + php->ph_recv_lk = curr; + sop->so_recv_hd = php; + return (0); + } + + /* + * Otherwise, loop thru the queue until we find the + * proper insertion point for the PDU. We also check + * to make sure there isn't a PDU already on the queue + * with a matching sequence number. + */ + while (next = curr->ph_recv_lk) { + if (SEQ_LT(seq, next->ph_ns, sop->so_rcvnext)) { + if (seq == curr->ph_ns) + return (1); + php->ph_recv_lk = next; + curr->ph_recv_lk = php; + return (0); + } + curr = next; + } + + /* + * Insert PDU at end of queue + */ + if (seq == curr->ph_ns) + return (1); + php->ph_recv_lk = NULL; + curr->ph_recv_lk = php; + sop->so_recv_tl = php; + + return (0); +} + + +/* + * Drain Receiver Queues + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +sscop_rcvr_drain(sop) + struct sscop *sop; +{ + struct pdu_hdr *php; + + /* + * Free receive queue buffers + */ + while (php = sop->so_recv_hd) { + sop->so_recv_hd = php->ph_recv_lk; + KB_FREEALL(php->ph_buf); + } + sop->so_recv_tl = NULL; + + return; +} + + +/* + * Service connection's transmit queues + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +sscop_service_xmit(sop) + struct sscop *sop; +{ + KBuffer *m, *n; + struct pdu_hdr *php; + int err = 0, pollsent = 0; + + /* + * Initially assume we need service + */ + sop->so_flags |= SOF_XMITSRVC; + + /* + * Loop until done with queues + * + * (Congestion control will be added later) + */ + while (1) { + if (php = sop->so_rexmit_hd) { + + /* + * Send SD PDU from retransmit queue + * + * First, get a copy of the PDU to send + */ + m = php->ph_buf; + if (KB_LEN(m) == 0) + m = KB_NEXT(m); + KB_COPY(m, 0, KB_COPYALL, n, KB_F_NOWAIT); + if (n == NULL) { + err = 1; + break; + } + + /* + * Now pass it down the stack + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, + sop->so_tokl, sop->so_connvc, (int)n, 0, err); + if (err) { + KB_FREEALL(n); + break; + } + + /* + * PDU is on its way, so remove it from + * the retransmit queue + */ + if (sop->so_rexmit_tl == php) { + sop->so_rexmit_hd = NULL; + sop->so_rexmit_tl = NULL; + } else { + sop->so_rexmit_hd = php->ph_rexmit_lk; + } + php->ph_rexmit_lk = NULL; + + /* + * Update PDU's poll sequence + */ + php->ph_nps = sop->so_pollsend; + + } else if (sop->so_xmit_hd) { + + /* + * Newly arrived data waiting to be sent. + * See if transmit window allows us to send it. + */ + if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)){ + /* + * OK, send SD PDU from transmission queue + */ + err = sscop_proc_xmit(sop); + if (err) + break; + } else { + /* + * Can't send now, so leave idle phase. + */ + if (sop->so_timer[SSCOP_T_IDLE] != 0) { + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_timer[SSCOP_T_NORESP] = + sop->so_parm.sp_timeresp; + err = 1; + } + break; + } + + } else { + + /* + * We're finished, so clear service required flag + */ + sop->so_flags &= ~SOF_XMITSRVC; + break; + } + + /* + * We've sent another SD PDU + */ + sop->so_polldata++; + + /* + * Transition into active (polling) phase + */ + if (sop->so_timer[SSCOP_T_POLL] != 0) { + if (sop->so_flags & SOF_KEEPALIVE) { + /* + * Leaving transient phase + */ + sop->so_flags &= ~SOF_KEEPALIVE; + sop->so_timer[SSCOP_T_POLL] = + sop->so_parm.sp_timepoll; + } + } else { + /* + * Leaving idle phase + */ + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_timer[SSCOP_T_NORESP] = + sop->so_parm.sp_timeresp; + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + } + + /* + * Let's see if we need to send a POLL yet + */ + if (sop->so_polldata < sop->so_parm.sp_maxpd) + continue; + + /* + * Yup, send another poll out + */ + SEQ_INCR(sop->so_pollsend, 1); + (void) sscop_send_poll(sop); + pollsent++; + + /* + * Reset data counter for this poll cycle + */ + sop->so_polldata = 0; + + /* + * Restart polling timer in active phase + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + } + + /* + * If we need/want to send a poll, but haven't sent any yet + * on this servicing, send one now + */ + if (err && (pollsent == 0)) { + /* + * Send poll + */ + SEQ_INCR(sop->so_pollsend, 1); + (void) sscop_send_poll(sop); + + /* + * Reset data counter for this poll cycle + */ + sop->so_polldata = 0; + + /* + * Restart polling timer in active phase + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_flags &= ~SOF_KEEPALIVE; + } + + return; +} + + +/* + * Process Transmission Queue PDU + * + * For the first entry on the transmission queue: add a PDU header and + * trailer, send a copy of the PDU down the stack and move the PDU from + * the transmission queue to the pending ack queue. + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * 0 head of transmission queue successfully processed + * else processing error, tranmission queue unchanged + * + */ +static int +sscop_proc_xmit(sop) + struct sscop *sop; +{ + KBuffer *m, *ml, *n; + struct pdu_hdr *php; + sscop_seq seq; + int len = 0, err; + int pad, trlen, space; + u_char *cp; + + /* + * Get first buffer chain on queue + */ + if ((m = sop->so_xmit_hd) == NULL) + return (0); + + /* + * Count data and get to last buffer in chain + */ + for (ml = m; ; ml = KB_NEXT(ml)) { + len += KB_LEN(ml); + if (KB_NEXT(ml) == NULL) + break; + } + + /* + * Verify data length + */ + if (len > sop->so_parm.sp_maxinfo) { + sscop_abort(sop, "sscop: maximum data size exceeded\n"); + return (1); + } + + /* + * Get space for PDU header + */ + KB_HEADROOM(m, space); + if (space < sizeof(struct pdu_hdr)) { + /* + * Allocate & link buffer for header + */ + KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER); + if (n == NULL) + return (1); + + KB_LEN(n) = 0; + KB_HEADSET(n, sizeof(struct pdu_hdr)); + KB_LINKHEAD(n, m); + KB_QNEXT(n) = KB_QNEXT(m); + KB_QNEXT(m) = NULL; + sop->so_xmit_hd = n; + if (sop->so_xmit_tl == m) + sop->so_xmit_tl = n; + m = n; + } + + /* + * Figure out how much padding we'll need + */ + pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len; + trlen = pad + sizeof(struct sd_pdu); + + /* + * Now get space for PDU trailer and padding + */ + KB_TAILROOM(ml, space); + if (space < trlen) { + /* + * Allocate & link buffer for pad and trailer + */ + KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER); + if (n == NULL) + return (1); + + KB_LEN(n) = 0; + KB_LINK(n, ml); + ml = n; + } + + /* + * Build the PDU trailer + * + * Since we can't be sure of alignment in the buffers, we + * have to move this a byte at a time and we have to be + * careful with host byte order issues. + */ + KB_DATASTART(ml, cp, u_char *); + cp += KB_LEN(ml) + pad; + *cp++ = (pad << PT_PAD_SHIFT) | PT_SD; + seq = sop->so_send; + *(cp + 2) = (u_char)(seq & 0xff); + seq >>= 8; + *(cp + 1) = (u_char)(seq & 0xff); + seq >>= 8; + *(cp) = (u_char)(seq & 0xff); + KB_LEN(ml) += trlen; + + /* + * Get a copy of the SD PDU to send + */ + if (KB_LEN(m) == 0) + n = KB_NEXT(m); + else + n = m; + KB_COPY(n, 0, KB_COPYALL, n, KB_F_NOWAIT); + if (n == NULL) { + KB_LEN(ml) -= trlen; + return (1); + } + + /* + * Now pass copy down the stack + */ + STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl, + sop->so_connvc, (int)n, 0, err); + if (err) { + KB_FREEALL(n); + KB_LEN(ml) -= trlen; + return (1); + } + + /* + * PDU is on its way, so remove buffer from + * the transmission queue + */ + if (sop->so_xmit_tl == m) { + sop->so_xmit_hd = NULL; + sop->so_xmit_tl = NULL; + } else { + sop->so_xmit_hd = KB_QNEXT(m); + } + KB_QNEXT(m) = NULL; + + /* + * Build PDU header + * + * We can at least assume/require that the start of + * the user data is aligned. Also note that we don't + * include this header in the buffer len/offset fields. + */ + KB_DATASTART(m, php, struct pdu_hdr *); + php--; + php->ph_ns = sop->so_send; + php->ph_nps = sop->so_pollsend; + php->ph_buf = m; + php->ph_rexmit_lk = NULL; + php->ph_pack_lk = NULL; + + /* + * Put PDU onto the pending ack queue + */ + if (sop->so_pack_hd == NULL) + sop->so_pack_hd = php; + else + sop->so_pack_tl->ph_pack_lk = php; + sop->so_pack_tl = php; + + /* + * Finally, bump send sequence number + */ + SEQ_INCR(sop->so_send, 1); + + return (0); +} + + +/* + * Detect Retransmitted PDUs + * + * Arguments: + * sop pointer to sscop connection block + * nsq connection sequence value (N(SQ)) from received PDU + * + * Returns: + * 0 received PDU was NOT retransmitted + * 1 received PDU was retransmitted + * + */ +int +sscop_is_rexmit(sop, nsq) + struct sscop *sop; + u_char nsq; +{ + + /* + * For Q.SAAL1, N(SQ) doesn't exist + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) + return (0); + + /* + * If we've already received the N(SQ) value, + * then this PDU has been retransmitted + */ + if (nsq == sop->so_rcvconn) + return (1); + + /* + * New PDU, save its N(SQ) + */ + sop->so_rcvconn = nsq; + + return (0); +} + + +/* + * Start connection poll timer + * + * Arguments: + * sop pointer to sscop connection block + * + * Returns: + * none + * + */ +void +sscop_set_poll(sop) + struct sscop *sop; +{ + + /* + * Decide which polling timer value to set + */ + if ((sop->so_xmit_hd != NULL) || SEQ_NEQ(sop->so_send, sop->so_ack)) { + /* + * Data outstanding, poll frequently + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll; + sop->so_flags &= ~SOF_KEEPALIVE; + } else { + /* + * No data outstanding, just poll occassionally + */ + sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timekeep; + sop->so_flags |= SOF_KEEPALIVE; + } + + return; +} + diff --git a/sys/netatm/uni/sscop_timer.c b/sys/netatm/uni/sscop_timer.c new file mode 100644 index 0000000..8c23344b --- /dev/null +++ b/sys/netatm/uni/sscop_timer.c @@ -0,0 +1,576 @@ +/* + * + * =================================== + * 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: sscop_timer.c,v 1.6 1998/04/07 23:21:48 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP - Timer processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_timer.c,v 1.6 1998/04/07 23:21:48 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static void sscop_poll_expire __P((struct sscop *)); +static void sscop_noresponse_expire __P((struct sscop *)); +static void sscop_cc_expire __P((struct sscop *)); +static void sscop_idle_expire __P((struct sscop *)); + +/* + * Local variables + */ +static void (*sscop_expired[SSCOP_T_NUM]) __P((struct sscop *)) = { + sscop_poll_expire, + sscop_noresponse_expire, + sscop_cc_expire, + sscop_idle_expire +}; + + +/* + * Process an SSCOP timer tick + * + * This function is called SSCOP_HZ times a second in order to update + * all of the sscop connection timers. The sscop expiration function + * will be called to process all timer expirations. + * + * Called at splnet. + * + * Arguments: + * tip pointer to sscop timer control block + * + * Returns: + * none + * + */ +void +sscop_timeout(tip) + struct atm_time *tip; +{ + struct sscop *sop, **sprev; + int i; + + + /* + * Schedule next timeout + */ + atm_timeout(&sscop_timer, ATM_HZ/SSCOP_HZ, sscop_timeout); + + /* + * Run through all connections, updating each active timer. + * If an expired timer is found, notify that entry. + */ + sprev = &sscop_head; + while (sop = *sprev) { + + /* + * Check out each timer + */ + for (i =0; i < SSCOP_T_NUM; i++) { + + /* + * Decrement timer if it's active + */ + if (sop->so_timer[i] && (--sop->so_timer[i] == 0)) { + +#ifdef DIAGNOSTIC + { + static char *tn[] = { + "POLL", + "NORESPONSE", + "CC", + "IDLE" + }; + ATM_DEBUG3("sscop_timer: %s expired, sop=0x%x, state=%d\n", + tn[i], (int)sop, sop->so_state); + } +#endif + + /* + * Expired timer - process it + */ + (*sscop_expired[i])(sop); + + /* + * Make sure connection still exists + */ + if (*sprev != sop) + break; + } + } + + /* + * Update previous pointer if current control + * block wasn't deleted + */ + if (*sprev == sop) + sprev = &sop->so_next; + } +} + + +/* + * Process an SSCOP Timer_POLL expiration + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * none + * + */ +static void +sscop_poll_expire(sop) + struct sscop *sop; +{ + + /* + * Validate current state + */ + if ((sop->so_state != SOS_READY) && + ((sop->so_state != SOS_INRESYN) || + (sop->so_vers != SSCOP_VERS_QSAAL))) { + log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n", + "Timer_POLL", (int)sop, sop->so_state); + return; + } + + /* + * Send next poll along its way + */ + SEQ_INCR(sop->so_pollsend, 1); + (void) sscop_send_poll(sop); + + /* + * Reset data counter for this poll cycle + */ + sop->so_polldata = 0; + + /* + * Reset polling timer + */ + sscop_set_poll(sop); + + return; +} + + +/* + * Process an SSCOP Timer_IDLE expiration + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * none + * + */ +static void +sscop_idle_expire(sop) + struct sscop *sop; +{ + + /* + * Timer_IDLE only valid in READY state + */ + if (sop->so_state != SOS_READY) { + log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n", + "Timer_IDLE", (int)sop, sop->so_state); + return; + } + + /* + * Send next poll along its way + */ + SEQ_INCR(sop->so_pollsend, 1); + (void) sscop_send_poll(sop); + + /* + * Reset data counter for this poll cycle + */ + sop->so_polldata = 0; + + /* + * Start NO-RESPONSE timer + */ + sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp; + + /* + * Reset polling timer + */ + sscop_set_poll(sop); + + return; +} + + +/* + * Process an SSCOP Timer_NORESPONSE expiration + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * none + * + */ +static void +sscop_noresponse_expire(sop) + struct sscop *sop; +{ + int err; + + /* + * Validate current state + */ + if ((sop->so_state != SOS_READY) && + ((sop->so_state != SOS_INRESYN) || + (sop->so_vers != SSCOP_VERS_QSAAL))) { + log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n", + "Timer_NORESPONSE", (int)sop, sop->so_state); + return; + } + + /* + * Peer seems to be dead, so terminate session + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, + sop->so_toku, sop->so_connvc, + SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + /* + * Error, force retry + */ + sop->so_timer[SSCOP_T_NORESP] = 1; + return; + } + + /* + * Stop data transfer timers + */ + sop->so_timer[SSCOP_T_POLL] = 0; + sop->so_timer[SSCOP_T_IDLE] = 0; + sop->so_flags &= ~SOF_KEEPALIVE; + + /* + * Notify peer of termination + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Report peer's failure + */ + sscop_maa_error(sop, 'P'); + + if (sop->so_vers == SSCOP_VERS_QSAAL) + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + else + /* + * Clear out appropriate queues + */ + q2110_prep_retrieve(sop); + + /* + * Return to IDLE state + */ + sop->so_state = SOS_IDLE; + + return; +} + + +/* + * Process an SSCOP Timer_CC expiration + * + * Arguments: + * sop pointer to sscop connection control block + * + * Returns: + * none + * + */ +static void +sscop_cc_expire(sop) + struct sscop *sop; +{ + int err; + + /* + * Process timeout based on protocol state + */ + switch (sop->so_state) { + + case SOS_OUTCONN: + /* + * No response to our BGN yet + */ + if (sop->so_connctl < sop->so_parm.sp_maxcc) { + + /* + * Send another BGN PDU + */ + sop->so_connctl++; + (void) sscop_send_bgn(sop, SSCOP_SOURCE_USER); + + /* + * Restart retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + } else { + + /* + * Retransmit limit exceeded, terminate session + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, + sop->so_toku, sop->so_connvc, + SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + /* + * Error, force retry + */ + sop->so_timer[SSCOP_T_CC] = 1; + break; + } + + /* + * Notify peer of termination + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Report establishment failure + */ + sscop_maa_error(sop, 'O'); + + /* + * Clear reestablishment flag + */ + sop->so_flags &= ~SOF_REESTAB; + + /* + * Return to IDLE state + */ + sop->so_state = SOS_IDLE; + } + break; + + case SOS_OUTDISC: + /* + * No response to our END yet + */ + if (sop->so_connctl < sop->so_parm.sp_maxcc) { + + /* + * Send another END PDU + */ + sop->so_connctl++; + (void) sscop_send_end(sop, SSCOP_SOURCE_LAST); + + /* + * Restart retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + } else { + + /* + * Retransmit limit exceeded, force session termination + */ + STACK_CALL(SSCOP_RELEASE_CNF, sop->so_upper, + sop->so_toku, sop->so_connvc, 0, 0, err); + if (err) { + /* + * Error, force retry + */ + sop->so_timer[SSCOP_T_CC] = 1; + break; + } + + /* + * Report establishment failure + */ + sscop_maa_error(sop, 'O'); + + /* + * Return to IDLE state + */ + sop->so_state = SOS_IDLE; + } + break; + + case SOS_OUTRESYN: +rexmitrs: + /* + * No response to our RS yet + */ + if (sop->so_connctl < sop->so_parm.sp_maxcc) { + + /* + * Send another RS PDU + */ + sop->so_connctl++; + (void) sscop_send_rs(sop); + + /* + * Restart retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + } else { + + /* + * Retransmit limit exceeded, terminate session + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, + sop->so_toku, sop->so_connvc, + SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + /* + * Error, force retry + */ + sop->so_timer[SSCOP_T_CC] = 1; + break; + } + + /* + * Notify peer of termination + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Report establishment failure + */ + sscop_maa_error(sop, 'O'); + + if (sop->so_vers == SSCOP_VERS_QSAAL) + /* + * Clear connection data + */ + qsaal1_clear_connection(sop); + + /* + * Return to IDLE state + */ + sop->so_state = SOS_IDLE; + } + break; + + case SOS_CONRESYN: /* Q.SAAL1 */ +#if (SOS_OUTRECOV != SOS_CONRESYN) + case SOS_OUTRECOV: /* Q.2110 */ +#endif + if (sop->so_vers == SSCOP_VERS_QSAAL) { + /* + * Handle timeout for SOS_CONRESYN + */ + goto rexmitrs; + } + + /* + * Handle timeout for SOS_OUTRECOV + */ + + /* + * No response to our ER yet + */ + if (sop->so_connctl < sop->so_parm.sp_maxcc) { + + /* + * Send another ER PDU + */ + sop->so_connctl++; + (void) sscop_send_er(sop); + + /* + * Restart retransmit timer + */ + sop->so_timer[SSCOP_T_CC] = sop->so_parm.sp_timecc; + + } else { + + /* + * Retransmit limit exceeded, terminate session + */ + STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, + sop->so_toku, sop->so_connvc, + SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err); + if (err) { + /* + * Error, force retry + */ + sop->so_timer[SSCOP_T_CC] = 1; + break; + } + + /* + * Notify peer of termination + */ + (void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP); + + /* + * Report establishment failure + */ + sscop_maa_error(sop, 'O'); + + /* + * Clear receiver buffer + */ + sscop_rcvr_drain(sop); + + /* + * Return to IDLE state + */ + sop->so_state = SOS_IDLE; + } + break; + + default: + log(LOG_ERR, "sscop: invalid %s state: sop=0x%x, state=%d\n", + "Timer_CC", (int)sop, sop->so_state); + } +} + diff --git a/sys/netatm/uni/sscop_upper.c b/sys/netatm/uni/sscop_upper.c new file mode 100644 index 0000000..be9556d --- /dev/null +++ b/sys/netatm/uni/sscop_upper.c @@ -0,0 +1,412 @@ +/* + * + * =================================== + * 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: sscop_upper.c,v 1.6 1998/08/26 23:29:20 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP - CPCS SAP interface processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_upper.c,v 1.6 1998/08/26 23:29:20 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/sscop.h> +#include <netatm/uni/sscop_misc.h> +#include <netatm/uni/sscop_pdu.h> +#include <netatm/uni/sscop_var.h> + + +/* + * Local functions + */ +static caddr_t sscop_pdu_receive __P((KBuffer *, struct sscop *, int *)); + + +/* + * Local variables + */ +static union { + struct bgn_pdu t_bgn; + struct bgak_pdu t_bgak; + struct end_pdu t_end; + struct endak_q2110_pdu t_endak_q2110; + struct endak_qsaal_pdu t_endak_qsaal; + struct rs_pdu t_rs; + struct rsak_q2110_pdu t_rsak_q2110; + struct rsak_qsaal_pdu t_rsak_qsaal; + struct bgrej_pdu t_bgrej; + struct sd_pdu t_sd; + struct sdp_pdu t_sdp; + struct er_pdu t_er; + struct poll_pdu t_poll; + struct stat_pdu t_stat; + struct ustat_pdu t_ustat; + struct ud_pdu t_ud; + struct md_pdu t_md; + struct erak_pdu t_erak; +} sscop_trailer; + + +/* + * PDU length validation table + */ +struct pdulen { + int min; + int max; +}; + +static struct pdulen qsaal_pdulen[] = { + {0, 0}, + {sizeof(struct bgn_pdu), sizeof(struct bgn_pdu)}, + {sizeof(struct bgak_pdu), sizeof(struct bgak_pdu)}, + {sizeof(struct end_pdu), sizeof(struct end_pdu)}, + {sizeof(struct endak_qsaal_pdu),sizeof(struct endak_qsaal_pdu)}, + {sizeof(struct rs_pdu), sizeof(struct rs_pdu)}, + {sizeof(struct rsak_qsaal_pdu), sizeof(struct rsak_qsaal_pdu)}, + {sizeof(struct bgrej_pdu), sizeof(struct bgrej_pdu)}, + {sizeof(struct sd_pdu), sizeof(struct sd_pdu) + PDU_MAX_INFO}, + {sizeof(struct sdp_pdu), sizeof(struct sdp_pdu) + PDU_MAX_INFO}, + {sizeof(struct poll_pdu), sizeof(struct poll_pdu)}, + {sizeof(struct stat_pdu), sizeof(struct stat_pdu) + PDU_MAX_STAT}, + {sizeof(struct ustat_pdu), sizeof(struct ustat_pdu)}, + {sizeof(struct ud_pdu), sizeof(struct ud_pdu) + PDU_MAX_INFO}, + {sizeof(struct md_pdu), sizeof(struct md_pdu) + PDU_MAX_INFO}, + {0, 0} +}; + +static struct pdulen q2110_pdulen[] = { + {0, 0}, + {sizeof(struct bgn_pdu), sizeof(struct bgn_pdu) + PDU_MAX_UU}, + {sizeof(struct bgak_pdu), sizeof(struct bgak_pdu) + PDU_MAX_UU}, + {sizeof(struct end_pdu), sizeof(struct end_pdu) + PDU_MAX_UU}, + {sizeof(struct endak_q2110_pdu),sizeof(struct endak_q2110_pdu)}, + {sizeof(struct rs_pdu), sizeof(struct rs_pdu) + PDU_MAX_UU}, + {sizeof(struct rsak_q2110_pdu), sizeof(struct rsak_q2110_pdu)}, + {sizeof(struct bgrej_pdu), sizeof(struct bgrej_pdu) + PDU_MAX_UU}, + {sizeof(struct sd_pdu), sizeof(struct sd_pdu) + PDU_MAX_INFO}, + {sizeof(struct er_pdu), sizeof(struct er_pdu)}, + {sizeof(struct poll_pdu), sizeof(struct poll_pdu)}, + {sizeof(struct stat_pdu), sizeof(struct stat_pdu) + PDU_MAX_STAT}, + {sizeof(struct ustat_pdu), sizeof(struct ustat_pdu)}, + {sizeof(struct ud_pdu), sizeof(struct ud_pdu) + PDU_MAX_INFO}, + {sizeof(struct md_pdu), sizeof(struct md_pdu) + PDU_MAX_INFO}, + {sizeof(struct erak_pdu), sizeof(struct erak_pdu)} +}; + + +/* + * PDUs with Pad Length Fields + */ +static u_char qsaal_padlen[] = { + 0, /* --- */ + 0, /* BGN */ + 0, /* BGAK */ + 0, /* END */ + 0, /* ENDAK */ + 0, /* RS */ + 0, /* RSAK */ + 0, /* BGREJ */ + 1, /* SD */ + 1, /* SDP */ + 0, /* POLL */ + 0, /* STAT */ + 0, /* USTAT */ + 1, /* UD */ + 1, /* MD */ + 0 /* --- */ +}; + +static u_char q2110_padlen[] = { + 0, /* --- */ + 1, /* BGN */ + 1, /* BGAK */ + 1, /* END */ + 0, /* ENDAK */ + 1, /* RS */ + 0, /* RSAK */ + 1, /* BGREJ */ + 1, /* SD */ + 0, /* ER */ + 0, /* POLL */ + 0, /* STAT */ + 0, /* USTAT */ + 1, /* UD */ + 1, /* MD */ + 0 /* ERAK */ +}; + + +/* + * SSCOP Upper Stack Command Handler + * + * This function will receive all of the stack commands issued from the + * layer below SSCOP (ie. CPCS). Currently, only incoming PDUs will be + * received here. The appropriate processing function will be determined + * based on the received PDU type and the current sscop control block state. + * + * Arguments: + * cmd stack command code + * tok session token + * arg1 command specific argument + * arg2 command specific argument + * + * Returns: + * none + * + */ +void +sscop_upper(cmd, tok, arg1, arg2) + int cmd; + void *tok; + int arg1; + int arg2; +{ + struct sscop *sop = (struct sscop *)tok; + void (**ptab) __P((struct sscop *, KBuffer *, caddr_t)); + void (*func) __P((struct sscop *, KBuffer *, caddr_t)); + caddr_t trlr; + int type; + + ATM_DEBUG5("sscop_upper: cmd=0x%x, sop=0x%x, state=%d, arg1=0x%x, arg2=0x%x\n", + cmd, (int)sop, sop->so_state, arg1, arg2); + + switch (cmd) { + + case CPCS_UNITDATA_SIG: + /* + * Decode/validate received PDU + */ + trlr = sscop_pdu_receive((KBuffer *)arg1, sop, &type); + if (trlr == NULL) { + return; + } + + /* + * Validate sscop state + */ + if (sop->so_state > SOS_MAXSTATE) { + log(LOG_ERR, + "sscop_upper: invalid state sop=0x%x, state=%d\n", + (int)sop, sop->so_state); + KB_FREEALL((KBuffer *)arg1); + return; + } + + /* + * Call event processing function + */ + ptab = sop->so_vers == SSCOP_VERS_QSAAL ? + sscop_qsaal_pdutab[type]: + sscop_q2110_pdutab[type]; + func = ptab[sop->so_state]; + if (func == NULL) { + log(LOG_ERR, + "sscop_upper: unsupported pdu=%d, state=%d\n", + type, sop->so_state); + break; + } + (*func)(sop, (KBuffer *)arg1, trlr); + break; + + default: + log(LOG_ERR, "sscop_upper: unknown cmd 0x%x, sop=0x%x\n", + cmd, (int)sop); + } + + return; +} + + +/* + * Decode and Validate Received PDU + * + * This function will process all received SSCOP PDUs. The PDU type will be + * determined and PDU format validation will be performed. If the PDU is + * successfully decoded and validated, the buffer chain will have the PDU + * trailer removed, but any resultant zero-length buffers will NOT be freed. + * If the PDU fails validation, then the buffer chain will be freed. + * + * Arguments: + * m pointer to PDU buffer chain + * sop pointer to sscop connection block + * typep address to store PDU type + * + * Returns: + * addr pointer to (contiguous) PDU trailer + * 0 invalid PDU, buffer chain freed + * + */ +static caddr_t +sscop_pdu_receive(m, sop, typep) + KBuffer *m; + struct sscop *sop; + int *typep; +{ + KBuffer *m0, *ml, *mn; + caddr_t cp, tp; + int len, tlen, type, plen; + + /* + * Calculate PDU length and find the last two buffers in the chain + */ + len = 0; + for (m0 = m, ml = mn = NULL; m0; m0 = KB_NEXT(m0)) { + len += KB_LEN(m0); + mn = ml; + ml = m0; + } + + /* + * Make sure we've got a minimum sized PDU + */ + if (len < PDU_MIN_LEN) + goto badpdu; + + /* + * Get PDU type field + */ + if (KB_LEN(ml) >= PDU_MIN_LEN) { + KB_DATAEND(ml, tp, caddr_t); + tp -= PDU_MIN_LEN; + } else { + KB_DATAEND(mn, tp, caddr_t); + tp -= (PDU_MIN_LEN - KB_LEN(ml)); + } + *typep = type = *tp & PT_TYPE_MASK; + + /* + * Check up on PDU length + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) { + if ((len < (tlen = qsaal_pdulen[type].min)) || + (len > qsaal_pdulen[type].max) || + (len & PDU_LEN_MASK)) + goto badpdu; + } else { + if ((len < (tlen = q2110_pdulen[type].min)) || + (len > q2110_pdulen[type].max) || + (len & PDU_LEN_MASK)) + goto badpdu; + } + + /* + * Get a contiguous, aligned PDU trailer and adjust buffer + * controls to remove trailer + */ + if (KB_LEN(ml) >= tlen) { + /* + * Trailer is contained in last buffer + */ + KB_TAILADJ(ml, -tlen); + KB_DATAEND(ml, cp, caddr_t); + if ((int)cp & PDU_ADDR_MASK) { + /* + * Trailer not aligned in buffer, use local memory + */ + KM_COPY(cp, (caddr_t)&sscop_trailer, tlen); + cp = (caddr_t)&sscop_trailer; + } + } else { + /* + * Trailer is split across buffers, use local memory + */ + caddr_t cp1; + int off = tlen - KB_LEN(ml); + + cp = (caddr_t)&sscop_trailer; + + /* + * Ensure trailer is within last two buffers + */ + if ((mn == NULL) || (KB_LEN(mn) < off)) + goto badpdu; + + KB_DATASTART(ml, cp1, caddr_t); + KM_COPY(cp1, cp + off, KB_LEN(ml)); + KB_LEN(ml) = 0; + KB_TAILADJ(mn, -off); + KB_DATAEND(mn, cp1, caddr_t); + KM_COPY(cp1, cp, off); + } + + /* + * Get possible PDU Pad Length + */ + if (sop->so_vers == SSCOP_VERS_QSAAL) { + if (qsaal_padlen[type]) + plen = (*tp & PT_PAD_MASK) >> PT_PAD_SHIFT; + else + plen = 0; + } else { + if (q2110_padlen[type]) + plen = (*tp & PT_PAD_MASK) >> PT_PAD_SHIFT; + else + plen = 0; + } + + /* + * Perform Pad Length adjustments + */ + if (plen) { + if (KB_LEN(ml) >= plen) { + /* + * All pad bytes in last buffer + */ + KB_TAILADJ(ml, -plen); + } else { + /* + * Pad bytes split between buffers + */ + plen -= KB_LEN(ml); + if ((mn == NULL) || (KB_LEN(mn) < plen)) + goto badpdu; + KB_LEN(ml) = 0; + KB_TAILADJ(mn, -plen); + } + } + + return (cp); + +badpdu: + /* + * This MAA Error is only supposed to be for a PDU length violation, + * but we use it for any PDU format error. + */ + sscop_maa_error(sop, 'U'); + sscop_pdu_print(sop, m, "badpdu received"); + KB_FREEALL(m); + return (NULL); +} + diff --git a/sys/netatm/uni/sscop_var.h b/sys/netatm/uni/sscop_var.h new file mode 100644 index 0000000..9a739d3 --- /dev/null +++ b/sys/netatm/uni/sscop_var.h @@ -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: sscop_var.h,v 1.6 1998/04/07 23:21:57 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP protocol control blocks + * + */ + +#ifndef _UNI_SSCOP_VAR_H +#define _UNI_SSCOP_VAR_H + +/* + * Structure containing information for each SSCOP connection. + */ +struct sscop { + struct sscop *so_next; /* Next connection in chain */ + u_char so_state; /* Connection state (see below) */ + u_short so_flags; /* Connection flags (see below) */ + enum sscop_vers so_vers; /* SSCOP version */ + + /* Transmitter variables */ + sscop_seq so_send; /* VT(S) - next SD to send */ + sscop_seq so_sendmax; /* VT(MS) - max SD to send + 1 */ + sscop_seq so_ack; /* VT(A) - next expected ack */ + sscop_seq so_pollsend; /* VT(PS) - last POLL sent */ + sscop_seq so_pollack; /* VT(PA) - next expected STAT */ + short so_polldata; /* VT(PD) - SD's sent between POLLs */ + short so_connctl; /* VT(CC) - un-ack'd BGN,END,ER,RS */ + u_char so_sendconn; /* VT(SQ) - last BGN,ER,RS sent */ + + /* Receiver variables */ + sscop_seq so_rcvnext; /* VR(R) - next SD to receive */ + sscop_seq so_rcvhigh; /* VR(H) - next highest SD to receive */ + sscop_seq so_rcvmax; /* VR(MR) - max SD to receive + 1 */ + u_char so_rcvconn; /* VR(SQ) - last BGN,ER,RS received */ + + /* PDU queues */ + KBuffer *so_xmit_hd; /* SD transmission queue head */ + KBuffer *so_xmit_tl; /* SD transmission queue tail */ + struct pdu_hdr *so_rexmit_hd; /* SD retransmission queue head */ + struct pdu_hdr *so_rexmit_tl; /* SD retransmission queue head */ + struct pdu_hdr *so_pack_hd; /* SD pending ack queue head */ + struct pdu_hdr *so_pack_tl; /* SD pending ack queue tail */ + struct pdu_hdr *so_recv_hd; /* SD receive queue head */ + struct pdu_hdr *so_recv_tl; /* SD receive queue tail */ + + /* Connection parameters */ + struct sscop_parms so_parm; /* Connection parameters */ + + /* Timers */ + u_short so_timer[SSCOP_T_NUM]; /* Connection timers */ + + /* Stack variables */ + Atm_connvc *so_connvc; /* Connection vcc for this stack */ + void *so_toku; /* Stack upper layer's token */ + void *so_tokl; /* Stack lower layer's token */ + void (*so_upper) /* Stack upper layer's interface */ + __P((int, void *, int, int)); + void (*so_lower) /* Stack lower layer's interface */ + __P((int, void *, int, int)); + u_short so_headout; /* Output buffer headroom */ +}; + +/* + * Connection States + * + * Notes: + * # - state valid only for Q.SAAL1 + * ## - state valid only for Q.2110 + */ +#define SOS_INST 0 /* Instantiated, waiting for INIT */ +#define SOS_IDLE 1 /* Idle connection */ +#define SOS_OUTCONN 2 /* Outgoing connection pending */ +#define SOS_INCONN 3 /* Incoming connection pending */ +#define SOS_OUTDISC 4 /* Outgoing disconnection pending */ +#define SOS_OUTRESYN 5 /* Outgoing resynchronization pending */ +#define SOS_INRESYN 6 /* Incoming resynchronization pending */ +#define SOS_CONRESYN 7 /* Concurrent resynch pending (#) */ +#define SOS_OUTRECOV 7 /* Outgoing recovery pending (##) */ +#define SOS_RECOVRSP 8 /* Recovery response pending (##) */ +#define SOS_INRECOV 9 /* Incoming recovery pending (##) */ +#define SOS_READY 10 /* Data transfer ready */ +#define SOS_TERM 11 /* Waiting for TERM */ + +#define SOS_MAXSTATE 11 /* Maximum state value */ +#define SOS_NUMSTATES (SOS_MAXSTATE+1)/* Number of states */ + +/* + * Connection Flags + */ +#define SOF_NOCLRBUF 0x0001 /* Clear buffers = no */ +#define SOF_REESTAB 0x0002 /* SSCOP initiated reestablishment */ +#define SOF_XMITSRVC 0x0004 /* Transmit queues need servicing */ +#define SOF_KEEPALIVE 0x0008 /* Polling in transient phase */ +#define SOF_ENDSSCOP 0x0010 /* Last END PDU, SOURCE=SSCOP */ +#define SOF_NOCREDIT 0x0020 /* Transmit window closed */ + + +/* + * SSCOP statistics + */ +struct sscop_stat { + u_long sos_connects; /* Connection instances */ + u_long sos_aborts; /* Connection aborts */ + u_long sos_maa_error[MAA_ERROR_COUNT]; /* Management errors */ +}; + +#ifdef ATM_KERNEL +/* + * Global function declarations + */ + /* sscop.c */ +int sscop_start __P((void)); +int sscop_stop __P((void)); +void sscop_maa_error __P((struct sscop *, int)); +void sscop_abort __P((struct sscop *, char *)); + + /* sscop_lower.c */ +void sscop_lower __P((int, void *, int, int)); +void sscop_aa_noop_0 __P((struct sscop *, int, int)); +void sscop_aa_noop_1 __P((struct sscop *, int, int)); +void sscop_init_inst __P((struct sscop *, int, int)); +void sscop_term_all __P((struct sscop *, int, int)); + + /* sscop_pdu.c */ +int sscop_send_bgn __P((struct sscop *, int)); +int sscop_send_bgak __P((struct sscop *)); +int sscop_send_bgrej __P((struct sscop *)); +int sscop_send_end __P((struct sscop *, int)); +int sscop_send_endak __P((struct sscop *)); +int sscop_send_rs __P((struct sscop *)); +int sscop_send_rsak __P((struct sscop *)); +int sscop_send_er __P((struct sscop *)); +int sscop_send_erak __P((struct sscop *)); +int sscop_send_poll __P((struct sscop *)); +int sscop_send_stat __P((struct sscop *, sscop_seq)); +int sscop_send_ustat __P((struct sscop *, sscop_seq)); +int sscop_send_ud __P((struct sscop *, KBuffer *)); +void sscop_pdu_print __P((struct sscop *, KBuffer *, char *)); + + /* sscop_sigaa.c */ +void sscop_estreq_idle __P((struct sscop *, int, int)); +void sscop_estrsp_inconn __P((struct sscop *, int, int)); +void sscop_relreq_outconn __P((struct sscop *, int, int)); +void sscop_relreq_inconn __P((struct sscop *, int, int)); +void sscop_relreq_ready __P((struct sscop *, int, int)); +void sscop_datreq_ready __P((struct sscop *, int, int)); +void sscop_udtreq_all __P((struct sscop *, int, int)); + + /* sscop_sigcpcs.c */ +void sscop_noop __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgn_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgn_outdisc __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgn_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgn_inresyn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgak_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgak_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgak_outconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgrej_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgrej_outconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgrej_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgrej_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_bgrej_ready __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_end_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_end_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_end_outdisc __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_endak_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_endak_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_endak_outdisc __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_endak_ready __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_rs_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_rs_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_rsak_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_rsak_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_rsak_outresyn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_sd_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_sd_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_sd_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_poll_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_poll_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_poll_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_stat_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_stat_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_stat_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_stat_ready __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_ustat_error __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_ustat_idle __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_ustat_inconn __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_ustat_ready __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_ud_all __P((struct sscop *, KBuffer *, caddr_t)); +void sscop_md_all __P((struct sscop *, KBuffer *, caddr_t)); + + /* sscop_subr.c */ +KBuffer * sscop_stat_getelem __P((KBuffer *, sscop_seq *)); +struct pdu_hdr *sscop_pack_locate __P((struct sscop *, sscop_seq)); +void sscop_pack_free __P((struct sscop *, sscop_seq)); +void sscop_rexmit_insert __P((struct sscop *, struct pdu_hdr *)); +void sscop_rexmit_unlink __P((struct sscop *, struct pdu_hdr *)); +void sscop_xmit_drain __P((struct sscop *)); +int sscop_recv_insert __P((struct sscop *, struct pdu_hdr *)); +void sscop_rcvr_drain __P((struct sscop *)); +void sscop_service_xmit __P((struct sscop *)); +int sscop_is_rexmit __P((struct sscop *, u_char)); +void sscop_set_poll __P((struct sscop *)); + + /* sscop_timer.c */ +void sscop_timeout __P((struct atm_time *)); + + /* sscop_upper.c */ +void sscop_upper __P((int, void *, int, int)); + + /* q2110_sigaa.c */ + + /* q2110_sigcpcs.c */ + + /* q2110_subr.c */ +void q2110_clear_xmit __P((struct sscop *)); +void q2110_init_state __P((struct sscop *)); +void q2110_prep_retrieve __P((struct sscop *)); +void q2110_prep_recovery __P((struct sscop *)); +void q2110_deliver_data __P((struct sscop *)); +void q2110_error_recovery __P((struct sscop *)); + + /* qsaal1_sigaa.c */ + + /* qsaal1_sigcpcs.c */ + + /* qsaal1_subr.c */ +void qsaal1_reestablish __P((struct sscop *)); +void qsaal1_reset_xmit __P((struct sscop *)); +void qsaal1_reset_rcvr __P((struct sscop *)); +void qsaal1_clear_connection __P((struct sscop *)); + + +/* + * External variables + */ +extern struct sp_info sscop_pool; +extern int sscop_vccnt; +extern struct sscop *sscop_head; +extern struct sscop_stat sscop_stat; +extern struct atm_time sscop_timer; +extern void (*(*sscop_qsaal_aatab[])) + __P((struct sscop *, int, int)); +extern void (*(*sscop_q2110_aatab[])) + __P((struct sscop *, int, int)); +extern void (*(*sscop_qsaal_pdutab[])) + __P((struct sscop *, KBuffer *, caddr_t)); +extern void (*(*sscop_q2110_pdutab[])) + __P((struct sscop *, KBuffer *, caddr_t)); + +#endif /* ATM_KERNEL */ + +#endif /* _UNI_SSCOP_VAR_H */ diff --git a/sys/netatm/uni/uni.h b/sys/netatm/uni/uni.h new file mode 100644 index 0000000..700e859 --- /dev/null +++ b/sys/netatm/uni/uni.h @@ -0,0 +1,50 @@ +/* + * + * =================================== + * 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: uni.h,v 1.2 1997/05/06 22:20:39 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * Protocol definitions + * + */ + +#ifndef _UNI_UNI_H +#define _UNI_UNI_H + +/* + * UNI Version + */ +enum uni_vers { + UNI_VERS_3_0, + UNI_VERS_3_1, + UNI_VERS_4_0 +}; + +#endif /* _UNI_UNI_H */ diff --git a/sys/netatm/uni/uni_load.c b/sys/netatm/uni/uni_load.c new file mode 100644 index 0000000..23e7ff5 --- /dev/null +++ b/sys/netatm/uni/uni_load.c @@ -0,0 +1,450 @@ +/* + * + * =================================== + * 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: uni_load.c,v 1.6 1997/05/06 22:20:43 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * Loadable kernel module support + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uni_load.c,v 1.6 1997/05/06 22:20:43 mks Exp $"; +#endif + +#ifndef ATM_UNI_MODULE +#include "opt_atm.h" +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> + +/* + * External functions + */ +int sscop_start __P((void)); +int sscop_stop __P((void)); +int sscf_uni_start __P((void)); +int sscf_uni_stop __P((void)); +int uniip_start __P((void)); +int uniip_stop __P((void)); +int unisig_start __P((void)); +int unisig_stop __P((void)); + +/* + * Local functions + */ +static int uni_start __P((void)); +static int uni_stop __P((void)); + + +/* + * Initialize uni processing + * + * This will be called during module loading. We just notify all of our + * sub-services to initialize. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +static int +uni_start() +{ + int err; + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: uni=%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 uni sub-services + */ + err = sscop_start(); + if (err) + goto done; + + err = sscf_uni_start(); + if (err) + goto done; + + err = unisig_start(); + if (err) + goto done; + + err = uniip_start(); + if (err) + goto done; + +done: + return (err); +} + + +/* + * Halt uni processing + * + * This will be called just prior to unloading the module from + * memory. All sub-services will be notified of the termination. + * + * Arguments: + * none + * + * Returns: + * 0 shutdown was successful + * errno shutdown failed - reason indicated + * + */ +static int +uni_stop() +{ + int err, s = splnet(); + + /* + * Terminate uni sub-services + */ + err = uniip_stop(); + if (err) + goto done; + + err = unisig_stop(); + if (err) + goto done; + + err = sscf_uni_stop(); + if (err) + goto done; + + err = sscop_stop(); + if (err) + goto done; + +done: + (void) splx(s); + return (err); +} + + +#ifdef ATM_UNI_MODULE +/* + ******************************************************************* + * + * Loadable Module Support + * + ******************************************************************* + */ +static int uni_doload __P((void)); +static int uni_dounload __P((void)); + +/* + * 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 +uni_doload() +{ + int err = 0; + + /* + * Start us up + */ + err = uni_start(); + if (err) + /* Problems, clean up */ + (void)uni_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 +uni_dounload() +{ + int err = 0; + + /* + * OK, try to clean up our mess + */ + err = uni_stop(); + + return (err); +} + + +#ifdef sun +/* + * Loadable driver description + */ +struct vdldrv uni_drv = { + VDMAGIC_PSEUDO, /* Pseudo Driver */ + "uni_mod", /* name */ + NULL, /* 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 +uni_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 = uni_doload(); + if (err == 0) + /* Let vd driver know about us */ + vdp->vdd_vdtab = (struct vdlinkage *)&uni_drv; + break; + + case VDUNLOAD: + /* + * Module Unload + */ + err = uni_dounload(); + break; + + case VDSTAT: + /* + * Module Status + */ + + /* Not much to say at the moment */ + + break; + + default: + log(LOG_ERR, "uni_mod: Unknown vd command 0x%x\n", cmd); + err = EINVAL; + } + + return (err); +} +#endif /* sun */ + +#ifdef __FreeBSD__ + +#include <sys/exec.h> +#include <sys/sysent.h> +#include <sys/lkm.h> + +/* + * Loadable miscellaneous module description + */ +MOD_MISC(uni); + + +/* + * 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 +uni_load(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(uni_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 +uni_unload(lkmtp, cmd) + struct lkm_table *lkmtp; + int cmd; +{ + return(uni_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 +uni_mod(lkmtp, cmd, ver) + struct lkm_table *lkmtp; + int cmd; + int ver; +{ + MOD_DISPATCH(uni, lkmtp, cmd, ver, + uni_load, uni_unload, lkm_nullcmd); +} +#endif /* __FreeBSD__ */ + +#else /* !ATM_UNI_MODULE */ + +/* + ******************************************************************* + * + * Kernel Compiled Module Support + * + ******************************************************************* + */ +static void uni_doload __P((void *)); + +SYSINIT(atmuni, SI_SUB_PROTO_END, SI_ORDER_ANY, uni_doload, NULL) + +/* + * Kernel initialization + * + * Arguments: + * arg Not used + * + * Returns: + * none + * + */ +static void +uni_doload(void *arg) +{ + int err = 0; + + /* + * Start us up + */ + err = uni_start(); + if (err) { + /* Problems, clean up */ + (void)uni_stop(); + + log(LOG_ERR, "ATM UNI unable to initialize (%d)!!\n", err); + } + return; +} +#endif /* ATM_UNI_MODULE */ + diff --git a/sys/netatm/uni/uniarp.c b/sys/netatm/uni/uniarp.c new file mode 100644 index 0000000..f316a73 --- /dev/null +++ b/sys/netatm/uni/uniarp.c @@ -0,0 +1,1231 @@ +/* + * + * =================================== + * 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: uniarp.c,v 1.10 1998/07/20 18:58:45 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI ATMARP support (RFC1577) + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniarp.c,v 1.10 1998/07/20 18:58:45 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Global variables + */ +struct uniarp *uniarp_arptab[UNIARP_HASHSIZ] = {NULL}; +struct uniarp *uniarp_nomaptab = NULL; +struct uniarp *uniarp_pvctab = NULL; +struct atm_time uniarp_timer = {0, 0}; /* Aging timer */ +struct uniarp_stat uniarp_stat = {0}; +int uniarp_print = 0; + +Atm_endpoint uniarp_endpt = { + NULL, + ENDPT_ATMARP, + uniarp_ioctl, + uniarp_getname, + uniarp_connected, + uniarp_cleared, + NULL, + NULL, + NULL, + NULL, + uniarp_cpcs_data, + NULL, + NULL, + NULL, + NULL +}; + +struct sp_info uniarp_pool = { + "uni arp pool", /* si_name */ + sizeof(struct uniarp), /* si_blksiz */ + 10, /* si_blkcnt */ + 200 /* si_maxallow */ +}; + + +/* + * Local variables + */ +static void uniarp_server_mode __P((struct uniip *)); +static void uniarp_client_mode __P((struct uniip *, Atm_addr *)); + + +/* + * Process module loading notification + * + * Called whenever the uni module is initializing. + * + * Arguments: + * none + * + * Returns: + * 0 initialization successful + * errno initialization failed - reason indicated + * + */ +int +uniarp_start() +{ + int err; + + /* + * Register our endpoint + */ + err = atm_endpoint_register(&uniarp_endpt); + + return (err); +} + + +/* + * Process module unloading notification + * + * Called whenever the uni module is about to be unloaded. All signalling + * instances will have been previously detached. All uniarp resources + * must be freed now. + * + * Arguments: + * none + * + * Returns: + * none + * + */ +void +uniarp_stop() +{ + int i; + + /* + * Make sure the arp table is empty + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + if (uniarp_arptab[i] != NULL) + panic("uniarp_stop: arp table not empty"); + } + + /* + * Cancel timers + */ + (void) atm_untimeout(&uniarp_timer); + + /* + * De-register ourselves + */ + (void) atm_endpoint_deregister(&uniarp_endpt); + + /* + * Free our storage pools + */ + atm_release_pool(&uniarp_pool); +} + + +/* + * Process IP Network Interface Activation + * + * Called whenever an IP network interface becomes active. + * + * Called at splnet. + * + * Arguments: + * uip pointer to UNI IP interface + * + * Returns: + * none + * + */ +void +uniarp_ipact(uip) + struct uniip *uip; +{ + struct unisig *usp; + + ATM_DEBUG1("uniarp_ipact: uip=0x%x\n", (int)uip); + + /* + * Set initial state + */ + uip->uip_arpstate = UIAS_NOTCONF; + uip->uip_arpsvratm.address_format = T_ATM_ABSENT; + uip->uip_arpsvratm.address_length = 0; + uip->uip_arpsvrsub.address_format = T_ATM_ABSENT; + uip->uip_arpsvrsub.address_length = 0; + + usp = (struct unisig *)uip->uip_ipnif->inf_nif->nif_pif->pif_siginst; + if (usp->us_addr.address_format != T_ATM_ABSENT) + uip->uip_flags |= UIF_IFADDR; + + /* + * Make sure aging timer is running + */ + if ((uniarp_timer.ti_flag & TIF_QUEUED) == 0) + atm_timeout(&uniarp_timer, UNIARP_AGING, uniarp_aging); + + return; +} + + +/* + * Process IP Network Interface Deactivation + * + * Called whenever an IP network interface becomes inactive. All VCCs + * for this interface should already have been closed. + * + * Called at splnet. + * + * Arguments: + * uip pointer to UNI IP interface + * + * Returns: + * none + * + */ +void +uniarp_ipdact(uip) + struct uniip *uip; +{ + struct ip_nif *inp = uip->uip_ipnif; + struct uniarp *uap, *unext; + int i; + + ATM_DEBUG1("uniarp_ipdact: uip=0x%x\n", (int)uip); + + /* + * Delete all interface entries + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_intf != uip) + continue; + + /* + * All VCCs should (better) be gone by now + */ + if (uap->ua_ivp) + panic("uniarp_ipdact: entry not empty"); + + /* + * Clean up any loose ends + */ + UNIARP_CANCEL(uap); + + /* + * Delete entry from arp table and free entry + */ + UNIARP_DELETE(uap); + atm_free((caddr_t)uap); + } + } + + /* + * Clean up 'nomap' table + */ + for (uap = uniarp_nomaptab; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_intf != uip) + continue; + + /* + * All VCCs should (better) be gone by now + */ + if (uap->ua_ivp) + panic("uniarp_ipdact: entry not empty"); + + /* + * Clean up any loose ends + */ + UNIARP_CANCEL(uap); + + /* + * Delete entry from 'no map' table and free entry + */ + UNLINK(uap, struct uniarp, uniarp_nomaptab, ua_next); + atm_free((caddr_t)uap); + } + + /* + * Also clean up pvc table + */ + for (uap = uniarp_pvctab; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_intf != uip) + continue; + + /* + * All PVCs should (better) be gone by now + */ + panic("uniarp_ipdact: pvc table not empty"); + } + + /* + * Cancel arp interface timer + */ + UNIIP_ARP_CANCEL(uip); + + /* + * Stop aging timer if this is the last active interface + */ + if (uniip_head == uip && uip->uip_next == NULL) + (void) atm_untimeout(&uniarp_timer); +} + + +/* + * Process Interface ATM Address Change + * + * This function is called whenever the ATM address for a physical + * interface is set/changed. + * + * Called at splnet. + * + * Arguments: + * sip pointer to interface's UNI signalling instance + * + * Returns: + * none + * + */ +void +uniarp_ifaddr(sip) + struct siginst *sip; +{ + struct atm_nif *nip; + struct uniip *uip; + + ATM_DEBUG1("uniarp_ifaddr: sip=0x%x\n", (int)sip); + + /* + * We've got to handle this for every network interface + */ + for (nip = sip->si_pif->pif_nif; nip; nip = nip->nif_pnext) { + + /* + * Find our control blocks + */ + for (uip = uniip_head; uip; uip = uip->uip_next) { + if (uip->uip_ipnif->inf_nif == nip) + break; + } + if (uip == NULL) + continue; + + /* + * We don't support changing prefix (yet) + */ + if (uip->uip_flags & UIF_IFADDR) { + log(LOG_ERR, "uniarp_ifaddr: change not supported\n"); + continue; + } + + /* + * Note that address has been set and figure out what + * to do next + */ + uip->uip_flags |= UIF_IFADDR; + + if (uip->uip_arpstate == UIAS_CLIENT_PADDR) { + /* + * This is what we're waiting for + */ + uniarp_client_mode(uip, NULL); + } + } + + return; +} + + +/* + * Set ATMARP Server Mode + * + * This function is called to configure the local node to become the + * ATMARP server for the specified LIS. + * + * Called at splnet. + * + * Arguments: + * uip pointer to UNI IP interface + * + * Returns: + * none + * + */ +static void +uniarp_server_mode(uip) + struct uniip *uip; +{ + struct ip_nif *inp; + struct atm_nif *nip; + struct siginst *sgp; + struct ipvcc *ivp, *inext; + struct uniarp *uap, *unext; + int i; + + ATM_DEBUG1("uniarp_server_mode: uip=0x%x\n", (int)uip); + + /* + * Handle client/server mode changes first + */ + switch (uip->uip_arpstate) { + + case UIAS_NOTCONF: + case UIAS_SERVER_ACTIVE: + case UIAS_CLIENT_PADDR: + /* + * Nothing to undo + */ + break; + + case UIAS_CLIENT_POPEN: + /* + * We're becoming the server, so kill the pending connection + */ + UNIIP_ARP_CANCEL(uip); + if (ivp = uip->uip_arpsvrvcc) { + ivp->iv_flags &= ~IVF_NOIDLE; + uip->uip_arpsvrvcc = NULL; + (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_FAILED); + } + break; + + case UIAS_CLIENT_REGISTER: + case UIAS_CLIENT_ACTIVE: + /* + * We're becoming the server, but leave existing VCC as a + * "normal" IP VCC + */ + UNIIP_ARP_CANCEL(uip); + ivp = uip->uip_arpsvrvcc; + ivp->iv_flags &= ~IVF_NOIDLE; + uip->uip_arpsvrvcc = NULL; + break; + } + + /* + * Revalidate status for all arp entries on this interface + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_intf != uip) + continue; + + if (uap->ua_origin >= UAO_PERM) + continue; + + if (uap->ua_origin >= UAO_SCSP) { + if (uniarp_validate_ip(uip, &uap->ua_dstip, + uap->ua_origin) == 0) + continue; + } + + if (uap->ua_ivp == NULL) { + UNIARP_CANCEL(uap); + UNIARP_DELETE(uap); + atm_free((caddr_t)uap); + continue; + } + + if (uap->ua_flags & UAF_VALID) { + uap->ua_flags |= UAF_LOCKED; + for (ivp = uap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*ivp->iv_ipnif->inf_arpnotify) + (ivp, MAP_INVALID); + } + uap->ua_flags &= ~(UAF_LOCKED | UAF_VALID); + } + uap->ua_aging = 1; + uap->ua_origin = 0; + } + } + + /* + * OK, now let's make ourselves the server + */ + inp = uip->uip_ipnif; + nip = inp->inf_nif; + sgp = nip->nif_pif->pif_siginst; + ATM_ADDR_SEL_COPY(&sgp->si_addr, nip->nif_sel, &uip->uip_arpsvratm); + uip->uip_arpsvrip = IA_SIN(inp->inf_addr)->sin_addr; + uip->uip_arpstate = UIAS_SERVER_ACTIVE; + return; +} + + +/* + * Set ATMARP Client Mode + * + * This function is called to configure the local node to be an ATMARP + * client on the specified LIS using the specified ATMARP server. + * + * Called at splnet. + * + * Arguments: + * uip pointer to UNI IP interface + * aap pointer to the ATMARP server's ATM address + * + * Returns: + * none + * + */ +static void +uniarp_client_mode(uip, aap) + struct uniip *uip; + Atm_addr *aap; +{ + struct ip_nif *inp = uip->uip_ipnif; + struct uniarp *uap, *unext; + struct ipvcc *ivp, *inext; + int i; + + ATM_DEBUG2("uniarp_client_mode: uip=0x%x, atm=(%s,-)\n", + (int)uip, aap ? unisig_addr_print(aap): "-"); + + /* + * Handle client/server mode changes first + */ + switch (uip->uip_arpstate) { + + case UIAS_NOTCONF: + case UIAS_CLIENT_PADDR: + /* + * Nothing to undo + */ + break; + + case UIAS_CLIENT_POPEN: + /* + * If this is this a timeout retry, just go do it + */ + if (aap == NULL) + break; + + /* + * If this isn't really a different arpserver, we're done + */ + if (ATM_ADDR_EQUAL(aap, &uip->uip_arpsvratm)) + return; + + /* + * We're changing servers, so kill the pending connection + */ + UNIIP_ARP_CANCEL(uip); + if (ivp = uip->uip_arpsvrvcc) { + ivp->iv_flags &= ~IVF_NOIDLE; + uip->uip_arpsvrvcc = NULL; + (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_FAILED); + } + break; + + case UIAS_CLIENT_REGISTER: + case UIAS_CLIENT_ACTIVE: + /* + * If this isn't really a different arpserver, we're done + */ + if (ATM_ADDR_EQUAL(aap, &uip->uip_arpsvratm)) + return; + + /* + * We're changing servers, but leave existing VCC as a + * "normal" IP VCC + */ + UNIIP_ARP_CANCEL(uip); + ivp = uip->uip_arpsvrvcc; + ivp->iv_flags &= ~IVF_NOIDLE; + uip->uip_arpsvrvcc = NULL; + break; + + case UIAS_SERVER_ACTIVE: + /* + * We're changing from server mode, so... + * + * Reset valid/authoritative status for all arp entries + * on this interface + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_intf != uip) + continue; + + if (uap->ua_origin >= UAO_PERM) + continue; + + if (uap->ua_ivp == NULL) { + UNIARP_CANCEL(uap); + UNIARP_DELETE(uap); + atm_free((caddr_t)uap); + continue; + } + + if (uap->ua_flags & UAF_VALID) { + uap->ua_flags |= UAF_LOCKED; + for (ivp = uap->ua_ivp; ivp; + ivp = inext) { + inext = ivp->iv_arpnext; + (*ivp->iv_ipnif->inf_arpnotify) + (ivp, MAP_INVALID); + } + uap->ua_flags &= + ~(UAF_LOCKED | UAF_VALID); + } + uap->ua_aging = 1; + uap->ua_origin = 0; + } + } + uip->uip_arpsvratm.address_format = T_ATM_ABSENT; + uip->uip_arpsvratm.address_length = 0; + uip->uip_arpsvrsub.address_format = T_ATM_ABSENT; + uip->uip_arpsvrsub.address_length = 0; + uip->uip_arpsvrip.s_addr = 0; + break; + } + + /* + * Save the arp server address, if supplied now + */ + if (aap) + ATM_ADDR_COPY(aap, &uip->uip_arpsvratm); + + /* + * If the interface's ATM address isn't set yet, then we + * can't do much until it is + */ + if ((uip->uip_flags & UIF_IFADDR) == 0) { + uip->uip_arpstate = UIAS_CLIENT_PADDR; + return; + } + + /* + * Just to keep things simple, if we already have (or are trying to + * setup) any SVCs to our new server, kill the connections so we can + * open a "fresh" SVC for the arpserver connection. + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; uap = unext) { + unext = uap->ua_next; + + if (ATM_ADDR_EQUAL(&uip->uip_arpsvratm, + &uap->ua_dstatm) && + ATM_ADDR_EQUAL(&uip->uip_arpsvrsub, + &uap->ua_dstatmsub)) { + uap->ua_flags &= ~UAF_VALID; + for (ivp = uap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_FAILED); + } + } + } + } + for (uap = uniarp_nomaptab; uap; uap = unext) { + unext = uap->ua_next; + + if (ATM_ADDR_EQUAL(&uip->uip_arpsvratm, &uap->ua_dstatm) && + ATM_ADDR_EQUAL(&uip->uip_arpsvrsub, &uap->ua_dstatmsub)) { + uap->ua_flags &= ~UAF_VALID; + for (ivp = uap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_FAILED); + } + } + } + + /* + * Now, get an arp entry for the server connection + */ + uip->uip_arpstate = UIAS_CLIENT_POPEN; + uap = (struct uniarp *)atm_allocate(&uniarp_pool); + if (uap == NULL) { + UNIIP_ARP_TIMER(uip, 1 * ATM_HZ); + return; + } + + /* + * Next, initiate an SVC to the server + */ + if ((*inp->inf_createsvc)(&inp->inf_nif->nif_if, AF_ATM, + (caddr_t)&uip->uip_arpsvratm, &ivp)) { + atm_free((caddr_t)uap); + UNIIP_ARP_TIMER(uip, 1 * ATM_HZ); + return; + } + + /* + * Finally, get everything set up and wait for the SVC + * connection to complete + */ + uip->uip_arpsvrvcc = ivp; + ivp->iv_flags |= IVF_NOIDLE; + + ATM_ADDR_COPY(&uip->uip_arpsvratm, &uap->ua_dstatm); + ATM_ADDR_COPY(&uip->uip_arpsvrsub, &uap->ua_dstatmsub); + uap->ua_intf = uip; + + LINK2TAIL(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)uap; + + LINK2TAIL(uap, struct uniarp, uniarp_nomaptab, ua_next); + + return; +} + + +/* + * Process a UNI ARP interface timeout + * + * Called when a previously scheduled uniip arp interface timer expires. + * Processing will be based on the current uniip arp state. + * + * Called at splnet. + * + * Arguments: + * tip pointer to uniip arp timer control block + * + * Returns: + * none + * + */ +void +uniarp_iftimeout(tip) + struct atm_time *tip; +{ + struct ip_nif *inp; + struct uniip *uip; + + + /* + * Back-off to uniip control block + */ + uip = (struct uniip *) + ((caddr_t)tip - (int)(&((struct uniip *)0)->uip_arptime)); + + ATM_DEBUG2("uniarp_iftimeout: uip=0x%x, state=%d\n", (int)uip, + uip->uip_arpstate); + + /* + * Process timeout based on protocol state + */ + switch (uip->uip_arpstate) { + + case UIAS_CLIENT_POPEN: + /* + * Retry opening arp server connection + */ + uniarp_client_mode(uip, NULL); + break; + + case UIAS_CLIENT_REGISTER: + /* + * Resend registration request + */ + inp = uip->uip_ipnif; + (void) uniarp_arp_req(uip, &(IA_SIN(inp->inf_addr)->sin_addr)); + + /* + * Restart timer + */ + UNIIP_ARP_TIMER(uip, 2 * ATM_HZ); + + break; + + case UIAS_CLIENT_ACTIVE: + /* + * Refresh our registration + */ + inp = uip->uip_ipnif; + (void) uniarp_arp_req(uip, &(IA_SIN(inp->inf_addr)->sin_addr)); + + /* + * Restart timer + */ + UNIIP_ARP_TIMER(uip, UNIARP_REGIS_RETRY); + + break; + + default: + log(LOG_ERR, "uniarp_iftimeout: invalid state %d\n", + uip->uip_arpstate); + } +} + + +/* + * UNI ARP IOCTL support + * + * Function will be called at splnet. + * + * Arguments: + * code PF_ATM sub-operation code + * data pointer to code specific parameter data area + * arg1 pointer to code specific argument + * + * Returns: + * 0 request procesed + * errno error processing request - reason indicated + * + */ +int +uniarp_ioctl(code, data, arg1) + int code; + caddr_t data; + caddr_t arg1; +{ + struct atmaddreq *aap; + struct atmdelreq *adp; + struct atmsetreq *asp; + struct atminfreq *aip; + struct air_arp_rsp aar; + struct air_asrv_rsp asr; + struct atm_pif *pip; + struct atm_nif *nip; + struct ipvcc *ivp, *inext; + struct uniip *uip; + struct uniarp *uap; + struct unisig *usp; + struct in_addr ip; + Atm_addr atmsub; + u_long dst; + int err = 0, i, buf_len; + caddr_t buf_addr; + + switch (code) { + + case AIOCS_ADD_ARP: + /* + * Add a permanent ARP mapping + */ + aap = (struct atmaddreq *)data; + uip = (struct uniip *)arg1; + if (aap->aar_arp_addr.address_format != T_ATM_ENDSYS_ADDR) { + err = EINVAL; + break; + } + atmsub.address_format = T_ATM_ABSENT; + atmsub.address_length = 0; + ip = SATOSIN(&aap->aar_arp_dst)->sin_addr; + + /* + * Validate IP address + */ + if (uniarp_validate_ip(uip, &ip, aap->aar_arp_origin) != 0) { + err = EADDRNOTAVAIL; + break; + } + + /* + * Add an entry to the cache + */ + err = uniarp_cache_svc(uip, &ip, &aap->aar_arp_addr, + &atmsub, aap->aar_arp_origin); + break; + + case AIOCS_DEL_ARP: + /* + * Delete an ARP mapping + */ + adp = (struct atmdelreq *)data; + uip = (struct uniip *)arg1; + ip = SATOSIN(&adp->adr_arp_dst)->sin_addr; + + /* + * Now find the entry to be deleted + */ + UNIARP_LOOKUP(ip.s_addr, uap); + if (uap == NULL) { + err = ENOENT; + break; + } + + /* + * Notify all VCCs using this entry that they must finish + * up now. + */ + uap->ua_flags |= UAF_LOCKED; + for (ivp = uap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_FAILED); + } + + /* + * Now free up the entry + */ + UNIARP_CANCEL(uap); + UNIARP_DELETE(uap); + atm_free((caddr_t)uap); + break; + + case AIOCS_SET_ASV: + /* + * Set interface ARP server address + */ + asp = (struct atmsetreq *)data; + for (uip = uniip_head; uip; uip = uip->uip_next) { + if (uip->uip_ipnif->inf_nif == (struct atm_nif *)arg1) + break; + } + if (uip == NULL) { + err = ENOPROTOOPT; + break; + } + + /* + * Check for our own address + */ + usp = (struct unisig *) + uip->uip_ipnif->inf_nif->nif_pif->pif_siginst; + if (ATM_ADDR_EQUAL(&asp->asr_arp_addr, &usp->us_addr)) { + asp->asr_arp_addr.address_format = T_ATM_ABSENT; + } + + /* + * If we're going into server mode, make sure we can get + * the memory for the prefix list before continuing + */ + if (asp->asr_arp_addr.address_format == T_ATM_ABSENT) { + i = asp->asr_arp_plen / sizeof(struct uniarp_prf); + if (i <= 0) { + err = EINVAL; + break; + } + buf_len = i * sizeof(struct uniarp_prf); + buf_addr = KM_ALLOC(buf_len, M_DEVBUF, M_NOWAIT); + if (buf_addr == NULL) { + err = ENOMEM; + break; + } + err = copyin(asp->asr_arp_pbuf, buf_addr, buf_len); + if (err) { + KM_FREE(buf_addr, buf_len, M_DEVBUF); + break; + } + } else { + /* Silence the compiler */ + i = 0; + buf_addr = NULL; + } + + /* + * Free any existing prefix address list + */ + if (uip->uip_prefix != NULL) { + KM_FREE(uip->uip_prefix, + uip->uip_nprefix * sizeof(struct uniarp_prf), + M_DEVBUF); + uip->uip_prefix = NULL; + uip->uip_nprefix = 0; + } + + if (asp->asr_arp_addr.address_format == T_ATM_ABSENT) { + /* + * Set ATMARP server mode + */ + uip->uip_prefix = (struct uniarp_prf *)buf_addr; + uip->uip_nprefix = i; + uniarp_server_mode(uip); + } else + /* + * Set ATMARP client mode + */ + uniarp_client_mode(uip, &asp->asr_arp_addr); + break; + + case AIOCS_INF_ARP: + /* + * Get ARP table information + */ + aip = (struct atminfreq *)data; + + if (aip->air_arp_addr.sa_family != AF_INET) + break; + dst = SATOSIN(&aip->air_arp_addr)->sin_addr.s_addr; + + buf_addr = aip->air_buf_addr; + buf_len = aip->air_buf_len; + + pip = ((struct siginst *)arg1)->si_pif; + + /* + * Run through entire arp table + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; uap = uap->ua_next) { + /* + * We only want valid entries learned + * from the supplied interface. + */ + nip = uap->ua_intf->uip_ipnif->inf_nif; + if (nip->nif_pif != pip) + continue; + if ((dst != INADDR_ANY) && + (dst != uap->ua_dstip.s_addr)) + continue; + + /* + * Make sure there's room in the user's buffer + */ + if (buf_len < sizeof(aar)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + SATOSIN(&aar.aap_arp_addr)->sin_family = + AF_INET; + SATOSIN(&aar.aap_arp_addr)->sin_addr.s_addr = + uap->ua_dstip.s_addr; + (void) sprintf(aar.aap_intf, "%s%d", + nip->nif_if.if_name, + nip->nif_if.if_unit); + aar.aap_flags = uap->ua_flags; + aar.aap_origin = uap->ua_origin; + if (uap->ua_flags & UAF_VALID) + aar.aap_age = uap->ua_aging + + uap->ua_retry * UNIARP_RETRY_AGE; + else + aar.aap_age = 0; + ATM_ADDR_COPY(&uap->ua_dstatm, &aar.aap_addr); + ATM_ADDR_COPY(&uap->ua_dstatmsub, + &aar.aap_subaddr); + + /* + * Copy the response into the user's buffer + */ + if (err = copyout((caddr_t)&aar, buf_addr, + sizeof(aar))) + break; + buf_addr += sizeof(aar); + buf_len -= sizeof(aar); + } + if (err) + break; + } + + /* + * Now go through the 'nomap' table + */ + if (err || (dst != INADDR_ANY)) + goto updbuf; + for (uap = uniarp_nomaptab; uap; uap = uap->ua_next) { + /* + * We only want valid entries learned + * from the supplied interface. + */ + nip = uap->ua_intf->uip_ipnif->inf_nif; + if (nip->nif_pif != pip) + continue; + + /* + * Make sure there's room in the user's buffer + */ + if (buf_len < sizeof(aar)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + SATOSIN(&aar.aap_arp_addr)->sin_family = AF_INET; + SATOSIN(&aar.aap_arp_addr)->sin_addr.s_addr = 0; + (void) sprintf(aar.aap_intf, "%s%d", + nip->nif_if.if_name, nip->nif_if.if_unit); + aar.aap_flags = 0; + aar.aap_origin = uap->ua_origin; + aar.aap_age = 0; + ATM_ADDR_COPY(&uap->ua_dstatm, &aar.aap_addr); + ATM_ADDR_COPY(&uap->ua_dstatmsub, + &aar.aap_subaddr); + + /* + * Copy the response into the user's buffer + */ + if (err = copyout((caddr_t)&aar, buf_addr, + sizeof(aar))) + break; + buf_addr += sizeof(aar); + buf_len -= sizeof(aar); + } + +updbuf: + /* + * Update the buffer pointer and length + */ + aip->air_buf_addr = buf_addr; + aip->air_buf_len = buf_len; + + /* + * If the user wants the refresh status reset and no + * errors have been encountered, then do the reset + */ + if ((err == 0) && (aip->air_arp_flags & ARP_RESET_REF)) { + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; + uap = uap->ua_next) { + /* + * We only want valid entries learned + * from the supplied interface. + */ + nip = uap->ua_intf->uip_ipnif->inf_nif; + if (nip->nif_pif != pip) + continue; + if ((dst != INADDR_ANY) && + (dst != uap->ua_dstip.s_addr)) + continue; + + /* + * Reset refresh flag + */ + uap->ua_flags &= ~UAF_REFRESH; + } + } + } + break; + + case AIOCS_INF_ASV: + /* + * Get ARP server information + */ + aip = (struct atminfreq *)data; + + buf_addr = aip->air_buf_addr; + buf_len = aip->air_buf_len; + + for (uip = uniip_head; uip; uip = uip->uip_next) { + + if ((arg1 != NULL) && + (uip->uip_ipnif->inf_nif != (struct atm_nif *)arg1)) + continue; + + /* + * Make sure there's room in the user's buffer + */ + if (buf_len < sizeof(asr)) { + err = ENOSPC; + break; + } + + /* + * Fill in info to be returned + */ + nip = uip->uip_ipnif->inf_nif; + (void) sprintf(asr.asp_intf, "%s%d", + nip->nif_if.if_name, nip->nif_if.if_unit); + asr.asp_state = uip->uip_arpstate; + if (uip->uip_arpstate == UIAS_SERVER_ACTIVE) { + asr.asp_addr.address_format = T_ATM_ABSENT; + asr.asp_addr.address_length = 0; + } else { + ATM_ADDR_COPY(&uip->uip_arpsvratm, + &asr.asp_addr); + } + asr.asp_subaddr.address_format = T_ATM_ABSENT; + asr.asp_subaddr.address_length = 0; + asr.asp_nprefix = uip->uip_nprefix; + + /* + * Copy the response into the user's buffer + */ + if (err = copyout((caddr_t)&asr, buf_addr, sizeof(asr))) + break; + buf_addr += sizeof(asr); + buf_len -= sizeof(asr); + + /* + * Copy the prefix list into the user's buffer + */ + if (uip->uip_nprefix) { + i = uip->uip_nprefix + * sizeof(struct uniarp_prf); + if (buf_len < i) { + err = ENOSPC; + break; + } + if (err = copyout(uip->uip_prefix, buf_addr, i)) + break; + buf_addr += i; + buf_len -= i; + } + } + + /* + * Update the buffer pointer and length + */ + aip->air_buf_addr = buf_addr; + aip->air_buf_len = buf_len; + break; + + default: + err = EOPNOTSUPP; + } + + return (err); +} + + +/* + * Get Connection's Application/Owner Name + * + * Arguments: + * tok uniarp connection token (pointer to ipvcc) + * + * Returns: + * addr pointer to string containing our name + * + */ +caddr_t +uniarp_getname(tok) + void *tok; +{ + return ("ATMARP"); +} + diff --git a/sys/netatm/uni/uniarp_cache.c b/sys/netatm/uni/uniarp_cache.c new file mode 100644 index 0000000..6c9537e --- /dev/null +++ b/sys/netatm/uni/uniarp_cache.c @@ -0,0 +1,420 @@ +/* + * + * =================================== + * 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: uniarp_cache.c,v 1.8 1998/08/26 23:29:20 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI ATMARP support (RFC1577) - ARP cache processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniarp_cache.c,v 1.8 1998/08/26 23:29:20 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Add data to the arp table cache + * + * Called at splnet. + * + * Arguments: + * uip pointer to UNI IP interface + * ip pointer to IP address structure + * atm pointer to ATM address structure + * atmsub pointer to ATM subaddress structure + * origin source of arp information + * + * Returns: + * 0 cache successfully updated + * else updated failed - reason indicated + * + */ +int +uniarp_cache_svc(uip, ip, atm, atmsub, origin) + struct uniip *uip; + struct in_addr *ip; + Atm_addr *atm; + Atm_addr *atmsub; + u_int origin; +{ + struct ip_nif *inp; + struct ipvcc *ivp, *inext, *itail; + struct uniarp *nouap, *ipuap; + char abuf[64]; + +#ifdef DIAGNOSTIC + strncpy(abuf, unisig_addr_print(atmsub), sizeof(abuf)); + ATM_DEBUG4("cache_svc: ip=%s, atm=(%s,%s), origin=%d\n", + inet_ntoa(*ip), unisig_addr_print(atm), abuf, origin); +#endif + + /* + * Get interface info + */ + inp = uip->uip_ipnif; + + /* + * Find both cached entry and 'nomap' entries for this data. + */ + UNIARP_LOOKUP(ip->s_addr, ipuap); + for (nouap = uniarp_nomaptab; nouap; nouap = nouap->ua_next) { + if (ATM_ADDR_EQUAL(atm, &nouap->ua_dstatm) && + ATM_ADDR_EQUAL(atmsub, &nouap->ua_dstatmsub) && + (nouap->ua_intf == uip)) + break; + } + + /* + * If there aren't any entries yet, create one + */ + if ((ipuap == NULL) && (nouap == NULL)) { + ipuap = (struct uniarp *)atm_allocate(&uniarp_pool); + if (ipuap == NULL) + return (ENOMEM); + ipuap->ua_dstip.s_addr = ip->s_addr; + ipuap->ua_dstatm.address_format = T_ATM_ABSENT; + ipuap->ua_dstatmsub.address_format = T_ATM_ABSENT; + ipuap->ua_intf = uip; + UNIARP_ADD(ipuap); + } + + /* + * If there's no cached mapping, then make the 'nomap' entry + * the new cached entry. + */ + if (ipuap == NULL) { + UNLINK(nouap, struct uniarp, uniarp_nomaptab, ua_next); + nouap->ua_dstip.s_addr = ip->s_addr; + ipuap = nouap; + nouap = NULL; + UNIARP_ADD(ipuap); + } + + /* + * We need to check the consistency of the new data with any + * cached data. So taking the easy case first, if there isn't + * an ATM address in the cache then we can skip all these checks. + */ + if (ipuap->ua_dstatm.address_format != T_ATM_ABSENT) { + /* + * See if the new data conflicts with what's in the cache + */ + if (ATM_ADDR_EQUAL(atm, &ipuap->ua_dstatm) && + ATM_ADDR_EQUAL(atmsub, &ipuap->ua_dstatmsub) && + (uip == ipuap->ua_intf)) { + /* + * No conflicts here + */ + goto dataok; + } + + /* + * Data conflict...how we deal with this depends on + * the origins of the conflicting data + */ + if (origin == ipuap->ua_origin) { + /* + * The new data has equal precedence - if there are + * any VCCs using this entry, then we reject this + * "duplicate IP address" update. + */ + if (ipuap->ua_ivp != NULL) { + strncpy(abuf, unisig_addr_print(atmsub), + sizeof(abuf)); + log(LOG_WARNING, + "uniarp: duplicate IP address %s from %s,%s\n", + inet_ntoa(*ip), unisig_addr_print(atm), + abuf); + return (EACCES); + } + + } else if (origin > ipuap->ua_origin) { + /* + * New data's origin has higher precedence, + * so accept the new mapping and notify IP/ATM + * that a mapping change has occurred. IP/ATM will + * close any VCC's which aren't waiting for this map. + */ + ipuap->ua_flags |= UAF_LOCKED; + for (ivp = ipuap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_CHANGED); + } + ipuap->ua_flags &= ~UAF_LOCKED; + } else { + /* + * New data is of lesser origin precedence, + * so we just reject the update attempt. + */ + return (EACCES); + } + + strncpy(abuf, unisig_addr_print(atmsub), sizeof(abuf)); + log(LOG_WARNING, + "uniarp: ATM address for %s changed to %s,%s\n", + inet_ntoa(*ip), unisig_addr_print(atm), abuf); + } + + /* + * Update the cache entry with the new data + */ + ATM_ADDR_COPY(atm, &ipuap->ua_dstatm); + ATM_ADDR_COPY(atmsub, &ipuap->ua_dstatmsub); + ipuap->ua_intf = uip; + +dataok: + /* + * Update cache data origin + */ + ipuap->ua_origin = MAX(ipuap->ua_origin, origin); + + /* + * Ok, now act on this new/updated cache data + */ + ipuap->ua_flags |= UAF_LOCKED; + + /* + * Save pointer to last VCC currently on cached entry chain that + * will need to be notified of the map becoming valid + */ + itail = NULL; + if ((ipuap->ua_flags & UAF_VALID) == 0) { + + for (itail = ipuap->ua_ivp; itail && itail->iv_arpnext; + itail = itail->iv_arpnext) { + } + } + + /* + * If there was a 'nomap' entry for this mapping, then we need to + * announce the new mapping to them first. + */ + if (nouap) { + + /* + * Move the VCCs from this entry to the cache entry and + * let them know there's a valid mapping now + */ + for (ivp = nouap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + + UNLINK(ivp, struct ipvcc, nouap->ua_ivp, iv_arpnext); + + LINK2TAIL(ivp, struct ipvcc, ipuap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)ipuap; + + (*inp->inf_arpnotify)(ivp, MAP_VALID); + } + + /* + * Unlink and free the 'nomap' entry + */ + UNLINK(nouap, struct uniarp, uniarp_nomaptab, ua_next); + UNIARP_CANCEL(nouap); + atm_free((caddr_t)nouap); + } + + /* + * Now, if this entry wasn't valid, notify the remaining VCCs + */ + if (itail) { + + for (ivp = ipuap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_VALID); + if (ivp == itail) + break; + } + } + ipuap->ua_flags &= ~UAF_LOCKED; + + /* + * We now have a valid cache entry, so cancel any retry timer + * and reset the aging timeout + */ + UNIARP_CANCEL(ipuap); + if ((ipuap->ua_origin == UAO_REGISTER) && (origin != UAO_REGISTER)) { + if (((ipuap->ua_flags & UAF_VALID) == 0) || + (ipuap->ua_aging <= + UNIARP_SERVER_AGE - UNIARP_MIN_REFRESH)) { + ipuap->ua_flags |= UAF_REFRESH; + ipuap->ua_aging = UNIARP_SERVER_AGE; + ipuap->ua_retry = UNIARP_SERVER_RETRY; + } + } else { + if (uip->uip_arpstate == UIAS_SERVER_ACTIVE) { + ipuap->ua_aging = UNIARP_SERVER_AGE; + ipuap->ua_retry = UNIARP_SERVER_RETRY; + } else { + ipuap->ua_aging = UNIARP_CLIENT_AGE; + ipuap->ua_retry = UNIARP_CLIENT_RETRY; + } + ipuap->ua_flags |= UAF_REFRESH; + } + ipuap->ua_flags |= UAF_VALID; + ipuap->ua_flags &= ~UAF_USED; + return (0); +} + + +/* + * Process ARP data from a PVC + * + * The arp table cache is never updated with PVC information. + * + * Called at splnet. + * + * Arguments: + * ivp pointer to input PVC's IPVCC control block + * ip pointer to IP address structure + * atm pointer to ATM address structure + * atmsub pointer to ATM subaddress structure + * + * Returns: + * none + * + */ +void +uniarp_cache_pvc(ivp, ip, atm, atmsub) + struct ipvcc *ivp; + struct in_addr *ip; + Atm_addr *atm; + Atm_addr *atmsub; +{ + struct ip_nif *inp; + struct uniarp *uap; + +#ifdef DIAGNOSTIC + char buf[64]; + int vpi = 0, vci = 0; + + if ((ivp->iv_conn) && (ivp->iv_conn->co_connvc)) { + vpi = ivp->iv_conn->co_connvc->cvc_vcc->vc_vpi; + vci = ivp->iv_conn->co_connvc->cvc_vcc->vc_vci; + } + strncpy(buf, unisig_addr_print(atmsub), sizeof(buf)); + ATM_DEBUG5("cache_pvc: vcc=(%d,%d), ip=%s, atm=(%s,%s)\n", + vpi, vci, inet_ntoa(*ip), unisig_addr_print(atm), buf); +#endif + + /* + * Get PVC info + */ + inp = ivp->iv_ipnif; + uap = (struct uniarp *)ivp->iv_arpent; + + /* + * See if IP address for PVC has changed + */ + if (uap->ua_dstip.s_addr != ip->s_addr) { + if (uap->ua_dstip.s_addr != 0) + (*inp->inf_arpnotify)(ivp, MAP_CHANGED); + uap->ua_dstip.s_addr = ip->s_addr; + } + + /* + * Let IP/ATM know if address has become valid + */ + if ((uap->ua_flags & UAF_VALID) == 0) + (*inp->inf_arpnotify)(ivp, MAP_VALID); + uap->ua_flags |= UAF_VALID; + uap->ua_aging = UNIARP_CLIENT_AGE; + uap->ua_retry = UNIARP_CLIENT_RETRY; + + /* + * Save ATM addresses just for debugging + */ + ATM_ADDR_COPY(atm, &uap->ua_dstatm); + ATM_ADDR_COPY(atmsub, &uap->ua_dstatmsub); + + return; +} + + +/* + * Validate IP address + * + * Arguments: + * uip pointer to UNI IP interface + * ip pointer to IP address structure + * origin source of arp information + * + * Returns: + * 0 IP address is acceptable + * else invalid IP address + * + */ +int +uniarp_validate_ip(uip, ip, origin) + struct uniip *uip; + struct in_addr *ip; + u_int origin; +{ + struct uniarp_prf *upp; + int i; + + + /* + * Can't be multicast or broadcast address + */ + if (IN_MULTICAST(ntohl(ip->s_addr)) || +#if (defined(BSD) && (BSD >= 199306)) + in_broadcast(*ip, &uip->uip_ipnif->inf_nif->nif_if)) +#else + in_broadcast(*ip)) +#endif + return (1); + + /* + * For ATMARP registration information (including SCSP data), + * the address must be allowed by the interface's prefix list. + */ + if ((origin == UAO_REGISTER) || (origin == UAO_SCSP)) { + for (i = uip->uip_nprefix, upp = uip->uip_prefix; + i; i--, upp++) { + if ((ip->s_addr & upp->upf_mask.s_addr) == + upp->upf_addr.s_addr) + break; + } + if (i == 0) + return (1); + } + + return (0); +} + diff --git a/sys/netatm/uni/uniarp_input.c b/sys/netatm/uni/uniarp_input.c new file mode 100644 index 0000000..cceeaa9 --- /dev/null +++ b/sys/netatm/uni/uniarp_input.c @@ -0,0 +1,853 @@ +/* + * + * =================================== + * 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: uniarp_input.c,v 1.10 1998/07/13 00:04:32 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI ATMARP support (RFC1577) - Input packet processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniarp_input.c,v 1.10 1998/07/13 00:04:32 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Local functions + */ +static void proc_arp_req __P((struct ipvcc *, KBuffer *)); +static void proc_arp_rsp __P((struct ipvcc *, KBuffer *)); +static void proc_arp_nak __P((struct ipvcc *, KBuffer *)); +static void proc_inarp_req __P((struct ipvcc *, KBuffer *)); +static void proc_inarp_rsp __P((struct ipvcc *, KBuffer *)); + + +/* + * Local variables + */ +static Atm_addr satm; +static Atm_addr satmsub; +static Atm_addr tatm; +static Atm_addr tatmsub; +static struct in_addr sip; +static struct in_addr tip; + + +/* + * Process ATMARP Input Data + * + * Arguments: + * tok uniarp connection token (pointer to ipvcc) + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +void +uniarp_cpcs_data(tok, m) + void *tok; + KBuffer *m; +{ + struct ipvcc *ivp = tok; + struct atmarp_hdr *ahp; + KBuffer *n; + int len, plen = sizeof(struct atmarp_hdr); + +#ifdef DIAGNOSTIC + if (uniarp_print) + uniarp_pdu_print(ivp, m, "receive"); +#endif + + /* + * Verify IP's VCC state + */ + if (ivp->iv_state != IPVCC_ACTIVE) { + goto bad; + } + + /* + * Get the fixed fields together + */ + if (KB_LEN(m) < sizeof(struct atmarp_hdr)) { + KB_PULLUP(m, sizeof(struct atmarp_hdr), m); + if (m == NULL) + goto bad; + } + + KB_DATASTART(m, ahp, struct atmarp_hdr *); + + /* + * Initial packet verification + */ + if ((ahp->ah_hrd != htons(ARP_ATMFORUM)) || + (ahp->ah_pro != htons(ETHERTYPE_IP))) + goto bad; + + /* + * Verify/gather source address fields + */ + if (len = (ahp->ah_shtl & ARP_TL_LMASK)) { + if (ahp->ah_shtl & ARP_TL_E164) { + if (len > sizeof(struct atm_addr_e164)) + goto bad; + satm.address_format = T_ATM_E164_ADDR; + } else { + if (len != sizeof(struct atm_addr_nsap)) + goto bad; + satm.address_format = T_ATM_ENDSYS_ADDR; + } + satm.address_length = len; + if (KB_COPYDATA(m, plen, len, (caddr_t)satm.address)) + goto bad; + plen += len; + } else { + satm.address_format = T_ATM_ABSENT; + satm.address_length = 0; + } + + if (len = (ahp->ah_sstl & ARP_TL_LMASK)) { + if (((ahp->ah_sstl & ARP_TL_TMASK) != ARP_TL_NSAPA) || + (len != sizeof(struct atm_addr_nsap))) + goto bad; + satmsub.address_format = T_ATM_ENDSYS_ADDR; + satmsub.address_length = len; + if (KB_COPYDATA(m, plen, len, (caddr_t)satmsub.address)) + goto bad; + plen += len; + } else { + satmsub.address_format = T_ATM_ABSENT; + satmsub.address_length = 0; + } + + if (len = ahp->ah_spln) { + if (len != sizeof(struct in_addr)) + goto bad; + if (KB_COPYDATA(m, plen, len, (caddr_t)&sip)) + goto bad; + plen += len; + } else { + sip.s_addr = 0; + } + + /* + * Verify/gather target address fields + */ + if (len = (ahp->ah_thtl & ARP_TL_LMASK)) { + if (ahp->ah_thtl & ARP_TL_E164) { + if (len > sizeof(struct atm_addr_e164)) + goto bad; + tatm.address_format = T_ATM_E164_ADDR; + } else { + if (len != sizeof(struct atm_addr_nsap)) + goto bad; + tatm.address_format = T_ATM_ENDSYS_ADDR; + } + tatm.address_length = len; + if (KB_COPYDATA(m, plen, len, (caddr_t)tatm.address)) + goto bad; + plen += len; + } else { + tatm.address_format = T_ATM_ABSENT; + tatm.address_length = 0; + } + + if (len = (ahp->ah_tstl & ARP_TL_LMASK)) { + if (((ahp->ah_tstl & ARP_TL_TMASK) != ARP_TL_NSAPA) || + (len != sizeof(struct atm_addr_nsap))) + goto bad; + tatmsub.address_format = T_ATM_ENDSYS_ADDR; + tatmsub.address_length = len; + if (KB_COPYDATA(m, plen, len, (caddr_t)tatmsub.address)) + goto bad; + plen += len; + } else { + tatmsub.address_format = T_ATM_ABSENT; + tatmsub.address_length = 0; + } + + if (len = ahp->ah_tpln) { + if (len != sizeof(struct in_addr)) + goto bad; + if (KB_COPYDATA(m, plen, len, (caddr_t)&tip)) + goto bad; + plen += len; + } else { + tip.s_addr = 0; + } + + /* + * Verify packet length + */ + for (len = 0, n = m; n; n = KB_NEXT(n)) + len += KB_LEN(n); + if (len != plen) + goto bad; + + /* + * Now finish with packet-specific processing + */ + switch (ntohs(ahp->ah_op)) { + case ARP_REQUEST: + proc_arp_req(ivp, m); + break; + + case ARP_REPLY: + proc_arp_rsp(ivp, m); + break; + + case INARP_REQUEST: + proc_inarp_req(ivp, m); + break; + + case INARP_REPLY: + proc_inarp_rsp(ivp, m); + break; + + case ARP_NAK: + proc_arp_nak(ivp, m); + break; + + default: + goto bad; + } + + return; + +bad: + uniarp_stat.uas_rcvdrop++; + if (m) + KB_FREEALL(m); +} + + +/* + * Process an ATMARP request packet + * + * Arguments: + * ivp pointer to input VCC's IPVCC control block + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +proc_arp_req(ivp, m) + struct ipvcc *ivp; + KBuffer *m; +{ + struct ip_nif *inp; + struct atm_nif *nip; + struct siginst *sgp; + struct uniip *uip; + struct uniarp *uap; + struct in_addr myip; + int s = splnet(); + + /* + * Only an arp server should receive these + */ + inp = ivp->iv_ipnif; + nip = inp->inf_nif; + uip = (struct uniip *)inp->inf_isintf; + if ((uip == NULL) || + (uip->uip_arpstate != UIAS_SERVER_ACTIVE)) + goto drop; + + /* + * These should be sent only on SVCs + */ + if ((ivp->iv_flags & IVF_SVC) == 0) + goto drop; + + /* + * Locate our addresses + */ + sgp = nip->nif_pif->pif_siginst; + myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; + + /* + * Target IP address must be present + */ + if (tip.s_addr == 0) + goto drop; + + /* + * Drop packet if both Source addresses aren't present + */ + if ((sip.s_addr == 0) || (satm.address_format == T_ATM_ABSENT)) + goto drop; + + /* + * Source addresses can't be ours + */ + if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && + ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) { + struct vccb *vcp = ivp->iv_conn->co_connvc->cvc_vcc; + + log(LOG_WARNING, + "uniarp: vcc=(%d,%d) reports our ATM address\n", + vcp->vc_vpi, vcp->vc_vci); + goto drop; + } + if (sip.s_addr == myip.s_addr) { + struct vccb *vcp = ivp->iv_conn->co_connvc->cvc_vcc; + + log(LOG_WARNING, + "uniarp: vcc=(%d,%d) reports our IP address\n", + vcp->vc_vpi, vcp->vc_vci); + goto drop; + } + + /* + * Validate Source IP address + */ + if (uniarp_validate_ip(uip, &sip, UAO_REGISTER) != 0) + goto drop; + + /* + * If the source and target IP addresses are the same, then this + * must be a client registration request (RFC-2225). Otherwise, + * try to accomodate old clients (per RFC-2225 8.4.4). + */ + if (sip.s_addr == tip.s_addr) + (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, + UAO_REGISTER); + else { + uap = (struct uniarp *)ivp->iv_arpent; + if ((uap == NULL) || (uap->ua_origin < UAO_REGISTER)) + (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, + UAO_REGISTER); + } + + /* + * Lookup the target IP address in the cache (and also check if + * the query is for our address). + */ + UNIARP_LOOKUP(tip.s_addr, uap); + if (uap && (uap->ua_flags & UAF_VALID)) { + /* + * We've found a valid mapping + */ + (void) uniarp_arp_rsp(uip, &uap->ua_arpmap, &sip, &satm, + &satmsub, ivp); + + } else if (tip.s_addr == myip.s_addr) { + /* + * We're the target, so respond accordingly + */ + (void) uniarp_arp_rsp(uip, &uip->uip_arpsvrmap, &sip, &satm, + &satmsub, ivp); + + } else { + /* + * We don't know who the target is, so NAK the query + */ + (void) uniarp_arp_nak(uip, m, ivp); + m = NULL; + } + +drop: + (void) splx(s); + if (m) + KB_FREEALL(m); + return; +} + + +/* + * Process an ATMARP reply packet + * + * Arguments: + * ivp pointer to input VCC's IPVCC control block + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +proc_arp_rsp(ivp, m) + struct ipvcc *ivp; + KBuffer *m; +{ + struct ip_nif *inp; + struct atm_nif *nip; + struct siginst *sgp; + struct uniip *uip; + struct uniarp *uap; + struct in_addr myip; + int s = splnet(); + + /* + * Only the arp server should send these + */ + inp = ivp->iv_ipnif; + nip = inp->inf_nif; + uip = (struct uniip *)inp->inf_isintf; + if ((uip == NULL) || + (uip->uip_arpsvrvcc != ivp)) + goto drop; + + /* + * Locate our addresses + */ + sgp = nip->nif_pif->pif_siginst; + myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; + + /* + * Target addresses must be ours + */ + if ((tip.s_addr != myip.s_addr) || + !ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) || + !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub)) + goto drop; + + /* + * Drop packet if both Source addresses aren't present + */ + if ((sip.s_addr == 0) || (satm.address_format == T_ATM_ABSENT)) + goto drop; + + /* + * If the Source addresses are ours, this is an arp server + * registration response + */ + if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && + ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) { + if (sip.s_addr == myip.s_addr) { + /* + * Registration response - update our state and + * set a registration refresh timer + */ + if (uip->uip_arpstate == UIAS_CLIENT_REGISTER) + uip->uip_arpstate = UIAS_CLIENT_ACTIVE; + + if (uip->uip_arpstate == UIAS_CLIENT_ACTIVE) { + UNIIP_ARP_CANCEL(uip); + UNIIP_ARP_TIMER(uip, UNIARP_REGIS_REFRESH); + } + + /* + * If the cache entry for the server VCC isn't valid + * yet, then send an Inverse ATMARP request to solicit + * the server's IP address + */ + uap = (struct uniarp *)ivp->iv_arpent; + if ((uap->ua_flags & UAF_VALID) == 0) { + (void) uniarp_inarp_req(uip, &uap->ua_dstatm, + &uap->ua_dstatmsub, ivp); + } + goto drop; + } else { + log(LOG_WARNING, + "uniarp: arpserver has our IP address wrong\n"); + goto drop; + } + } else if (sip.s_addr == myip.s_addr) { + log(LOG_WARNING, + "uniarp: arpserver has our ATM address wrong\n"); + goto drop; + } + + /* + * Validate the Source IP address + */ + if (uniarp_validate_ip(uip, &sip, UAO_LOOKUP) != 0) + goto drop; + + /* + * Now we believe this packet contains an authoritative mapping, + * which we probably need to setup an outgoing SVC connection + */ + (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_LOOKUP); + +drop: + (void) splx(s); + KB_FREEALL(m); + return; +} + + +/* + * Process an ATMARP negative ack packet + * + * Arguments: + * ivp pointer to input VCC's IPVCC control block + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +proc_arp_nak(ivp, m) + struct ipvcc *ivp; + KBuffer *m; +{ + struct ip_nif *inp; + struct atm_nif *nip; + struct siginst *sgp; + struct uniip *uip; + struct uniarp *uap; + struct in_addr myip; + struct ipvcc *inext; + int s = splnet(); + + /* + * Only the arp server should send these + */ + inp = ivp->iv_ipnif; + nip = inp->inf_nif; + uip = (struct uniip *)inp->inf_isintf; + if ((uip == NULL) || + (uip->uip_arpsvrvcc != ivp)) + goto drop; + + /* + * Locate our addresses + */ + sgp = nip->nif_pif->pif_siginst; + myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; + + /* + * Source addresses must be ours + */ + if ((sip.s_addr != myip.s_addr) || + !ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) || + !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) + goto drop; + + /* + * Drop packet if the Target IP address isn't there or if this + * is a registration response, indicating an old or flakey server + */ + if ((tip.s_addr == 0) || (tip.s_addr == myip.s_addr)) + goto drop; + + /* + * Otherwise, see who we were looking for + */ + UNIARP_LOOKUP(tip.s_addr, uap); + if (uap == NULL) + goto drop; + + /* + * This entry isn't valid any longer, so notify all VCCs using this + * entry that they must finish up. The last notify should cause + * this entry to be freed by the vcclose() function. + */ + uap->ua_flags &= ~UAF_VALID; + for (ivp = uap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*inp->inf_arpnotify)(ivp, MAP_FAILED); + } + +drop: + (void) splx(s); + KB_FREEALL(m); + return; +} + + +/* + * Process an InATMARP request packet + * + * Arguments: + * ivp pointer to input VCC's IPVCC control block + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +proc_inarp_req(ivp, m) + struct ipvcc *ivp; + KBuffer *m; +{ + struct ip_nif *inp; + struct atm_nif *nip; + struct siginst *sgp; + struct uniip *uip; + struct in_addr myip; + int s = splnet(); + + /* + * Get interface pointers + */ + inp = ivp->iv_ipnif; + nip = inp->inf_nif; + uip = (struct uniip *)inp->inf_isintf; + if (uip == NULL) + goto drop; + + /* + * Locate our addresses + */ + sgp = nip->nif_pif->pif_siginst; + myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; + + /* + * Packet must have a Source IP address and, if it was received + * over an SVC, a Source ATM address too. + */ + if ((sip.s_addr == 0) || + ((ivp->iv_flags & IVF_SVC) && (satm.address_format == T_ATM_ABSENT))) + goto drop; + + /* + * Validate Source ATM address + * - can't be me + */ + if (satm.address_format != T_ATM_ABSENT) { + if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && + ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, + &satmsub)) + goto drop; + } + + /* + * Validate Source IP address + */ + if ((sip.s_addr == myip.s_addr) || + (uniarp_validate_ip(uip, &sip, UAO_PEER_REQ) != 0)) + goto drop; + + /* + * The Target ATM address is required for a packet received over + * an SVC, optional for a PVC. If one is present, it must be our + * address. + */ + if ((ivp->iv_flags & IVF_SVC) && (tatm.address_format == T_ATM_ABSENT)) + goto drop; + if ((tatm.address_format != T_ATM_ABSENT) && + (!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) || + !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub))) + goto drop; + + /* + * See where this packet is from + */ + if (ivp->iv_flags & IVF_PVC) { + /* + * Process the PVC arp data, although we don't really + * update the arp cache with this information + */ + uniarp_cache_pvc(ivp, &sip, &satm, &satmsub); + + } else if (uip->uip_arpsvrvcc == ivp) { + /* + * Packet is from the arp server, so we've received a + * registration/refresh request (1577 version). + * + * Therefore, update cache with authoritative data. + */ + (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_LOOKUP); + + /* + * Make sure the cache update didn't kill the server VCC + */ + if (uip->uip_arpsvrvcc != ivp) + goto drop; + + /* + * Update the server state and set the + * registration refresh timer + */ + uip->uip_arpstate = UIAS_CLIENT_ACTIVE; + UNIIP_ARP_CANCEL(uip); + UNIIP_ARP_TIMER(uip, UNIARP_REGIS_REFRESH); + } else { + /* + * Otherwise, we consider this source mapping data as + * non-authoritative and update the cache appropriately + */ + if (uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_PEER_REQ)) + goto drop; + } + + /* + * Send an InATMARP response back to originator + */ + (void) uniarp_inarp_rsp(uip, &sip, &satm, &satmsub, ivp); + +drop: + (void) splx(s); + KB_FREEALL(m); + return; +} + + +/* + * Process an InATMARP response packet + * + * Arguments: + * ivp pointer to input VCC's IPVCC control block + * m pointer to input packet buffer chain + * + * Returns: + * none + * + */ +static void +proc_inarp_rsp(ivp, m) + struct ipvcc *ivp; + KBuffer *m; +{ + struct ip_nif *inp; + struct atm_nif *nip; + struct siginst *sgp; + struct uniip *uip; + struct uniarp *uap; + struct in_addr myip; + int s = splnet(); + + /* + * Get interface pointers + */ + inp = ivp->iv_ipnif; + nip = inp->inf_nif; + uip = (struct uniip *)inp->inf_isintf; + if (uip == NULL) + goto drop; + + /* + * Locate our addresses + */ + sgp = nip->nif_pif->pif_siginst; + myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; + + /* + * Packet must have a Source IP address and, if it was received + * over an SVC, a Source ATM address too. + */ + if ((sip.s_addr == 0) || + ((ivp->iv_flags & IVF_SVC) && (satm.address_format == T_ATM_ABSENT))) + goto drop; + + /* + * Validate Source ATM address + * - can't be me + */ + if (satm.address_format != T_ATM_ABSENT) { + if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && + ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, + &satmsub)) + goto drop; + } + + /* + * Validate Source IP address + * - must be in our LIS + * - can't be me + * - can't be broadcast + * - can't be multicast + */ + if ((sip.s_addr == myip.s_addr) || + (uniarp_validate_ip(uip, &sip, UAO_PEER_RSP) != 0)) + goto drop; + + /* + * The Target ATM address is required for a packet received over + * an SVC, optional for a PVC. If one is present, it must be our + * address. + */ + if ((ivp->iv_flags & IVF_SVC) && (tatm.address_format == T_ATM_ABSENT)) + goto drop; + if ((tatm.address_format != T_ATM_ABSENT) && + (!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) || + !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub))) + goto drop; + + /* + * See where this packet is from + */ + if (ivp->iv_flags & IVF_PVC) { + /* + * Process the PVC arp data, although we don't really + * update the arp cache with this information + */ + uniarp_cache_pvc(ivp, &sip, &satm, &satmsub); + + } else { + /* + * Can't tell the difference between an RFC-1577 registration + * and a data connection from a client of another arpserver + * on our LIS (using SCSP) - so we'll update the cache now + * with what we've got. Our clients will get "registered" + * when (if) they query us with an arp request. + */ + (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, + UAO_PEER_RSP); + } + +drop: + (void) splx(s); + KB_FREEALL(m); + return; +} + + +/* + * Print an ATMARP PDU + * + * Arguments: + * ivp pointer to input VCC control block + * m pointer to pdu buffer chain + * msg pointer to message string + * + * Returns: + * none + * + */ +void +uniarp_pdu_print(ivp, m, msg) + struct ipvcc *ivp; + KBuffer *m; + char *msg; +{ + char buf[128]; + struct vccb *vcp; + + vcp = ivp->iv_conn->co_connvc->cvc_vcc; + sprintf(buf, "uniarp %s: vcc=(%d,%d)\n", msg, vcp->vc_vpi, vcp->vc_vci); + atm_pdu_print(m, buf); +} + diff --git a/sys/netatm/uni/uniarp_output.c b/sys/netatm/uni/uniarp_output.c new file mode 100644 index 0000000..81fab93 --- /dev/null +++ b/sys/netatm/uni/uniarp_output.c @@ -0,0 +1,797 @@ +/* + * + * =================================== + * 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: uniarp_output.c,v 1.7 1998/06/29 22:15:41 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI ATMARP support (RFC1577) - Output packet processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniarp_output.c,v 1.7 1998/06/29 22:15:41 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Issue an ATMARP Request PDU + * + * Arguments: + * uip pointer to IP interface + * tip pointer to target IP address + * + * Returns: + * 0 PDU was successfully sent + * else unable to send PDU + * + */ +int +uniarp_arp_req(uip, tip) + struct uniip *uip; + struct in_addr *tip; +{ + KBuffer *m; + struct atmarp_hdr *ahp; + struct atm_nif *nip; + struct ip_nif *inp; + struct ipvcc *ivp; + struct siginst *sip; + char *cp; + int len, err; + + inp = uip->uip_ipnif; + nip = inp->inf_nif; + sip = inp->inf_nif->nif_pif->pif_siginst; + + /* + * Figure out how long pdu is going to be + */ + len = sizeof(struct atmarp_hdr) + (2 * sizeof(struct in_addr)); + switch (sip->si_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + len += sip->si_addr.address_length; + break; + + case T_ATM_E164_ADDR: + len += sip->si_addr.address_length; + if (sip->si_subaddr.address_format == T_ATM_ENDSYS_ADDR) + len += sip->si_subaddr.address_length; + break; + } + + /* + * Get a buffer for pdu + */ + KB_ALLOCPKT(m, len, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) + return (1); + + /* + * Place aligned pdu at end of buffer + */ + KB_TAILALIGN(m, len); + KB_DATASTART(m, ahp, struct atmarp_hdr *); + + /* + * Setup variable fields pointer + */ + cp = (char *)ahp + sizeof(struct atmarp_hdr); + + /* + * Build fields + */ + ahp->ah_hrd = htons(ARP_ATMFORUM); + ahp->ah_pro = htons(ETHERTYPE_IP); + len = sip->si_addr.address_length; + switch (sip->si_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(sip->si_addr.address, cp, len - 1); + ((struct atm_addr_nsap *)cp)->aan_sel = nip->nif_sel; + cp += len; + + ahp->ah_sstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(sip->si_addr.address, cp, len); + cp += len; + + if (sip->si_subaddr.address_format == T_ATM_ENDSYS_ADDR) { + len = sip->si_subaddr.address_length; + ahp->ah_sstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_ssa */ + KM_COPY(sip->si_subaddr.address, cp, len - 1); + ((struct atm_addr_nsap *)cp)->aan_sel = nip->nif_sel; + cp += len; + } else + ahp->ah_sstl = 0; + break; + + default: + ahp->ah_shtl = 0; + ahp->ah_sstl = 0; + } + + ahp->ah_op = htons(ARP_REQUEST); + ahp->ah_spln = sizeof(struct in_addr); + + /* ah_spa */ + KM_COPY((caddr_t)&(IA_SIN(inp->inf_addr)->sin_addr), cp, + sizeof(struct in_addr)); + cp += sizeof(struct in_addr); + + ahp->ah_thtl = 0; + ahp->ah_tstl = 0; + + ahp->ah_tpln = sizeof(struct in_addr); + + /* ah_tpa */ + KM_COPY((caddr_t)tip, cp, sizeof(struct in_addr)); + + /* + * Finally, send the pdu to the ATMARP server + */ + ivp = uip->uip_arpsvrvcc; + if (uniarp_print) + uniarp_pdu_print(ivp, m, "send"); + err = atm_cm_cpcs_data(ivp->iv_arpconn, m); + if (err) { + /* + * Didn't make it + */ + KB_FREEALL(m); + return (1); + } + + return (0); +} + + +/* + * Issue an ATMARP Response PDU + * + * Arguments: + * uip pointer to IP interface + * amp pointer to source map entry + * tip pointer to target IP address + * tatm pointer to target ATM address + * tsub pointer to target ATM subaddress + * ivp pointer to vcc over which to send pdu + * + * Returns: + * 0 PDU was successfully sent + * else unable to send PDU + * + */ +int +uniarp_arp_rsp(uip, amp, tip, tatm, tsub, ivp) + struct uniip *uip; + struct arpmap *amp; + struct in_addr *tip; + Atm_addr *tatm; + Atm_addr *tsub; + struct ipvcc *ivp; +{ + KBuffer *m; + struct atmarp_hdr *ahp; + char *cp; + int len, err; + + /* + * Figure out how long pdu is going to be + */ + len = sizeof(struct atmarp_hdr) + (2 * sizeof(struct in_addr)); + switch (amp->am_dstatm.address_format) { + case T_ATM_ENDSYS_ADDR: + len += amp->am_dstatm.address_length; + break; + + case T_ATM_E164_ADDR: + len += amp->am_dstatm.address_length; + if (amp->am_dstatmsub.address_format == T_ATM_ENDSYS_ADDR) + len += amp->am_dstatmsub.address_length; + break; + } + + switch (tatm->address_format) { + case T_ATM_ENDSYS_ADDR: + len += tatm->address_length; + break; + + case T_ATM_E164_ADDR: + len += tatm->address_length; + if (tsub->address_format == T_ATM_ENDSYS_ADDR) + len += tsub->address_length; + break; + } + + /* + * Get a buffer for pdu + */ + KB_ALLOCPKT(m, len, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) + return (1); + + /* + * Place aligned pdu at end of buffer + */ + KB_TAILALIGN(m, len); + KB_DATASTART(m, ahp, struct atmarp_hdr *); + + /* + * Setup variable fields pointer + */ + cp = (char *)ahp + sizeof(struct atmarp_hdr); + + /* + * Build fields + */ + ahp->ah_hrd = htons(ARP_ATMFORUM); + ahp->ah_pro = htons(ETHERTYPE_IP); + len = amp->am_dstatm.address_length; + switch (amp->am_dstatm.address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(amp->am_dstatm.address, cp, len); + cp += len; + + ahp->ah_sstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(amp->am_dstatm.address, cp, len); + cp += len; + + if (amp->am_dstatmsub.address_format == T_ATM_ENDSYS_ADDR) { + len = amp->am_dstatmsub.address_length; + ahp->ah_sstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_ssa */ + KM_COPY(amp->am_dstatmsub.address, cp, len); + cp += len; + } else + ahp->ah_sstl = 0; + break; + + default: + ahp->ah_shtl = 0; + ahp->ah_sstl = 0; + } + + ahp->ah_op = htons(ARP_REPLY); + ahp->ah_spln = sizeof(struct in_addr); + + /* ah_spa */ + KM_COPY((caddr_t)&->am_dstip, cp, sizeof(struct in_addr)); + cp += sizeof(struct in_addr); + + len = tatm->address_length; + switch (tatm->address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_thtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_tha */ + KM_COPY(tatm->address, cp, len); + cp += len; + + ahp->ah_tstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_thtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_tha */ + KM_COPY(tatm->address, cp, len); + cp += len; + + if (tsub->address_format == T_ATM_ENDSYS_ADDR) { + len = tsub->address_length; + ahp->ah_tstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_tsa */ + KM_COPY(tsub->address, cp, len); + cp += len; + } else + ahp->ah_tstl = 0; + break; + + default: + ahp->ah_thtl = 0; + ahp->ah_tstl = 0; + } + + ahp->ah_tpln = sizeof(struct in_addr); + + /* ah_tpa */ + KM_COPY((caddr_t)tip, cp, sizeof(struct in_addr)); + + /* + * Finally, send the pdu to the vcc peer + */ + if (uniarp_print) + uniarp_pdu_print(ivp, m, "send"); + err = atm_cm_cpcs_data(ivp->iv_arpconn, m); + if (err) { + /* + * Didn't make it + */ + KB_FREEALL(m); + return (1); + } + + return (0); +} + + +/* + * Issue an ATMARP NAK PDU + * + * Arguments: + * uip pointer to IP interface + * m pointer to ATMARP_REQ buffer chain + * ivp pointer to vcc over which to send pdu + * + * Returns: + * 0 PDU was successfully sent + * else unable to send PDU + * + */ +int +uniarp_arp_nak(uip, m, ivp) + struct uniip *uip; + KBuffer *m; + struct ipvcc *ivp; +{ + struct atmarp_hdr *ahp; + int err; + + /* + * Get the fixed fields together + */ + if (KB_LEN(m) < sizeof(struct atmarp_hdr)) { + KB_PULLUP(m, sizeof(struct atmarp_hdr), m); + if (m == NULL) + return (1); + } + KB_DATASTART(m, ahp, struct atmarp_hdr *); + + /* + * Set new op-code + */ + ahp->ah_op = htons(ARP_NAK); + + /* + * Finally, send the pdu to the vcc peer + */ + if (uniarp_print) + uniarp_pdu_print(ivp, m, "send"); + err = atm_cm_cpcs_data(ivp->iv_arpconn, m); + if (err) { + /* + * Didn't make it + */ + KB_FREEALL(m); + return (1); + } + + return (0); +} + + +/* + * Issue an InATMARP Request PDU + * + * Arguments: + * uip pointer to IP interface + * tatm pointer to target ATM address + * tsub pointer to target ATM subaddress + * ivp pointer to vcc over which to send pdu + * + * Returns: + * 0 PDU was successfully sent + * else unable to send PDU + * + */ +int +uniarp_inarp_req(uip, tatm, tsub, ivp) + struct uniip *uip; + Atm_addr *tatm; + Atm_addr *tsub; + struct ipvcc *ivp; +{ + KBuffer *m; + struct atmarp_hdr *ahp; + struct atm_nif *nip; + struct ip_nif *inp; + struct siginst *sip; + char *cp; + int len, err; + + inp = uip->uip_ipnif; + nip = inp->inf_nif; + sip = inp->inf_nif->nif_pif->pif_siginst; + + /* + * Figure out how long pdu is going to be + */ + len = sizeof(struct atmarp_hdr) + sizeof(struct in_addr); + switch (sip->si_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + len += sip->si_addr.address_length; + break; + + case T_ATM_E164_ADDR: + len += sip->si_addr.address_length; + if (sip->si_subaddr.address_format == T_ATM_ENDSYS_ADDR) + len += sip->si_subaddr.address_length; + break; + } + + switch (tatm->address_format) { + case T_ATM_ENDSYS_ADDR: + len += tatm->address_length; + break; + + case T_ATM_E164_ADDR: + len += tatm->address_length; + if (tsub->address_format == T_ATM_ENDSYS_ADDR) + len += tsub->address_length; + break; + } + + /* + * Get a buffer for pdu + */ + KB_ALLOCPKT(m, len, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) + return (1); + + /* + * Place aligned pdu at end of buffer + */ + KB_TAILALIGN(m, len); + KB_DATASTART(m, ahp, struct atmarp_hdr *); + + /* + * Setup variable fields pointer + */ + cp = (char *)ahp + sizeof(struct atmarp_hdr); + + /* + * Build fields + */ + ahp->ah_hrd = htons(ARP_ATMFORUM); + ahp->ah_pro = htons(ETHERTYPE_IP); + len = sip->si_addr.address_length; + switch (sip->si_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(sip->si_addr.address, cp, len - 1); + ((struct atm_addr_nsap *)cp)->aan_sel = nip->nif_sel; + cp += len; + + ahp->ah_sstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(sip->si_addr.address, cp, len); + cp += len; + + if (sip->si_subaddr.address_format == T_ATM_ENDSYS_ADDR) { + len = sip->si_subaddr.address_length; + ahp->ah_sstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_ssa */ + KM_COPY(sip->si_subaddr.address, cp, len - 1); + ((struct atm_addr_nsap *)cp)->aan_sel = nip->nif_sel; + cp += len; + } else + ahp->ah_sstl = 0; + break; + + default: + ahp->ah_shtl = 0; + ahp->ah_sstl = 0; + } + + ahp->ah_op = htons(INARP_REQUEST); + ahp->ah_spln = sizeof(struct in_addr); + + /* ah_spa */ + KM_COPY((caddr_t)&(IA_SIN(inp->inf_addr)->sin_addr), cp, + sizeof(struct in_addr)); + cp += sizeof(struct in_addr); + + len = tatm->address_length; + switch (tatm->address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_thtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_tha */ + KM_COPY(tatm->address, cp, len); + cp += len; + + ahp->ah_tstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_thtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_tha */ + KM_COPY(tatm->address, cp, len); + cp += len; + + if (tsub->address_format == T_ATM_ENDSYS_ADDR) { + len = tsub->address_length; + ahp->ah_tstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_tsa */ + KM_COPY(tsub->address, cp, len); + cp += len; + } else + ahp->ah_tstl = 0; + break; + + default: + ahp->ah_thtl = 0; + ahp->ah_tstl = 0; + } + + ahp->ah_tpln = 0; + + /* + * Finally, send the pdu to the vcc peer + */ + if (uniarp_print) + uniarp_pdu_print(ivp, m, "send"); + err = atm_cm_cpcs_data(ivp->iv_arpconn, m); + if (err) { + /* + * Didn't make it + */ + KB_FREEALL(m); + return (1); + } + + return (0); +} + + +/* + * Issue an InATMARP Response PDU + * + * Arguments: + * uip pointer to IP interface + * tip pointer to target IP address + * tatm pointer to target ATM address + * tsub pointer to target ATM subaddress + * ivp pointer to vcc over which to send pdu + * + * Returns: + * 0 PDU was successfully sent + * else unable to send PDU + * + */ +int +uniarp_inarp_rsp(uip, tip, tatm, tsub, ivp) + struct uniip *uip; + struct in_addr *tip; + Atm_addr *tatm; + Atm_addr *tsub; + struct ipvcc *ivp; +{ + KBuffer *m; + struct atmarp_hdr *ahp; + struct atm_nif *nip; + struct ip_nif *inp; + struct siginst *sip; + char *cp; + int len, err; + + inp = uip->uip_ipnif; + nip = inp->inf_nif; + sip = inp->inf_nif->nif_pif->pif_siginst; + + /* + * Figure out how long pdu is going to be + */ + len = sizeof(struct atmarp_hdr) + (2 * sizeof(struct in_addr)); + switch (sip->si_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + len += sip->si_addr.address_length; + break; + + case T_ATM_E164_ADDR: + len += sip->si_addr.address_length; + if (sip->si_subaddr.address_format == T_ATM_ENDSYS_ADDR) + len += sip->si_subaddr.address_length; + break; + } + + switch (tatm->address_format) { + case T_ATM_ENDSYS_ADDR: + len += tatm->address_length; + break; + + case T_ATM_E164_ADDR: + len += tatm->address_length; + if (tsub->address_format == T_ATM_ENDSYS_ADDR) + len += tsub->address_length; + break; + } + + /* + * Get a buffer for pdu + */ + KB_ALLOCPKT(m, len, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) + return (1); + + /* + * Place aligned pdu at end of buffer + */ + KB_TAILALIGN(m, len); + KB_DATASTART(m, ahp, struct atmarp_hdr *); + + /* + * Setup variable fields pointer + */ + cp = (char *)ahp + sizeof(struct atmarp_hdr); + + /* + * Build fields + */ + ahp->ah_hrd = htons(ARP_ATMFORUM); + ahp->ah_pro = htons(ETHERTYPE_IP); + len = sip->si_addr.address_length; + switch (sip->si_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(sip->si_addr.address, cp, len - 1); + ((struct atm_addr_nsap *)cp)->aan_sel = nip->nif_sel; + cp += len; + + ahp->ah_sstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_sha */ + KM_COPY(sip->si_addr.address, cp, len); + cp += len; + + if (sip->si_subaddr.address_format == T_ATM_ENDSYS_ADDR) { + len = sip->si_subaddr.address_length; + ahp->ah_sstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_ssa */ + KM_COPY(sip->si_subaddr.address, cp, len - 1); + ((struct atm_addr_nsap *)cp)->aan_sel = nip->nif_sel; + cp += len; + } else + ahp->ah_sstl = 0; + break; + + default: + ahp->ah_shtl = 0; + ahp->ah_sstl = 0; + } + + ahp->ah_op = htons(INARP_REPLY); + ahp->ah_spln = sizeof(struct in_addr); + + /* ah_spa */ + KM_COPY((caddr_t)&(IA_SIN(inp->inf_addr)->sin_addr), cp, + sizeof(struct in_addr)); + cp += sizeof(struct in_addr); + + len = tatm->address_length; + switch (tatm->address_format) { + case T_ATM_ENDSYS_ADDR: + ahp->ah_thtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_tha */ + KM_COPY(tatm->address, cp, len); + cp += len; + + ahp->ah_tstl = 0; + break; + + case T_ATM_E164_ADDR: + ahp->ah_thtl = ARP_TL_E164 | (len & ARP_TL_LMASK); + + /* ah_tha */ + KM_COPY(tatm->address, cp, len); + cp += len; + + if (tsub->address_format == T_ATM_ENDSYS_ADDR) { + len = tsub->address_length; + ahp->ah_tstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); + + /* ah_tsa */ + KM_COPY(tsub->address, cp, len); + cp += len; + } else + ahp->ah_tstl = 0; + break; + + default: + ahp->ah_thtl = 0; + ahp->ah_tstl = 0; + } + + ahp->ah_tpln = sizeof(struct in_addr); + + /* ah_tpa */ + KM_COPY((caddr_t)tip, cp, sizeof(struct in_addr)); + + /* + * Finally, send the pdu to the vcc peer + */ + if (uniarp_print) + uniarp_pdu_print(ivp, m, "send"); + err = atm_cm_cpcs_data(ivp->iv_arpconn, m); + if (err) { + /* + * Didn't make it + */ + KB_FREEALL(m); + return (1); + } + + return (0); +} + diff --git a/sys/netatm/uni/uniarp_timer.c b/sys/netatm/uni/uniarp_timer.c new file mode 100644 index 0000000..1f00202 --- /dev/null +++ b/sys/netatm/uni/uniarp_timer.c @@ -0,0 +1,320 @@ +/* + * + * =================================== + * 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: uniarp_timer.c,v 1.8 1998/06/29 22:44:31 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI ATMARP support (RFC1577) - Timer processing + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniarp_timer.c,v 1.8 1998/06/29 22:44:31 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Local functions + */ +static void uniarp_svc_oldage __P((struct uniarp *)); +static void uniarp_pvc_oldage __P((struct uniarp *)); + + +/* + * Process a UNI ATMARP entry timeout + * + * Called when a previously scheduled uniarp control block timer expires. + * + * Called at splnet. + * + * Arguments: + * tip pointer to uniarp timer control block + * + * Returns: + * none + * + */ +void +uniarp_timeout(tip) + struct atm_time *tip; +{ + struct uniip *uip; + struct uniarp *uap; + struct ipvcc *ivp; + + + /* + * Back-off to uniarp control block + */ + uap = (struct uniarp *) + ((caddr_t)tip - (int)(&((struct uniarp *)0)->ua_time)); + uip = uap->ua_intf; + + + /* + * Do we know the IP address for this entry yet?? + */ + if (uap->ua_dstip.s_addr == 0) { + + /* + * No, then send another InATMARP_REQ on each active VCC + * associated with this entry to solicit the peer's identity. + */ + for (ivp = uap->ua_ivp; ivp; ivp = ivp->iv_arpnext) { + if (ivp->iv_state != IPVCC_ACTIVE) + continue; + (void) uniarp_inarp_req(uip, &uap->ua_dstatm, + &uap->ua_dstatmsub, ivp); + } + + /* + * Restart retry timer + */ + UNIARP_TIMER(uap, UNIARP_ARP_RETRY); + } else { + /* + * Yes, then we're trying to find the ATM address for this + * IP address - so send another ATMARP_REQ to the arpserver + * (if it's up at the moment) + */ + if (uip->uip_arpstate == UIAS_CLIENT_ACTIVE) + (void) uniarp_arp_req(uip, &uap->ua_dstip); + + /* + * Restart retry timer + */ + UNIARP_TIMER(uap, UNIARP_ARP_RETRY); + } + + return; +} + + +/* + * Process an UNI ARP SVC entry aging timer expiration + * + * This function is called when an SVC arp entry's aging timer has expired. + * + * Called at splnet(). + * + * Arguments: + * uap pointer to atmarp table entry + * + * Returns: + * none + * + */ +static void +uniarp_svc_oldage(uap) + struct uniarp *uap; +{ + struct ipvcc *ivp, *inext; + struct uniip *uip = uap->ua_intf; + + + /* + * Permanent (manually installed) entries are never aged + */ + if (uap->ua_origin >= UAO_PERM) + return; + + /* + * If entry is valid and we're out of retrys, tell + * IP/ATM that the SVCs can't be used + */ + if ((uap->ua_flags & UAF_VALID) && (uap->ua_retry-- == 0)) { + uap->ua_flags |= UAF_LOCKED; + for (ivp = uap->ua_ivp; ivp; ivp = inext) { + inext = ivp->iv_arpnext; + (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_INVALID); + } + uap->ua_flags &= ~(UAF_LOCKED | UAF_VALID); + uap->ua_origin = 0; + + /* + * Delete and free an unused entry + */ + if (uap->ua_ivp == NULL) { + UNIARP_CANCEL(uap); + UNIARP_DELETE(uap); + atm_free((caddr_t)uap); + return; + } + } + + /* + * We want to try and refresh this entry but we don't want + * to keep unused entries laying around forever. + */ + if (uap->ua_ivp || (uap->ua_flags & UAF_USED)) { + if (uip->uip_arpstate == UIAS_CLIENT_ACTIVE) { + /* + * If we are a client (and the server VCC is active), + * then we'll ask the server for a refresh + */ + (void) uniarp_arp_req(uip, &uap->ua_dstip); + } else { + /* + * Otherwise, solicit the each active VCC peer with + * an Inverse ATMARP + */ + for (ivp = uap->ua_ivp; ivp; ivp = ivp->iv_arpnext) { + if (ivp->iv_state != IPVCC_ACTIVE) + continue; + (void) uniarp_inarp_req(uip, &uap->ua_dstatm, + &uap->ua_dstatmsub, ivp); + } + } + } + + /* + * Reset timeout + */ + if (uap->ua_flags & UAF_VALID) + uap->ua_aging = UNIARP_RETRY_AGE; + else + uap->ua_aging = UNIARP_REVALID_AGE; + + return; +} + + +/* + * Process an UNI ARP PVC entry aging timer expiration + * + * This function is called when a PVC arp entry's aging timer has expired. + * + * Called at splnet(). + * + * Arguments: + * uap pointer to atmarp table entry + * + * Returns: + * none + * + */ +static void +uniarp_pvc_oldage(uap) + struct uniarp *uap; +{ + struct ipvcc *ivp = uap->ua_ivp; + + /* + * If entry is valid and we're out of retrys, tell + * IP/ATM that PVC can't be used + */ + if ((uap->ua_flags & UAF_VALID) && (uap->ua_retry-- == 0)) { + (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_INVALID); + uap->ua_flags &= ~UAF_VALID; + } + + /* + * Solicit peer with Inverse ATMARP + */ + (void) uniarp_inarp_req(uap->ua_intf, &uap->ua_dstatm, + &uap->ua_dstatmsub, ivp); + + /* + * Reset timeout + */ + if (uap->ua_flags & UAF_VALID) + uap->ua_aging = UNIARP_RETRY_AGE; + else + uap->ua_aging = UNIARP_REVALID_AGE; + + return; +} + + +/* + * Process a UNI ARP aging timer tick + * + * This function is called every UNIARP_AGING seconds, in order to age + * all the arp table entries. If an entry's timer is expired, then the + * uniarp old-age timeout function will be called for that entry. + * + * Called at splnet. + * + * Arguments: + * tip pointer to uniarp aging timer control block + * + * Returns: + * none + * + */ +void +uniarp_aging(tip) + struct atm_time *tip; +{ + struct uniarp *uap, *unext; + int i; + + + /* + * Schedule next timeout + */ + atm_timeout(&uniarp_timer, UNIARP_AGING, uniarp_aging); + + /* + * Run through arp table bumping each entry's aging timer. + * If an expired timer is found, process that entry. + */ + for (i = 0; i < UNIARP_HASHSIZ; i++) { + for (uap = uniarp_arptab[i]; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_aging && --uap->ua_aging == 0) + uniarp_svc_oldage(uap); + } + } + + /* + * Check out PVC aging timers too + */ + for (uap = uniarp_pvctab; uap; uap = unext) { + unext = uap->ua_next; + + if (uap->ua_aging && --uap->ua_aging == 0) + uniarp_pvc_oldage(uap); + } + + /* + * Only fully resolved SVC entries need aging, so there's no need + * to examine the 'no map' table + */ +} + diff --git a/sys/netatm/uni/uniarp_vcm.c b/sys/netatm/uni/uniarp_vcm.c new file mode 100644 index 0000000..9b1f211 --- /dev/null +++ b/sys/netatm/uni/uniarp_vcm.c @@ -0,0 +1,708 @@ +/* + * + * =================================== + * 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: uniarp_vcm.c,v 1.10 1998/06/29 22:15:46 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI ATMARP support (RFC1577) - Virtual Channel Management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniarp_vcm.c,v 1.10 1998/06/29 22:15:46 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Local variables + */ +static struct attr_llc uniarp_llc = { + T_ATM_PRESENT, + { + T_ATM_LLC_SHARING, + 8, + {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x06} + } +}; + +static struct t_atm_cause uniarp_cause = { + T_ATM_ITU_CODING, + T_ATM_LOC_USER, + T_ATM_CAUSE_TEMPORARY_FAILURE, + {0, 0, 0, 0} +}; + + +/* + * Process a new PVC requiring ATMARP support + * + * This function is called after IP/ATM has successfully opened a PVC which + * requires ATMARP support. We will send an InATMARP request over the PVC + * to elicit a response from the PVC's ATMARP peer informing us of its + * network address. This information will also be used by IP/ATM in order + * to complete its address-to-VC mapping table. + * + * Arguments: + * ivp pointer to PVC's IPVCC control block + * + * Returns: + * MAP_PROCEEDING - OK so far, querying for peer's mapping + * MAP_FAILED - error, unable to allocate resources + * + */ +int +uniarp_pvcopen(ivp) + struct ipvcc *ivp; +{ + struct uniip *uip; + struct uniarp *uap; + int s, err; + + ATM_DEBUG1("uniarp_pvcopen: ivp=0x%x\n", (int)ivp); + + ivp->iv_arpent = NULL; + + /* + * Check things out + */ + if ((ivp->iv_flags & IVF_LLC) == 0) + return (MAP_FAILED); + + /* + * Get uni interface + */ + uip = (struct uniip *)ivp->iv_ipnif->inf_isintf; + if (uip == NULL) + return (MAP_FAILED); + + /* + * Get an arp map entry + */ + uap = (struct uniarp *)atm_allocate(&uniarp_pool); + if (uap == NULL) + return (MAP_FAILED); + + /* + * Create our CM connection + */ + err = atm_cm_addllc(&uniarp_endpt, ivp, &uniarp_llc, + ivp->iv_conn, &ivp->iv_arpconn); + if (err) { + /* + * We don't take no (or maybe) for an answer + */ + if (ivp->iv_arpconn) { + (void) atm_cm_release(ivp->iv_arpconn, &uniarp_cause); + ivp->iv_arpconn = NULL; + } + atm_free((caddr_t)uap); + return (MAP_FAILED); + } + + /* + * Get map entry set up + */ + s = splnet(); + uap->ua_dstatm.address_format = T_ATM_ABSENT; + uap->ua_dstatmsub.address_format = T_ATM_ABSENT; + uap->ua_intf = uip; + + /* + * Put ivp on arp entry chain + */ + LINK2TAIL(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)uap; + + /* + * Put arp entry on pvc chain + */ + LINK2TAIL(uap, struct uniarp, uniarp_pvctab, ua_next); + + /* + * Send Inverse ATMARP request + */ + (void) uniarp_inarp_req(uip, &uap->ua_dstatm, &uap->ua_dstatmsub, ivp); + + /* + * Start resend timer + */ + uap->ua_aging = UNIARP_REVALID_AGE; + + (void) splx(s); + return (MAP_PROCEEDING); +} + + +/* + * Process a new outgoing SVC requiring ATMARP support + * + * This function is called by the IP/ATM module to resolve a destination + * IP address to an ATM address in order to open an SVC to that destination. + * If a valid mapping is already in our cache, then we just tell the caller + * about it and that's that. Otherwise, we have to allocate a new arp entry + * and issue a query for the mapping. + * + * Arguments: + * ivp pointer to SVC's IPVCC control block + * dst pointer to destination IP address + * + * Returns: + * MAP_VALID - Got the answer, returned via iv_arpent field. + * MAP_PROCEEDING - OK so far, querying for peer's mapping + * MAP_FAILED - error, unable to allocate resources + * + */ +int +uniarp_svcout(ivp, dst) + struct ipvcc *ivp; + struct in_addr *dst; +{ + struct uniip *uip; + struct uniarp *uap; + int s = splnet(); + + ATM_DEBUG2("uniarp_svcout: ivp=0x%x,dst=0x%x\n", (int)ivp, dst->s_addr); + + ivp->iv_arpent = NULL; + + /* + * Get uni interface + */ + uip = (struct uniip *)ivp->iv_ipnif->inf_isintf; + if (uip == NULL) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Lookup IP destination address + */ + UNIARP_LOOKUP(dst->s_addr, uap); + + if (uap) { + /* + * We've got an entry, verify interface + */ + if (uap->ua_intf != uip) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Chain this vcc onto entry + */ + LINK2TAIL(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)uap; + uap->ua_flags |= UAF_USED; + + if (uap->ua_flags & UAF_VALID) { + /* + * Entry is valid, we're done + */ + (void) splx(s); + return (MAP_VALID); + } else { + /* + * We're already looking for this address + */ + (void) splx(s); + return (MAP_PROCEEDING); + } + } + + /* + * No info in the cache. If we're the server, then + * we're already authoritative, so just deny request. + * If we're a client but the server VCC isn't open we + * also deny the request. + */ + if (uip->uip_arpstate != UIAS_CLIENT_ACTIVE) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * We're a client with an open VCC to the server, get a new arp entry + */ + uap = (struct uniarp *)atm_allocate(&uniarp_pool); + if (uap == NULL) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Get entry set up + */ + uap->ua_dstip.s_addr = dst->s_addr; + uap->ua_dstatm.address_format = T_ATM_ABSENT; + uap->ua_dstatm.address_length = 0; + uap->ua_dstatmsub.address_format = T_ATM_ABSENT; + uap->ua_dstatmsub.address_length = 0; + uap->ua_intf = uip; + + /* + * Link ipvcc to arp entry for later notification + */ + LINK2TAIL(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)uap; + uap->ua_flags |= UAF_USED; + + /* + * Add arp entry to table + */ + UNIARP_ADD(uap); + + /* + * Issue arp request for this address + */ + (void) uniarp_arp_req(uip, dst); + + /* + * Start retry timer + */ + UNIARP_TIMER(uap, UNIARP_ARP_RETRY); + + (void) splx(s); + return (MAP_PROCEEDING); +} + + +/* + * Process a new incoming SVC requiring ATMARP support + * + * This function is called by the IP/ATM module to resolve a caller's ATM + * address to its IP address for an incoming call in order to allow a + * bi-directional flow of IP packets on the SVC. If a valid mapping is + * already in our cache, then we will use it. Otherwise, we have to allocate + * a new arp entry and wait for the SVC to become active so that we can issue + * an InATMARP to the peer. + * + * Arguments: + * ivp pointer to SVC's IPVCC control block + * dst pointer to caller's ATM address + * dstsub pointer to caller's ATM subaddress + * + * Returns: + * MAP_VALID - Got the answer, returned via iv_arpent field. + * MAP_PROCEEDING - OK so far, querying for peer's mapping + * MAP_FAILED - error, unable to allocate resources + * + */ +int +uniarp_svcin(ivp, dst, dstsub) + struct ipvcc *ivp; + Atm_addr *dst; + Atm_addr *dstsub; +{ + struct uniip *uip; + struct uniarp *uap; + int found = 0, i, s = splnet(); + + ATM_DEBUG1("uniarp_svcin: ivp=0x%x\n", (int)ivp); + + /* + * Clear ARP entry field + */ + ivp->iv_arpent = NULL; + + /* + * Check things out + */ + if ((ivp->iv_flags & IVF_LLC) == 0) + return (MAP_FAILED); + + /* + * Get uni interface + */ + uip = (struct uniip *)ivp->iv_ipnif->inf_isintf; + if (uip == NULL) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Make sure we're configured as a client or server + */ + if (uip->uip_arpstate == UIAS_NOTCONF) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * If we know the caller's ATM address, look it up + */ + uap = NULL; + if (dst->address_format != T_ATM_ABSENT) { + for (i = 0; (i < UNIARP_HASHSIZ) && (found == 0); i++) { + for (uap = uniarp_arptab[i]; uap; uap = uap->ua_next) { + if (ATM_ADDR_EQUAL(dst, &uap->ua_dstatm) && + ATM_ADDR_EQUAL(dstsub, &uap->ua_dstatmsub)){ + found = 1; + break; + } + } + } + if (uap == NULL) { + for (uap = uniarp_nomaptab; uap; uap = uap->ua_next) { + if (ATM_ADDR_EQUAL(dst, &uap->ua_dstatm) && + ATM_ADDR_EQUAL(dstsub, &uap->ua_dstatmsub)) + break; + } + } + } + + if (uap) { + /* + * We've got an entry, verify interface + */ + if (uap->ua_intf != uip) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Chain the vcc onto this entry + */ + LINK2TAIL(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)uap; + uap->ua_flags |= UAF_USED; + + if (uap->ua_flags & UAF_VALID) { + /* + * Entry is valid, we're done + */ + (void) splx(s); + return (MAP_VALID); + } else { + /* + * We're already looking for this address + */ + (void) splx(s); + return (MAP_PROCEEDING); + } + } + + /* + * No info in the cache - get a new arp entry + */ + uap = (struct uniarp *)atm_allocate(&uniarp_pool); + if (uap == NULL) { + (void) splx(s); + return (MAP_FAILED); + } + + /* + * Get entry set up + */ + ATM_ADDR_COPY(dst, &uap->ua_dstatm); + ATM_ADDR_COPY(dstsub, &uap->ua_dstatmsub); + uap->ua_intf = uip; + + /* + * Link ipvcc to arp entry for later notification + */ + LINK2TAIL(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + ivp->iv_arpent = (struct arpmap *)uap; + uap->ua_flags |= UAF_USED; + + /* + * Add arp entry to 'nomap' table + */ + LINK2TAIL(uap, struct uniarp, uniarp_nomaptab, ua_next); + + (void) splx(s); + + /* + * Now we just wait for SVC to become active + */ + return (MAP_PROCEEDING); +} + + +/* + * Process ARP SVC activation notification + * + * This function is called by the IP/ATM module whenever a previously + * opened SVC has successfully been connected. + * + * Arguments: + * ivp pointer to SVC's IPVCC control block + * + * Returns: + * 0 activation processing successful + * errno activation failed - reason indicated + * + */ +int +uniarp_svcactive(ivp) + struct ipvcc *ivp; +{ + struct ip_nif *inp; + struct uniip *uip; + struct uniarp *uap; + int err, s = splnet(); + + ATM_DEBUG1("uniarp_svcactive: ivp=0x%x\n", (int)ivp); + + inp = ivp->iv_ipnif; + uip = (struct uniip *)inp->inf_isintf; + uap = (struct uniarp *)ivp->iv_arpent; + + /* + * First, we need to create our CM connection + */ + err = atm_cm_addllc(&uniarp_endpt, ivp, &uniarp_llc, + ivp->iv_conn, &ivp->iv_arpconn); + if (err) { + /* + * We don't take no (or maybe) for an answer + */ + if (ivp->iv_arpconn) { + (void) atm_cm_release(ivp->iv_arpconn, &uniarp_cause); + ivp->iv_arpconn = NULL; + } + return (err); + } + + /* + * Is this the client->server vcc?? + */ + if (uip->uip_arpsvrvcc == ivp) { + + /* + * Yep, go into the client registration phase + */ + uip->uip_arpstate = UIAS_CLIENT_REGISTER; + + /* + * To register ourselves, RFC1577 says we should wait + * around for the server to send us an InATMARP_Request. + * However, draft-1577+ just has us send an ATMARP_Request + * for our own address. To keep everyone happy, we'll go + * with both and see what works! + */ + (void) uniarp_arp_req(uip, &(IA_SIN(inp->inf_addr)->sin_addr)); + + /* + * Start retry timer + */ + UNIIP_ARP_TIMER(uip, 1 * ATM_HZ); + + (void) splx(s); + return (0); + } + + /* + * Send an InATMARP_Request on this VCC to find out/notify who's at + * the other end. If we're the server, this will start off the + * RFC1577 registration procedure. If we're a client, then this + * SVC is for user data and it's pretty likely that both ends are + * going to be sending packets. So, if we're the caller, we'll be + * nice and let the callee know right away who we are. If we're the + * callee, let's find out asap the caller's IP address. + */ + (void) uniarp_inarp_req(uip, &uap->ua_dstatm, &uap->ua_dstatmsub, ivp); + + /* + * Start retry timer if entry isn't valid yet + */ + if (((uap->ua_flags & UAF_VALID) == 0) && + ((uap->ua_time.ti_flag & TIF_QUEUED) == 0)) + UNIARP_TIMER(uap, UNIARP_ARP_RETRY); + + (void) splx(s); + return (0); +} + + +/* + * Process VCC close + * + * This function is called just prior to IP/ATM closing a VCC which + * supports ATMARP. We'll sever our links to the VCC and then + * figure out how much more cleanup we need to do for now. + * + * Arguments: + * ivp pointer to VCC's IPVCC control block + * + * Returns: + * none + * + */ +void +uniarp_vcclose(ivp) + struct ipvcc *ivp; +{ + struct uniip *uip; + struct uniarp *uap; + int s; + + ATM_DEBUG1("uniarp_vcclose: ivp=0x%x\n", (int)ivp); + + /* + * Close our CM connection + */ + if (ivp->iv_arpconn) { + (void) atm_cm_release(ivp->iv_arpconn, &uniarp_cause); + ivp->iv_arpconn = NULL; + } + + /* + * Get atmarp entry + */ + if ((uap = (struct uniarp *)ivp->iv_arpent) == NULL) + return; + uip = uap->ua_intf; + + s = splnet(); + + /* + * If this is the arpserver VCC, then schedule ourselves to + * reopen the connection soon + */ + if (uip->uip_arpsvrvcc == ivp) { + uip->uip_arpsvrvcc = NULL; + uip->uip_arpstate = UIAS_CLIENT_POPEN; + UNIIP_ARP_CANCEL(uip); + UNIIP_ARP_TIMER(uip, 5 * ATM_HZ); + } + + /* + * Remove IP VCC from chain + */ + UNLINK(ivp, struct ipvcc, uap->ua_ivp, iv_arpnext); + + /* + * SVCs and PVCs are handled separately + */ + if (ivp->iv_flags & IVF_SVC) { + /* + * If the mapping is currently valid or in use, or if there + * are other VCCs still using this mapping, we're done for now + */ + if ((uap->ua_flags & (UAF_VALID | UAF_LOCKED)) || + (uap->ua_origin >= UAO_PERM) || + (uap->ua_ivp != NULL)) { + (void) splx(s); + return; + } + + /* + * Unlink the entry + */ + if (uap->ua_dstip.s_addr == 0) { + UNLINK(uap, struct uniarp, uniarp_nomaptab, ua_next); + } else { + UNIARP_DELETE(uap); + } + } else { + /* + * Remove entry from pvc table + */ + UNLINK(uap, struct uniarp, uniarp_pvctab, ua_next); + } + + UNIARP_CANCEL(uap); + + /* + * Finally, free the entry + */ + atm_free((caddr_t)uap); + + (void) splx(s); + return; +} + + +/* + * Process ATMARP VCC Connected Notification + * + * Arguments: + * toku owner's connection token (ipvcc protocol block) + * + * Returns: + * none + * + */ +void +uniarp_connected(toku) + void *toku; +{ + + /* + * Since we only do atm_cm_addllc()'s on active connections, + * we should never get called here... + */ + panic("uniarp_connected"); +} + + +/* + * Process ATMARP VCC Cleared Notification + * + * Arguments: + * toku owner's connection token (ipvcc protocol block) + * cause pointer to cause code + * + * Returns: + * none + * + */ +void +uniarp_cleared(toku, cause) + void *toku; + struct t_atm_cause *cause; +{ + struct ipvcc *ivp = toku; + int s; + + s = splnet(); + + /* + * We're done with VCC + */ + ivp->iv_arpconn = NULL; + + /* + * If IP is finished with VCC, then we'll free it + */ + if (ivp->iv_state == IPVCC_FREE) + atm_free((caddr_t)ivp); + + (void) splx(s); +} + diff --git a/sys/netatm/uni/uniip.c b/sys/netatm/uni/uniip.c new file mode 100644 index 0000000..0039585 --- /dev/null +++ b/sys/netatm/uni/uniip.c @@ -0,0 +1,252 @@ +/* + * + * =================================== + * 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: uniip.c,v 1.6 1998/05/18 19:18:42 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * UNI IP interface module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: uniip.c,v 1.6 1998/05/18 19:18:42 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/uniip_var.h> + + +/* + * Local functions + */ +static int uniip_ipact __P((struct ip_nif *)); +static int uniip_ipdact __P((struct ip_nif *)); + + +/* + * Global variables + */ +struct uniip *uniip_head = NULL; + +struct ip_serv uniip_ipserv = { + uniip_ipact, + uniip_ipdact, + uniarp_ioctl, + uniarp_pvcopen, + uniarp_svcout, + uniarp_svcin, + uniarp_svcactive, + uniarp_vcclose, + NULL, + {ATM_AAL5, ATM_ENC_LLC}, +}; + + +/* + * Local variables + */ +static struct sp_info uniip_pool = { + "uni ip pool", /* si_name */ + sizeof(struct uniip), /* si_blksiz */ + 2, /* si_blkcnt */ + 100 /* si_maxallow */ +}; + + +/* + * Process module loading notification + * + * Called whenever the uni module is initializing. + * + * Arguments: + * none + * + * Returns: + * 0 initialization successful + * errno initialization failed - reason indicated + * + */ +int +uniip_start() +{ + int err; + + /* + * Tell arp to initialize stuff + */ + err = uniarp_start(); + + return (err); +} + + +/* + * Process module unloading notification + * + * Called whenever the uni module is about to be unloaded. All signalling + * instances will have been previously detached. All uniip resources + * must be freed now. + * + * Arguments: + * none + * + * Returns: + * 0 shutdown was successful + * errno shutdown failed - reason indicated + * + */ +int +uniip_stop() +{ + + /* + * All IP interfaces should be gone + */ + if (uniip_head) + return (EBUSY); + + /* + * Tell arp to stop + */ + uniarp_stop(); + + /* + * Free our storage pools + */ + atm_release_pool(&uniip_pool); + + return (0); +} + + +/* + * Process IP Network Interface Activation + * + * Called whenever an IP network interface becomes active. + * + * Called at splnet. + * + * Arguments: + * inp pointer to IP network interface + * + * Returns: + * 0 command successful + * errno command failed - reason indicated + * + */ +static int +uniip_ipact(inp) + struct ip_nif *inp; +{ + struct uniip *uip; + + /* + * Make sure we don't already have this interface + */ + for (uip = uniip_head; uip; uip = uip->uip_next) { + if (uip->uip_ipnif == inp) + return (EEXIST); + } + + /* + * Get a new interface control block + */ + uip = (struct uniip *)atm_allocate(&uniip_pool); + if (uip == NULL) + return (ENOMEM); + + /* + * Initialize and link up + */ + uip->uip_ipnif = inp; + LINK2TAIL(uip, struct uniip, uniip_head, uip_next); + + /* + * Link from IP world + */ + inp->inf_isintf = (caddr_t)uip; + + /* + * Tell arp about new interface + */ + uniarp_ipact(uip); + + return (0); +} + + +/* + * Process IP Network Interface Deactivation + * + * Called whenever an IP network interface becomes inactive. + * + * Called at splnet. + * + * Arguments: + * inp pointer to IP network interface + * + * Returns: + * 0 command successful + * errno command failed - reason indicated + * + */ +static int +uniip_ipdact(inp) + struct ip_nif *inp; +{ + struct uniip *uip; + + /* + * Get the appropriate IP interface block + */ + uip = (struct uniip *)inp->inf_isintf; + if (uip == NULL) + return (ENXIO); + + /* + * Let arp know about this + */ + uniarp_ipdact(uip); + + /* + * Free interface info + */ + UNLINK(uip, struct uniip, uniip_head, uip_next); + if (uip->uip_prefix != NULL) + KM_FREE(uip->uip_prefix, + uip->uip_nprefix * sizeof(struct uniarp_prf), M_DEVBUF); + atm_free((caddr_t)uip); + + return (0); +} + diff --git a/sys/netatm/uni/uniip_var.h b/sys/netatm/uni/uniip_var.h new file mode 100644 index 0000000..ea1eb8c --- /dev/null +++ b/sys/netatm/uni/uniip_var.h @@ -0,0 +1,318 @@ +/* + * + * =================================== + * 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: uniip_var.h,v 1.9 1998/07/13 00:00:39 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * IP interface control blocks + * + */ + +#ifndef _UNI_UNIIP_VAR_H +#define _UNI_UNIIP_VAR_H + +#ifdef ATM_KERNEL +/* + * UNI IP network interface structure. There will be one such structure for + * each IP network interface attached via a UNI signalling instance. + */ +struct uniip { + struct uniip *uip_next; /* Next attached IP interface */ + struct ip_nif *uip_ipnif; /* IP network interface */ + u_char uip_flags; /* Interface flags (see below) */ + + /* ATMARP (RFC1577) */ + u_char uip_arpstate; /* ARP interface state (see below) */ + struct arpmap uip_arpsvrmap; /* ATMARP server map info */ + struct ipvcc *uip_arpsvrvcc; /* ATMARP server's VCC */ + int uip_nprefix; /* Count of IP prefixes (server only) */ + struct uniarp_prf *uip_prefix; /* Array of IP prefixes (server only) */ + struct atm_time uip_arptime; /* ARP timer controls */ +}; +#define uip_arpsvrip uip_arpsvrmap.am_dstip +#define uip_arpsvratm uip_arpsvrmap.am_dstatm +#define uip_arpsvrsub uip_arpsvrmap.am_dstatmsub +#endif /* ATM_KERNEL */ + +/* + * UNI Interface Flags + */ +#define UIF_IFADDR 0x01 /* Interface address is set */ + +/* + * UNI ARP Interface States + */ +#define UIAS_NOTCONF 1 /* Not configured */ +#define UIAS_SERVER_ACTIVE 2 /* Server - active */ +#define UIAS_CLIENT_PADDR 3 /* Client - pending ATM address */ +#define UIAS_CLIENT_POPEN 4 /* Client - pending server vcc open */ +#define UIAS_CLIENT_REGISTER 5 /* Client - registering with server */ +#define UIAS_CLIENT_ACTIVE 6 /* Client - active */ + + +#ifdef ATM_KERNEL +/* + * Structure for allowable IP prefixes for ATMARP server registration + */ +struct uniarp_prf { + struct in_addr upf_addr; /* Prefix address */ + struct in_addr upf_mask; /* Prefix mask */ +}; + + +/* + * UNI ARP protocol constants + */ +#define UNIARP_AGING (60 * ATM_HZ) /* Aging timer tick */ +#define UNIARP_HASHSIZ 19 /* Hash table size */ +#define UNIARP_REGIS_REFRESH (15 * 60 * ATM_HZ) + /* Client registration refresh timer */ +#define UNIARP_REGIS_RETRY (60 * ATM_HZ) + /* Client registration retry timer */ +#define UNIARP_ARP_RETRY (3 * ATM_HZ) /* ARP command retry timer */ +#define UNIARP_CLIENT_AGE 12 /* Client validation timeout */ +#define UNIARP_CLIENT_RETRY 3 /* Client validation retrys */ +#define UNIARP_SERVER_AGE 17 /* Server validation timeout */ +#define UNIARP_SERVER_RETRY 3 /* Server validation retrys */ +#define UNIARP_RETRY_AGE 1 /* Retry timeout */ +#define UNIARP_REVALID_AGE 2 /* Revalidation timeout */ +#define UNIARP_MIN_REFRESH 10 /* Minimum entry refresh time */ + + +/* + * Structure for ATMARP mappings. Each of these structures will contain + * IP address to ATM hardware address mappings. There will be one such + * structure for each IP address and for each unresolved ATM address + * currently in use. + */ +struct uniarp { + struct arpmap ua_arpmap; /* Common entry header */ + struct uniip *ua_intf; /* Interface where we learned answer */ + struct uniarp *ua_next; /* Hash chain link */ + u_char ua_flags; /* Flags (see below) */ + u_char ua_origin; /* Source of mapping (see below) */ + u_char ua_retry; /* Retry counter */ + u_char ua_aging; /* Aging timeout value (minutes) */ + struct ipvcc *ua_ivp; /* Head of IP VCC chain */ + struct atm_time ua_time; /* Timer controls */ +}; +#define ua_dstip ua_arpmap.am_dstip +#define ua_dstatm ua_arpmap.am_dstatm +#define ua_dstatmsub ua_arpmap.am_dstatmsub +#endif /* ATM_KERNEL */ + +/* + * UNIARP Entry Flags + */ +#define UAF_VALID ARPF_VALID /* Entry is valid */ +#define UAF_REFRESH ARPF_REFRESH /* Entry has been refreshed */ +#define UAF_LOCKED 0x04 /* Entry is locked */ +#define UAF_USED 0x08 /* Entry has been used recently */ + +/* + * UNIARP Entry Origin + * + * The origin values are ranked according to the source precedence. + * Larger values are more preferred. + */ +#define UAO_LOCAL 100 /* Local address */ +#define UAO_PERM ARP_ORIG_PERM /* Permanently installed */ +#define UAO_REGISTER 40 /* Learned via client registration */ +#define UAO_SCSP 30 /* Learned via SCSP */ +#define UAO_LOOKUP 20 /* Learned via server lookup */ +#define UAO_PEER_RSP 15 /* Learned from peer - inarp rsp */ +#define UAO_PEER_REQ 10 /* Learned from peer - inarp req */ + +/* + * ATMARP/InATMARP Packet Format + */ +struct atmarp_hdr { + u_short ah_hrd; /* Hardware type (see below) */ + u_short ah_pro; /* Protocol type */ + u_char ah_shtl; /* Type/len of source ATM address */ + u_char ah_sstl; /* Type/len of source ATM subaddress */ + u_short ah_op; /* Operation code (see below) */ + u_char ah_spln; /* Length of source protocol address */ + u_char ah_thtl; /* Type/len of target ATM address */ + u_char ah_tstl; /* Type/len of target ATM subaddress */ + u_char ah_tpln; /* Length of target protocol address */ +#ifdef notdef + /* Variable size fields */ + u_char ah_sha[]; /* Source ATM address */ + u_char ah_ssa[]; /* Source ATM subaddress */ + u_char ah_spa[]; /* Source protocol address */ + u_char ah_tha[]; /* Target ATM subaddress */ + u_char ah_tsa[]; /* Target ATM address */ + u_char ah_tpa[]; /* Target protocol subaddress */ +#endif +}; + +/* + * Hardware types + */ +#define ARP_ATMFORUM 19 + +/* + * Operation types + */ +#define ARP_REQUEST 1 /* ATMARP request */ +#define ARP_REPLY 2 /* ATMARP response */ +#define INARP_REQUEST 8 /* InATMARP request */ +#define INARP_REPLY 9 /* InATMARP response */ +#define ARP_NAK 10 /* ATMARP negative ack */ + +/* + * Type/length fields + */ +#define ARP_TL_TMASK 0x40 /* Type mask */ +#define ARP_TL_NSAPA 0x00 /* Type = ATM Forum NSAPA */ +#define ARP_TL_E164 0x40 /* Type = E.164 */ +#define ARP_TL_LMASK 0x3f /* Length mask */ + + +#ifdef ATM_KERNEL +/* + * Timer macros + */ +#define UNIIP_ARP_TIMER(s, t) atm_timeout(&(s)->uip_arptime, (t), uniarp_iftimeout) +#define UNIIP_ARP_CANCEL(s) atm_untimeout(&(s)->uip_arptime) +#define UNIARP_TIMER(s, t) atm_timeout(&(s)->ua_time, (t), uniarp_timeout) +#define UNIARP_CANCEL(s) atm_untimeout(&(s)->ua_time) + + +/* + * Macros for manipulating UNIARP tables and entries + */ +#define UNIARP_HASH(ip) ((u_long)(ip) % UNIARP_HASHSIZ) + +#define UNIARP_ADD(ua) \ +{ \ + struct uniarp **h; \ + h = &uniarp_arptab[UNIARP_HASH((ua)->ua_dstip.s_addr)]; \ + LINK2TAIL((ua), struct uniarp, *h, ua_next); \ +} + +#define UNIARP_DELETE(ua) \ +{ \ + struct uniarp **h; \ + h = &uniarp_arptab[UNIARP_HASH((ua)->ua_dstip.s_addr)]; \ + UNLINK((ua), struct uniarp, *h, ua_next); \ +} + +#define UNIARP_LOOKUP(ip, ua) \ +{ \ + for ((ua) = uniarp_arptab[UNIARP_HASH(ip)]; \ + (ua); (ua) = (ua)->ua_next) { \ + if ((ua)->ua_dstip.s_addr == (ip)) \ + break; \ + } \ +} + + +/* + * Global UNIARP Statistics + */ +struct uniarp_stat { + u_long uas_rcvdrop; /* Input packets dropped */ +}; + + +/* + * External variables + */ +extern struct uniip *uniip_head; +extern struct ip_serv uniip_ipserv; +extern struct uniarp *uniarp_arptab[]; +extern struct uniarp *uniarp_nomaptab; +extern struct uniarp *uniarp_pvctab; +extern struct sp_info uniarp_pool; +extern struct atm_time uniarp_timer; +extern int uniarp_print; +extern Atm_endpoint uniarp_endpt; +extern struct uniarp_stat uniarp_stat; + + +/* + * Global function declarations + */ + /* uniarp.c */ +int uniarp_start __P((void)); +void uniarp_stop __P((void)); +void uniarp_ipact __P((struct uniip *)); +void uniarp_ipdact __P((struct uniip *)); +void uniarp_ifaddr __P((struct siginst *)); +void uniarp_iftimeout __P((struct atm_time *)); +int uniarp_ioctl __P((int, caddr_t, caddr_t)); +caddr_t uniarp_getname __P((void *)); + + /* uniarp_cache.c */ +int uniarp_cache_svc __P((struct uniip *, struct in_addr *, + Atm_addr *, Atm_addr *, u_int)); +void uniarp_cache_pvc __P((struct ipvcc *, struct in_addr *, + Atm_addr *, Atm_addr *)); +int uniarp_validate_ip __P((struct uniip *, struct in_addr *, + u_int)); + + /* uniarp_input.c */ +void uniarp_cpcs_data __P((void *, KBuffer *)); +void uniarp_pdu_print __P((struct ipvcc *, KBuffer *, char *)); + + /* uniarp_output.c */ +int uniarp_arp_req __P((struct uniip *, struct in_addr *)); +int uniarp_arp_rsp __P((struct uniip *, struct arpmap *, + struct in_addr *, Atm_addr *, + Atm_addr *, struct ipvcc *)); +int uniarp_arp_nak __P((struct uniip *, KBuffer *, struct ipvcc *)); +int uniarp_inarp_req __P((struct uniip *, Atm_addr *, + Atm_addr *, struct ipvcc *)); +int uniarp_inarp_rsp __P((struct uniip *, struct in_addr *, + Atm_addr *, Atm_addr *, struct ipvcc *)); + + /* uniarp_timer.c */ +void uniarp_timeout __P((struct atm_time *)); +void uniarp_aging __P((struct atm_time *)); + + /* uniarp_vcm.c */ +int uniarp_pvcopen __P((struct ipvcc *)); +int uniarp_svcout __P((struct ipvcc *, struct in_addr *)); +int uniarp_svcin __P((struct ipvcc *, Atm_addr *, Atm_addr *)); +int uniarp_svcactive __P((struct ipvcc *)); +void uniarp_vcclose __P((struct ipvcc *)); +void uniarp_connected __P((void *)); +void uniarp_cleared __P((void *, struct t_atm_cause *)); + + /* uniip.c */ +int uniip_start __P((void)); +int uniip_stop __P((void)); + + +#endif /* ATM_KERNEL */ + +#endif /* _UNI_UNIIP_VAR_H */ diff --git a/sys/netatm/uni/unisig.h b/sys/netatm/uni/unisig.h new file mode 100644 index 0000000..b69714d --- /dev/null +++ b/sys/netatm/uni/unisig.h @@ -0,0 +1,49 @@ +/* + * + * =================================== + * 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: unisig.h,v 1.2 1997/05/06 22:21:33 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Protocol control blocks + * + */ + +#ifndef _UNISIG_H +#define _UNISIG_H + +/* + * ATM Forum UNI 3.0/3.1 Signalling + */ +#define UNISIG_SIG_VPI 0 /* Signalling VPI */ +#define UNISIG_SIG_VCI 5 /* Signalling VCI */ + +#define STACK_SSCF "uni_sscf" + +#endif /* _UNISIG_H */ diff --git a/sys/netatm/uni/unisig_decode.c b/sys/netatm/uni/unisig_decode.c new file mode 100644 index 0000000..98f5dfd --- /dev/null +++ b/sys/netatm/uni/unisig_decode.c @@ -0,0 +1,2474 @@ +/* + * + * =================================== + * 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: unisig_decode.c,v 1.13 1998/08/26 23:29:20 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message formatting module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_decode.c,v 1.13 1998/08/26 23:29:20 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_mbuf.h> +#include <netatm/uni/unisig_decode.h> + +#define ALLOC_IE(ie) \ + (ie) = (struct ie_generic *) atm_allocate(&unisig_iepool); \ + if (!ie) \ + return(ENOMEM); + + +/* + * Local functions + */ +static int usf_dec_ie __P((struct usfmt *, struct unisig_msg *, struct ie_generic *)); +static int usf_dec_ie_hdr __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_aalp __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_clrt __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_bbcp __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_bhli __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_blli __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_clst __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_cdad __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_cdsa __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_cgad __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_cgsa __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_caus __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_cnid __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_qosp __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_brpi __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_rsti __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_bsdc __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_trnt __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_uimp __P((struct usfmt *, struct ie_generic *)); +static int usf_dec_ie_ident __P((struct usfmt *, struct ie_generic *, + struct ie_decode_tbl *)); +static int usf_dec_atm_addr __P((struct usfmt *, Atm_addr *, int)); + + +/* + * Table associating IE type with IE vector index + */ +u_char unisig_ie_ident_vec[] = { + UNI_IE_AALP, + UNI_IE_CLRT, + UNI_IE_BBCP, + UNI_IE_BHLI, + UNI_IE_BLLI, + UNI_IE_CLST, + UNI_IE_CDAD, + UNI_IE_CDSA, + UNI_IE_CGAD, + UNI_IE_CGSA, + UNI_IE_CAUS, + UNI_IE_CNID, + UNI_IE_QOSP, + UNI_IE_BRPI, + UNI_IE_RSTI, + UNI_IE_BLSH, + UNI_IE_BNSH, + UNI_IE_BSDC, + UNI_IE_TRNT, + UNI_IE_EPRF, + UNI_IE_EPST +}; + + +/* + * Tables specifying which IEs are mandatory, optional, and + * not allowed for each Q.2931 message type + */ +static u_char uni_calp_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_OPT, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_OPT, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_conn_ie_tbl[] = { + IE_OPT, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_OPT, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_OPT, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_OPT, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_cack_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_NA, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_setu_ie_tbl[] = { + IE_MAND, /* ATM AAL Parameters (not required by + UNI 3.0) */ + IE_MAND, /* ATM User Cell Rate */ + IE_MAND, /* Broadband Bearer Capability */ + IE_OPT, /* Broadband High Layer Information */ + IE_MAND, /* Broadband Low Layer Information (not required by UNI 3.0 */ + IE_NA, /* Call State */ + IE_MAND, /* Called Party Number */ + IE_OPT, /* Called Party Subaddress */ + IE_OPT, /* Calling Party Number */ + IE_OPT, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_MAND, /* Connection Identifier */ + IE_MAND, /* Quality of Service Parameters */ + IE_OPT, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_OPT, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_OPT, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_rlse_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_MAND, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_NA, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_rlsc_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_MAND, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_NA, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_rstr_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_OPT, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_MAND, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_NA, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_rsta_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_OPT, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_MAND, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_NA, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_stat_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_MAND, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_MAND, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_OPT, /* Endpoint Reference */ + IE_OPT /* Endpoint State */ +}; + +static u_char uni_senq_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_OPT, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_addp_ie_tbl[] = { + IE_OPT, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_OPT, /* Broadband High Layer Information */ + IE_OPT, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_MAND, /* Called Party Number */ + IE_OPT, /* Called Party Subaddress */ + IE_OPT, /* Calling Party Number */ + IE_OPT, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_OPT, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_MAND, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_adpa_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_NA, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_MAND, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_adpr_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_MAND, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_MAND, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_drpp_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_MAND, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_MAND, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +static u_char uni_drpa_ie_tbl[] = { + IE_NA, /* ATM AAL Parameters */ + IE_NA, /* ATM User Cell Rate */ + IE_NA, /* Broadband Bearer Capability */ + IE_NA, /* Broadband High Layer Information */ + IE_NA, /* Broadband Low Layer Information */ + IE_NA, /* Call State */ + IE_NA, /* Called Party Number */ + IE_NA, /* Called Party Subaddress */ + IE_NA, /* Calling Party Number */ + IE_NA, /* Calling Party Subaddress */ + IE_OPT, /* Cause */ + IE_NA, /* Connection Identifier */ + IE_NA, /* Quality of Service Parameters */ + IE_NA, /* Broadband Repeat Indicator */ + IE_NA, /* Restart Indicator */ + IE_NA, /* Broadband Locking Shift */ + IE_NA, /* Broadband Non-locking Shift */ + IE_NA, /* Broadband Sending Complete */ + IE_NA, /* Transit Net */ + IE_MAND, /* Endpoint Reference */ + IE_NA /* Endpoint State */ +}; + +/* + * Table of Q.2931 message types + */ +static struct { + u_char msg_type; + u_char *msg_ie_tbl; +} uni_msg_types[] = { + { UNI_MSG_CALP, uni_calp_ie_tbl }, + { UNI_MSG_CONN, uni_conn_ie_tbl }, + { UNI_MSG_CACK, uni_cack_ie_tbl }, + { UNI_MSG_SETU, uni_setu_ie_tbl }, + { UNI_MSG_RLSE, uni_rlse_ie_tbl }, + { UNI_MSG_RLSC, uni_rlsc_ie_tbl }, + { UNI_MSG_RSTR, uni_rstr_ie_tbl }, + { UNI_MSG_RSTA, uni_rsta_ie_tbl }, + { UNI_MSG_STAT, uni_stat_ie_tbl }, + { UNI_MSG_SENQ, uni_senq_ie_tbl }, + { UNI_MSG_ADDP, uni_addp_ie_tbl }, + { UNI_MSG_ADPA, uni_adpa_ie_tbl }, + { UNI_MSG_ADPR, uni_adpr_ie_tbl }, + { UNI_MSG_DRPP, uni_drpp_ie_tbl }, + { UNI_MSG_DRPA, uni_drpa_ie_tbl }, +}; + + +/* + * Table of information elements + */ +static struct ie_ent ie_table[] = { + { UNI_IE_AALP, 5, 16, UNI_MSG_IE_AALP, usf_dec_ie_aalp }, + { UNI_IE_CLRT, 0, 26, UNI_MSG_IE_CLRT, usf_dec_ie_clrt }, + { UNI_IE_BBCP, 2, 3, UNI_MSG_IE_BBCP, usf_dec_ie_bbcp }, + { UNI_IE_BHLI, 1, 9, UNI_MSG_IE_BHLI, usf_dec_ie_bhli }, + { UNI_IE_BLLI, 0, 13, UNI_MSG_IE_BLLI, usf_dec_ie_blli }, + { UNI_IE_CLST, 1, 1, UNI_MSG_IE_CLST, usf_dec_ie_clst }, + { UNI_IE_CDAD, 1, 21, UNI_MSG_IE_CDAD, usf_dec_ie_cdad }, + { UNI_IE_CDSA, 1, 21, UNI_MSG_IE_CDSA, usf_dec_ie_cdsa }, + { UNI_IE_CGAD, 1, 22, UNI_MSG_IE_CGAD, usf_dec_ie_cgad }, + { UNI_IE_CGSA, 1, 21, UNI_MSG_IE_CGSA, usf_dec_ie_cgsa }, + { UNI_IE_CAUS, 2, 30, UNI_MSG_IE_CAUS, usf_dec_ie_caus }, + { UNI_IE_CNID, 5, 5, UNI_MSG_IE_CNID, usf_dec_ie_cnid }, + { UNI_IE_QOSP, 2, 2, UNI_MSG_IE_QOSP, usf_dec_ie_qosp }, + { UNI_IE_BRPI, 1, 1, UNI_MSG_IE_BRPI, usf_dec_ie_brpi }, + { UNI_IE_RSTI, 1, 1, UNI_MSG_IE_RSTI, usf_dec_ie_rsti }, + { UNI_IE_BLSH, 1, 1, UNI_MSG_IE_ERR, usf_dec_ie_uimp }, + { UNI_IE_BNSH, 1, 1, UNI_MSG_IE_ERR, usf_dec_ie_uimp }, + { UNI_IE_BSDC, 1, 1, UNI_MSG_IE_BSDC, usf_dec_ie_bsdc }, + { UNI_IE_TRNT, 1, 5, UNI_MSG_IE_TRNT, usf_dec_ie_trnt }, + { UNI_IE_EPRF, 3, 3, UNI_MSG_IE_ERR, usf_dec_ie_uimp }, + { UNI_IE_EPST, 1, 1, UNI_MSG_IE_ERR, usf_dec_ie_uimp }, + { 0, 0, 0, 0, 0 } +}; + +/* + * Decoding table for AAL 1 + */ +struct ie_decode_tbl ie_aal1_tbl[] = { + { 133, 1, IE_OFF_SIZE(ie_aalp_1_subtype) }, + { 134, 1, IE_OFF_SIZE(ie_aalp_1_cbr_rate) }, + { 135, 2, IE_OFF_SIZE(ie_aalp_1_multiplier) }, + { 136, 1, IE_OFF_SIZE(ie_aalp_1_clock_recovery) }, + { 137, 1, IE_OFF_SIZE(ie_aalp_1_error_correction) }, + { 138, 1, IE_OFF_SIZE(ie_aalp_1_struct_data_tran) }, + { 139, 1, IE_OFF_SIZE(ie_aalp_1_partial_cells) }, + { 0, 0, 0, 0 } +}; + +/* + * Decoding table for AAL 3/4 + */ +struct ie_decode_tbl ie_aal4_tbl_30[] = { + { 140, 2, IE_OFF_SIZE(ie_aalp_4_fwd_max_sdu) }, + { 129, 2, IE_OFF_SIZE(ie_aalp_4_bkwd_max_sdu) }, + { 130, 2, IE_OFF_SIZE(ie_aalp_4_mid_range) }, + { 131, 1, IE_OFF_SIZE(ie_aalp_4_mode) }, + { 132, 1, IE_OFF_SIZE(ie_aalp_4_sscs_type) }, + { 0, 0, 0, 0 } +}; +struct ie_decode_tbl ie_aal4_tbl_31[] = { + { 140, 2, IE_OFF_SIZE(ie_aalp_4_fwd_max_sdu) }, + { 129, 2, IE_OFF_SIZE(ie_aalp_4_bkwd_max_sdu) }, + { 130, 4, IE_OFF_SIZE(ie_aalp_4_mid_range) }, + { 132, 1, IE_OFF_SIZE(ie_aalp_4_sscs_type) }, + { 0, 0, 0, 0 } +}; + +/* + * Decoding table for AAL 5 + */ +struct ie_decode_tbl ie_aal5_tbl_30[] = { + { 140, 2, IE_OFF_SIZE(ie_aalp_5_fwd_max_sdu) }, + { 129, 2, IE_OFF_SIZE(ie_aalp_5_bkwd_max_sdu) }, + { 131, 1, IE_OFF_SIZE(ie_aalp_5_mode) }, + { 132, 1, IE_OFF_SIZE(ie_aalp_5_sscs_type) }, + { 0, 0, 0, 0 } +}; +struct ie_decode_tbl ie_aal5_tbl_31[] = { + { 140, 2, IE_OFF_SIZE(ie_aalp_5_fwd_max_sdu) }, + { 129, 2, IE_OFF_SIZE(ie_aalp_5_bkwd_max_sdu) }, + { 132, 1, IE_OFF_SIZE(ie_aalp_5_sscs_type) }, + { 0, 0, 0, 0 } +}; + +/* + * Decoding table for ATM user cell rate + */ +struct ie_decode_tbl ie_clrt_tbl[] = { + {UNI_IE_CLRT_FWD_PEAK_ID, 3, IE_OFF_SIZE(ie_clrt_fwd_peak)}, + {UNI_IE_CLRT_BKWD_PEAK_ID, 3, IE_OFF_SIZE(ie_clrt_bkwd_peak)}, + {UNI_IE_CLRT_FWD_PEAK_01_ID, 3, IE_OFF_SIZE(ie_clrt_fwd_peak_01)}, + {UNI_IE_CLRT_BKWD_PEAK_01_ID, 3, IE_OFF_SIZE(ie_clrt_bkwd_peak_01)}, + {UNI_IE_CLRT_FWD_SUST_ID, 3, IE_OFF_SIZE(ie_clrt_fwd_sust)}, + {UNI_IE_CLRT_BKWD_SUST_ID, 3, IE_OFF_SIZE(ie_clrt_bkwd_sust)}, + {UNI_IE_CLRT_FWD_SUST_01_ID, 3, IE_OFF_SIZE(ie_clrt_fwd_sust_01)}, + {UNI_IE_CLRT_BKWD_SUST_01_ID, 3, IE_OFF_SIZE(ie_clrt_bkwd_sust_01)}, + {UNI_IE_CLRT_FWD_BURST_ID, 3, IE_OFF_SIZE(ie_clrt_fwd_burst)}, + {UNI_IE_CLRT_BKWD_BURST_ID, 3, IE_OFF_SIZE(ie_clrt_bkwd_burst)}, + {UNI_IE_CLRT_FWD_BURST_01_ID, 3, IE_OFF_SIZE(ie_clrt_fwd_burst_01)}, + {UNI_IE_CLRT_BKWD_BURST_01_ID, 3, IE_OFF_SIZE(ie_clrt_bkwd_burst_01)}, + {UNI_IE_CLRT_BEST_EFFORT_ID, 0, IE_OFF_SIZE(ie_clrt_best_effort)}, + {UNI_IE_CLRT_TM_OPTIONS_ID, 1, IE_OFF_SIZE(ie_clrt_tm_options)}, + {0, 0, 0, 0 } +}; + +/* + * IEs initialized to empty values + */ +struct ie_aalp ie_aalp_absent = { + T_ATM_ABSENT +}; + +struct ie_clrt ie_clrt_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT +}; + +struct ie_bbcp ie_bbcp_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT +}; + +struct ie_bhli ie_bhli_absent = { + T_ATM_ABSENT, + { 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +struct ie_blli ie_blli_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + { 0, 0, 0 }, + { 0, 0 } +}; + +struct ie_clst ie_clst_absent = { + T_ATM_ABSENT +}; + +struct ie_cdad ie_cdad_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + { T_ATM_ABSENT, 0 } +}; + +struct ie_cdsa ie_cdsa_absent = { + { T_ATM_ABSENT, 0 } +}; + +struct ie_cgad ie_cgad_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + { T_ATM_ABSENT, 0 } +}; + +struct ie_cgsa ie_cgsa_absent = { + { T_ATM_ABSENT, 0 } +}; + +struct ie_caus ie_caus_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + 0 +}; + +struct ie_cnid ie_cnid_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT, + T_ATM_ABSENT +}; + +struct ie_qosp ie_qosp_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT +}; + +struct ie_brpi ie_brpi_absent = { + T_ATM_ABSENT +}; + +struct ie_rsti ie_rsti_absent = { + T_ATM_ABSENT +}; + +struct ie_blsh ie_blsh_absent = { + T_ATM_ABSENT +}; + +struct ie_bnsh ie_bnsh_absent = { + T_ATM_ABSENT +}; + +struct ie_bsdc ie_bsdc_absent = { + T_ATM_ABSENT +}; + +struct ie_trnt ie_trnt_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT, + 0 +}; + +struct ie_eprf ie_eprf_absent = { + T_ATM_ABSENT, + T_ATM_ABSENT +}; + +struct ie_epst ie_epst_absent = { + T_ATM_ABSENT +}; + + +/* + * Decode a UNI signalling message + * + * Arguments: + * usf pointer to a unisig formatting structure + * msg pointer to a signalling message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_dec_msg(usf, msg) + struct usfmt *usf; + struct unisig_msg *msg; +{ + int i, len, rc; + short s; + u_char c, *ie_tbl; + struct ie_generic *ie; + + ATM_DEBUG2("usf_dec_msg: usf=0x%x, msg=0x%x\n", (int) usf, + (int) msg); + + /* + * Check the total message length + */ + if (usf_count(usf) < UNI_MSG_MIN_LEN) { + return(EIO); + } + + /* + * Get and check the protocol discriminator + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + if (c != UNI_MSG_DISC_Q93B) + return(EIO); + + /* + * Get and check the call reference length + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + if (c != 3) + return(EIO); + + /* + * Get the call reference + */ + rc = usf_int3(usf, &msg->msg_call_ref); + if (rc) + return(rc); + + /* + * Get the message type + */ + rc = usf_byte(usf, &msg->msg_type); + if (rc) + return(rc); + + /* + * Get the message type extension + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + msg->msg_type_flag = (c >> UNI_MSG_TYPE_FLAG_SHIFT) & + UNI_MSG_TYPE_FLAG_MASK; + msg->msg_type_action = c & UNI_MSG_TYPE_ACT_MASK; + + /* + * Get the message length and make sure we actually have + * enough data for the whole message + */ + rc = usf_short(usf, &s); + if (rc) + return(rc); + msg->msg_length = s; + if (usf_count(usf) != msg->msg_length) { + return(EMSGSIZE); + } + + /* + * Process information elements + */ + len = msg->msg_length; + while (len) { + ALLOC_IE(ie); + rc = usf_dec_ie(usf, msg, ie); + if (rc) { + atm_free(ie); + return(rc); + } + len -= (ie->ie_length + UNI_IE_HDR_LEN); + } + + /* + * Make sure that mandatory IEs are included and + * unwanted ones aren't + */ + for (i=0; msg->msg_type!=uni_msg_types[i].msg_type && + uni_msg_types[i].msg_type!=0; i++) { + } + if (!uni_msg_types[i].msg_ie_tbl) + goto done; + + /* + * If the message type is in the table, check the IEs. + * If it isn't, the receive routine will catch the error. + */ + ie_tbl = uni_msg_types[i].msg_ie_tbl; + for (i=0; i<UNI_MSG_IE_CNT-1; i++) { + switch(ie_tbl[i]) { + case IE_MAND: + if (!msg->msg_ie_vec[i]) { + /* + * Mandatory IE missing + */ + ALLOC_IE(ie); + ie->ie_ident = unisig_ie_ident_vec[i]; + ie->ie_err_cause = UNI_IE_CAUS_MISSING; + MSG_IE_ADD(msg, ie, UNI_MSG_IE_ERR); + } + break; + case IE_NA: + if (msg->msg_ie_vec[i]) { + /* + * Disallowed IE present + */ + ie = msg->msg_ie_vec[i]; + msg->msg_ie_vec[i] = + (struct ie_generic *) 0; + MSG_IE_ADD(msg, ie, UNI_MSG_IE_ERR); + while (ie) { + ie->ie_err_cause = + UNI_IE_CAUS_IEEXIST; + ie = ie->ie_next; + } + } + break; + case IE_OPT: + break; + } + } + +done: + return(0); +} + + +/* + * Decode an information element + * + * This routine will be called repeatedly as long as there are + * information elements left to be decoded. It will decode the + * first part of the IE, look its type up in a table, and call + * the appropriate routine to decode the rest. After an IE is + * successfully decoded, it is linked into the UNI signalling + * message structure. If an error is discovered, the IE is linked + * into the IE error chain and an error cause is set in the header. + * + * Arguments: + * usf pointer to a UNISIG formatting structure + * msg pointer to a UNISIG message structure + * ie pointer to a generic IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie(usf, msg, ie) + struct usfmt *usf; + struct unisig_msg *msg; + struct ie_generic *ie; +{ + int i, ie_index, rc; + + /* + * Decode the IE header (identifier, instruction field, + * and length) + */ + rc = usf_dec_ie_hdr(usf, ie); + if (rc) + return(rc); + /* + * Ignore the IE if it is of zero length. + */ + if (!ie->ie_length) { + atm_free(ie); + return(0); + } + + /* + * Look up the information element in the table + */ + for (i=0; (ie->ie_ident != ie_table[i].ident) && + (ie_table[i].decode != NULL); i++) { + } + if (ie_table[i].decode == NULL) { + /* + * Unrecognized IE + */ + ie_index = UNI_MSG_IE_ERR; + } else { + ie_index = ie_table[i].p_idx; + } + + /* + * Check for unimplemented or unrecognized IEs + */ + if (ie_index == UNI_MSG_IE_ERR) { + ie->ie_err_cause = UNI_IE_CAUS_IEEXIST; + + /* + * Skip over the invalid IE + */ + rc = usf_dec_ie_uimp(usf, ie); + if (rc) + return(rc); + goto done; + } + + /* + * Check the length against the IE table + */ + if (ie->ie_length < ie_table[i].min_len || + ie->ie_length > ie_table[i].max_len) { + ie_index = UNI_MSG_IE_ERR; + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + + /* + * Skip over the invalid IE + */ + rc = usf_dec_ie_uimp(usf, ie); + if (rc) + return(rc); + goto done; + } + + /* + * Process the IE by calling the function indicated + * in the IE table + */ + rc = ie_table[i].decode(usf, ie); + if (rc) + return(rc); + + /* + * Link the IE into the signalling message + */ +done: + if (ie->ie_err_cause) { + ie_index = UNI_MSG_IE_ERR; + } + MSG_IE_ADD(msg, ie, ie_index); + + return(0); +} + + +/* + * Decode an information element header + * + * Arguments: + * usf pointer to a UNISIG formatting structure + * ie pointer to a generic IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_hdr(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c; + short s; + int rc; + + /* + * Get the IE identifier + */ + rc = usf_byte(usf, &ie->ie_ident); + if (rc) + return(rc); + + /* + * Get the extended type + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_coding = (c >> UNI_IE_CODE_SHIFT) & UNI_IE_CODE_MASK; + ie->ie_flag = (c >> UNI_IE_FLAG_SHIFT) & UNI_IE_FLAG_MASK; + ie->ie_action = c & UNI_IE_ACT_MASK; + + /* + * Get the length. + */ + rc = usf_short(usf, &s); + if (rc) + return(rc); + ie->ie_length = s; + + return(0); +} + + +/* + * Decode an AAL parameters information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to an AAL parms IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_aalp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc = 0; + + /* + * Clear the IE + */ + KM_COPY(&ie_aalp_absent, &ie->ie_u.ie_aalp, + sizeof(ie_aalp_absent)); + + /* + * Get the AAL type + */ + rc = usf_byte(usf, &ie->ie_aalp_aal_type); + if (rc) + return(rc); + + /* + * Subtract the length of the AAL type from the total. + * It will be readjusted after usf_dec_ie_ident is finished. + */ + ie->ie_length--; + + /* + * Process based on AAL type + */ + switch (ie->ie_aalp_aal_type) { + case UNI_IE_AALP_AT_AAL1: + /* + * Clear the AAL 1 subparameters + */ + ie->ie_aalp_1_subtype = T_ATM_ABSENT; + ie->ie_aalp_1_cbr_rate = T_ATM_ABSENT; + ie->ie_aalp_1_multiplier = T_ATM_ABSENT; + ie->ie_aalp_1_clock_recovery = T_ATM_ABSENT; + ie->ie_aalp_1_error_correction = T_ATM_ABSENT; + ie->ie_aalp_1_struct_data_tran = T_ATM_ABSENT; + ie->ie_aalp_1_partial_cells = T_ATM_ABSENT; + + /* + * Parse the AAL fields based on their IDs + */ + rc = usf_dec_ie_ident(usf, ie, ie_aal1_tbl); + break; + case UNI_IE_AALP_AT_AAL3: + /* + * Clear the AAL 3/4 subparameters + */ + ie->ie_aalp_4_fwd_max_sdu = T_ATM_ABSENT; + ie->ie_aalp_4_bkwd_max_sdu = T_ATM_ABSENT; + ie->ie_aalp_4_mid_range = T_ATM_ABSENT; + ie->ie_aalp_4_mode = T_ATM_ABSENT; + ie->ie_aalp_4_sscs_type = T_ATM_ABSENT; + + /* + * Parse the AAL fields based on their IDs + */ + if (usf->usf_sig->us_proto == ATM_SIG_UNI30) + rc = usf_dec_ie_ident(usf, ie, ie_aal4_tbl_30); + else + rc = usf_dec_ie_ident(usf, ie, ie_aal4_tbl_31); + + /* + * If either forward or backward maximum SDU + * size is specified, the other must also be + * specified. + */ + if ((ie->ie_aalp_4_fwd_max_sdu != T_ATM_ABSENT && + ie->ie_aalp_4_bkwd_max_sdu == T_ATM_ABSENT) || + (ie->ie_aalp_4_fwd_max_sdu == T_ATM_ABSENT && + ie->ie_aalp_4_bkwd_max_sdu != T_ATM_ABSENT)) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + } + break; + case UNI_IE_AALP_AT_AAL5: + /* + * Clear the AAL 5 subparameters + */ + ie->ie_aalp_5_fwd_max_sdu = T_ATM_ABSENT; + ie->ie_aalp_5_bkwd_max_sdu = T_ATM_ABSENT; + ie->ie_aalp_5_mode = T_ATM_ABSENT; + ie->ie_aalp_5_sscs_type = T_ATM_ABSENT; + + /* + * Parse the AAL fields based on their IDs + */ + if (usf->usf_sig->us_proto == ATM_SIG_UNI30) + rc = usf_dec_ie_ident(usf, ie, ie_aal5_tbl_30); + else + rc = usf_dec_ie_ident(usf, ie, ie_aal5_tbl_31); + + /* + * If either forward or backward maximum SDU + * size is specified, the other must also be + * specified. + */ + if ((ie->ie_aalp_5_fwd_max_sdu != T_ATM_ABSENT && + ie->ie_aalp_5_bkwd_max_sdu == T_ATM_ABSENT) || + (ie->ie_aalp_5_fwd_max_sdu == T_ATM_ABSENT && + ie->ie_aalp_5_bkwd_max_sdu != T_ATM_ABSENT)) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + } + break; + case UNI_IE_AALP_AT_AALU: + /* + * Check user parameter length + */ + if (ie->ie_length > + sizeof(ie->ie_aalp_user_info) + + 1) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + } + + /* + * Get the user data + */ + i = 0; + while (i < ie->ie_length - 2) { + rc = usf_byte(usf, &ie->ie_aalp_user_info[i]); + if (rc) + break; + i++; + } + break; + default: + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + } + ie->ie_length++; + + return(rc); +} + + +/* + * Decode a user cell rate information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_clrt(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + + /* + * Clear the IE + */ + KM_COPY(&ie_clrt_absent, &ie->ie_u.ie_clrt, + sizeof(ie_clrt_absent)); + + /* + * Parse the IE using field identifiers + */ + rc = usf_dec_ie_ident(usf, ie, ie_clrt_tbl); + return(rc); +} + + +/* + * Decode a broadband bearer capability information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_bbcp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_bbcp_absent, &ie->ie_u.ie_bbcp, + sizeof(ie_bbcp_absent)); + + /* + * Get the broadband bearer class + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_bbcp_bearer_class = c & UNI_IE_BBCP_BC_MASK; + + /* + * If the broadband bearer class was X, the next + * byte has the traffic type and timing requirements + */ + if (ie->ie_bbcp_bearer_class == UNI_IE_BBCP_BC_BCOB_X && + !(c & UNI_IE_EXT_BIT)) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_bbcp_traffic_type = (c >> UNI_IE_BBCP_TT_SHIFT) & + UNI_IE_BBCP_TT_MASK; + ie->ie_bbcp_timing_req = c & UNI_IE_BBCP_TR_MASK; + } + + /* + * Get the clipping and user plane connection configuration + */ + if (c & UNI_IE_EXT_BIT) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_bbcp_clipping = (c >> UNI_IE_BBCP_SC_SHIFT) & + UNI_IE_BBCP_SC_MASK; + ie->ie_bbcp_conn_config = c & UNI_IE_BBCP_CC_MASK; + } + + return(0); +} + + +/* + * Decode a broadband high layer information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_bhli(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_bhli_absent, &ie->ie_u.ie_bhli, + sizeof(ie_bhli_absent)); + + /* + * Get the high layer information type + */ + rc = usf_ext(usf, &i); + ie->ie_bhli_type = i & UNI_IE_EXT_MASK; + if (rc) + return(rc); + + /* + * What comes next depends on the type + */ + switch (ie->ie_bhli_type) { + case UNI_IE_BHLI_TYPE_ISO: + case UNI_IE_BHLI_TYPE_USER: + /* + * ISO or user-specified parameters -- take the + * length of information from the IE length + */ + for (i=0; i<ie->ie_length-1; i++) { + rc = usf_byte(usf, &ie->ie_bhli_info[i]); + if (rc) + return(rc); + } + break; + case UNI_IE_BHLI_TYPE_HLP: + /* + * Make sure the IE is long enough for the high + * layer profile information, then get it + */ + if (usf->usf_sig->us_proto != ATM_SIG_UNI30) + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + if (ie->ie_length < UNI_IE_BHLI_HLP_LEN+1) + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + for (i=0; i<ie->ie_length && + i<UNI_IE_BHLI_HLP_LEN; i++) { + rc = usf_byte(usf, &ie->ie_bhli_info[i]); + if (rc) + return(rc); + } + break; + case UNI_IE_BHLI_TYPE_VSA: + /* + * Make sure the IE is long enough for the vendor- + * specific application information, then get it + */ + if (ie->ie_length < UNI_IE_BHLI_VSA_LEN+1) + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + for (i=0; i<ie->ie_length && + i<UNI_IE_BHLI_VSA_LEN; i++) { + rc = usf_byte(usf, &ie->ie_bhli_info[i]); + if (rc) + return(rc); + } + break; + default: + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + for (i=0; i<ie->ie_length; i++) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + } + } + + return(0); +} + + +/* + * Decode a broadband low layer information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_blli(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c, id; + int bc, i, rc; + u_int ipi; + + /* + * Clear the IE + */ + KM_COPY(&ie_blli_absent, &ie->ie_u.ie_blli, + sizeof(ie_blli_absent)); + + /* + * Get paramteters for the protocol layers as long as + * there is still information left in the IE + */ + bc = ie->ie_length; + while (bc) { + /* + * Get the type and process based on what it is + */ + rc = usf_byte(usf, &id); + if (rc) + return(rc); + switch (((id & UNI_IE_EXT_MASK) >> + UNI_IE_BLLI_LID_SHIFT) & + UNI_IE_BLLI_LID_MASK) { + case UNI_IE_BLLI_L1_ID: + /* + * Layer 1 info + */ + ie->ie_blli_l1_id = id & UNI_IE_BLLI_LP_MASK; + bc--; + break; + case UNI_IE_BLLI_L2_ID: + /* + * Layer 2 info--contents vary based on type + */ + ie->ie_blli_l2_id = id & UNI_IE_BLLI_LP_MASK; + bc--; + if (id & UNI_IE_EXT_BIT) + break; + switch (ie->ie_blli_l2_id) { + case UNI_IE_BLLI_L2P_X25L: + case UNI_IE_BLLI_L2P_X25M: + case UNI_IE_BLLI_L2P_HDLC1: + case UNI_IE_BLLI_L2P_HDLC2: + case UNI_IE_BLLI_L2P_HDLC3: + case UNI_IE_BLLI_L2P_Q922: + case UNI_IE_BLLI_L2P_ISO7776: + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l2_mode = (c >> + UNI_IE_BLLI_L2MODE_SHIFT) & + UNI_IE_BLLI_L2MODE_MASK; + if (!(c & UNI_IE_EXT_BIT)) + break; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l2_window = + c & UNI_IE_EXT_MASK; + break; + case UNI_IE_BLLI_L2P_USER: + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l2_user_proto = + c & UNI_IE_EXT_MASK; + break; + } + break; + case UNI_IE_BLLI_L3_ID: + /* + * Layer 3 info--contents vary based on type + */ + ie->ie_blli_l3_id = id & UNI_IE_BLLI_LP_MASK; + bc--; + switch (ie->ie_blli_l3_id) { + case UNI_IE_BLLI_L3P_X25: + case UNI_IE_BLLI_L3P_ISO8208: + case UNI_IE_BLLI_L3P_ISO8878: + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l3_mode = (c >> + UNI_IE_BLLI_L3MODE_SHIFT) & + UNI_IE_BLLI_L3MODE_MASK; + if (!(c & UNI_IE_EXT_BIT)) + break; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l3_packet_size = + c & UNI_IE_BLLI_L3PS_MASK; + if (!(c & UNI_IE_EXT_BIT)) + break; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l3_window = + c & UNI_IE_EXT_MASK; + break; + case UNI_IE_BLLI_L3P_USER: + rc = usf_byte(usf, &c); + if (rc) + return(rc); + bc--; + ie->ie_blli_l3_mode = + c & UNI_IE_EXT_MASK; + break; + case UNI_IE_BLLI_L3P_ISO9577: + rc = usf_ext(usf, &ipi); + if (rc) + return(rc); + bc -= 2; + ie->ie_blli_l3_ipi = ipi >> + UNI_IE_BLLI_L3IPI_SHIFT; + if (ie->ie_blli_l3_ipi != + UNI_IE_BLLI_L3IPI_SNAP) + break; + + rc = usf_byte(usf, &c); + ie->ie_blli_l3_snap_id = c & UNI_IE_EXT_MASK; + if (rc) + return(rc); + bc --; + + rc = usf_byte(usf, + &ie->ie_blli_l3_oui[0]); + if (rc) + return(rc); + rc = usf_byte(usf, + &ie->ie_blli_l3_oui[1]); + if (rc) + return(rc); + rc = usf_byte(usf, + &ie->ie_blli_l3_oui[2]); + if (rc) + return(rc); + rc = usf_byte(usf, + &ie->ie_blli_l3_pid[0]); + if (rc) + return(rc); + rc = usf_byte(usf, + &ie->ie_blli_l3_pid[1]); + if (rc) + return(rc); + bc -= 5; + break; + } + break; + default: + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + for (i=0; i<ie->ie_length; i++) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + } + } + } + + return(0); +} + + +/* + * Decode a call state information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_clst(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_clst_absent, &ie->ie_u.ie_clst, + sizeof(ie_clst_absent)); + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_clst_state = c & UNI_IE_CLST_STATE_MASK; + + return(0); +} + + +/* + * Decode a called party number information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_cdad(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int len, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_cdad_absent, &ie->ie_u.ie_cdad, + sizeof(ie_cdad_absent)); + + /* + * Get and check the numbering plan + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_cdad_plan = c & UNI_IE_CDAD_PLAN_MASK; + len = ie->ie_length - 1; + switch (ie->ie_cdad_plan) { + case UNI_IE_CDAD_PLAN_E164: + ie->ie_cdad_addr.address_format = T_ATM_E164_ADDR; + break; + case UNI_IE_CDAD_PLAN_NSAP: + ie->ie_cdad_addr.address_format = T_ATM_ENDSYS_ADDR; + break; + default: + /* + * Invalid numbering plan + */ + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + while (len) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + } + + return(0); + } + + /* + * Get the ATM address + */ + rc = usf_dec_atm_addr(usf, &ie->ie_cdad_addr, len); + if (rc == EINVAL) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + rc = 0; + } + + return(rc); +} + + +/* + * Decode a called party subaddress information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_cdsa(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int len, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_cdsa_absent, &ie->ie_u.ie_cdsa, + sizeof(ie_cdsa_absent)); + + /* + * Get and check the subaddress type + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len = ie->ie_length - 1; + if (((c >> UNI_IE_CDSA_TYPE_SHIFT) & UNI_IE_CDSA_TYPE_MASK) != + UNI_IE_CDSA_TYPE_AESA) { + /* + * Invalid subaddress type + */ + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + while (len) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + } + + return(0); + } + + /* + * Get the ATM address + */ + ie->ie_cdsa_addr.address_format = T_ATM_ENDSYS_ADDR; + rc = usf_dec_atm_addr(usf, &ie->ie_cdsa_addr, len); + if (rc == EINVAL) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + rc = 0; + } + + return(rc); +} + + +/* + * Decode a calling party number information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_cgad(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int len, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_cgad_absent, &ie->ie_u.ie_cgad, + sizeof(ie_cgad_absent)); + + /* + * Get and check the numbering plan + */ + len = ie->ie_length; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_cgad_type = (c >> UNI_IE_CGAD_TYPE_SHIFT) & + UNI_IE_CGAD_TYPE_MASK; + ie->ie_cgad_plan = c & UNI_IE_CGAD_PLAN_MASK; + len--; + switch (ie->ie_cgad_plan) { + case UNI_IE_CGAD_PLAN_E164: + ie->ie_cgad_addr.address_format = T_ATM_E164_ADDR; + break; + case UNI_IE_CGAD_PLAN_NSAP: + ie->ie_cgad_addr.address_format = T_ATM_ENDSYS_ADDR; + break; + default: + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + while (len) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + } + + return(0); + } + + /* + * Get the presentation and screening indicators, if present + */ + if (!(c & UNI_IE_EXT_BIT)) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + ie->ie_cgad_pres_ind = (c >> UNI_IE_CGAD_PRES_SHIFT) & + UNI_IE_CGAD_PRES_MASK; + ie->ie_cgad_screen_ind = c & UNI_IE_CGAD_SCR_MASK; + } else { + ie->ie_cgad_pres_ind = 0; + ie->ie_cgad_screen_ind =0; + } + + /* + * Get the ATM address + */ + rc = usf_dec_atm_addr(usf, &ie->ie_cgad_addr, len); + if (rc == EINVAL) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + rc = 0; + } + + return(rc); +} + + +/* + * Decode a calling party subaddress information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_cgsa(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int len, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_cgsa_absent, &ie->ie_u.ie_cgsa, + sizeof(ie_cgsa_absent)); + + /* + * Get and check the subaddress type + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len = ie->ie_length - 1; + if (((c >> UNI_IE_CGSA_TYPE_SHIFT) & UNI_IE_CGSA_TYPE_MASK) != + UNI_IE_CGSA_TYPE_AESA) { + /* + * Invalid subaddress type + */ + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + while (len) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + } + + return(0); + } + + /* + * Get the ATM address + */ + ie->ie_cgsa_addr.address_format = T_ATM_ENDSYS_ADDR; + rc = usf_dec_atm_addr(usf, &ie->ie_cgsa_addr, len); + if (rc == EINVAL) { + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + rc = 0; + } + + return(rc); +} + + +/* + * Decode a cause information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_caus(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, len, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_caus_absent, &ie->ie_u.ie_caus, + sizeof(ie_caus_absent)); + + /* + * Get the cause location + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_caus_loc = c & UNI_IE_CAUS_LOC_MASK; + + /* + * Get the cause value + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_caus_cause = c & UNI_IE_EXT_MASK; + + /* + * Get any included diagnostics + */ + len = ie->ie_length - 2; + for (i = 0, ie->ie_caus_diag_len = 0; + len && i < sizeof(ie->ie_caus_diagnostic); + len--, i++, ie->ie_caus_diag_len++) { + rc = usf_byte(usf, &ie->ie_caus_diagnostic[i]); + if (rc) + return(rc); + } + + return(0); +} + + +/* + * Decode a conection identifier information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_cnid(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + + /* + * Clear the IE + */ + KM_COPY(&ie_cnid_absent, &ie->ie_u.ie_cnid, + sizeof(ie_cnid_absent)); + + rc = usf_ext(usf, &i); + if (rc) + return(rc); + ie->ie_cnid_vp_sig = (i >> UNI_IE_CNID_VPSIG_SHIFT) & + UNI_IE_CNID_VPSIG_MASK; + ie->ie_cnid_pref_excl = i & UNI_IE_CNID_PREX_MASK; + + rc = usf_short(usf, &ie->ie_cnid_vpci); + if (rc) + return(rc); + rc = usf_short(usf, &ie->ie_cnid_vci); + return(rc); +} + + +/* + * Decode a quality of service parameters information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_qosp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + + /* + * Clear the IE + */ + KM_COPY(&ie_qosp_absent, &ie->ie_u.ie_qosp, + sizeof(ie_qosp_absent)); + + /* + * Get forward QoS class + */ + rc = usf_byte(usf, &ie->ie_qosp_fwd_class); + if (rc) + return(rc); + + /* + * Get backward QoS class + */ + rc = usf_byte(usf, &ie->ie_qosp_bkwd_class); + + return(rc); +} + + +/* + * Decode a broadband repeat indicator information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_brpi(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_brpi_absent, &ie->ie_u.ie_brpi, + sizeof(ie_brpi_absent)); + + /* + * Get the repeat indicator + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + ie->ie_brpi_ind = c & UNI_IE_BRPI_IND_MASK; + + return(0); +} + + +/* + * Decode a restart indicator information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_rsti(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_rsti_absent, &ie->ie_u.ie_rsti, + sizeof(ie_rsti_absent)); + + /* + * Get the restart class + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + ie->ie_rsti_class = c & UNI_IE_RSTI_CLASS_MASK; + + return(0); +} + + +/* + * Decode a broadband sending complete information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a broadband sending complete IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_bsdc(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_bsdc_absent, &ie->ie_u.ie_bsdc, + sizeof(ie_bsdc_absent)); + + /* + * Get the sending complete indicator + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Validate the indicator + */ + c &= UNI_IE_EXT_MASK; + if (c != UNI_IE_BSDC_IND) + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + ie->ie_bsdc_ind = c; + + return(0); +} + + +/* + * Decode a transit network selection information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a transit network selection IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_trnt(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, len, rc; + u_char c; + + /* + * Clear the IE + */ + KM_COPY(&ie_trnt_absent, &ie->ie_u.ie_trnt, + sizeof(ie_trnt_absent)); + + /* + * Get the network ID type and plan + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_trnt_id_type = (c >> UNI_IE_TRNT_IDT_SHIFT) & + UNI_IE_TRNT_IDT_MASK; + ie->ie_trnt_id_plan = c & UNI_IE_TRNT_IDP_MASK; + + /* + * Get the length of the network ID + */ + len = ie->ie_length - 1; + ie->ie_trnt_id_len = MIN(len, sizeof(ie->ie_trnt_id)); + + /* + * Get the network ID + */ + for (i=0; i<len; i++) { + if (i<sizeof(ie->ie_trnt_id)) + rc = usf_byte(usf, &ie->ie_trnt_id[i]); + else + rc = usf_byte(usf, &c); + if (rc) + return(rc); + } + + return(0); +} + + +/* + * Decode an unimplemented information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_uimp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + u_char c; + + /* + * Skip over the IE contents + */ + for (i=0; i<ie->ie_length; i++) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + } + + return(0); +} + + +/* + * Decode an information element using field identifiers + * + * The AAL parameters and ATM user cell rate IEs are formatted + * with a one-byte identifier preceeding each field. The routine + * parses these IEs by using a table which relates the field + * identifiers with the fields in the appropriate IE structure. + * Field order in the received message is immaterial. + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * tbl pointer to an IE decoding table + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_ie_ident(usf, ie, tbl) + struct usfmt *usf; + struct ie_generic *ie; + struct ie_decode_tbl *tbl; +{ + int i, len, rc; + u_char c; + u_int8_t cv; + u_int16_t sv; + u_int32_t iv; + void *dest; + + /* + * Scan through the IE + */ + len = ie->ie_length; + while (len) { + /* + * Get the field identifier + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + + /* + * Look up the field in the table + */ + for (i=0; (tbl[i].ident != c) && tbl[i].len; i++) { + } + if (tbl[i].ident == 0) { + /* + * Bad subfield identifier -- flag an + * error and skip over the rest of the IE + */ + ie->ie_err_cause = UNI_IE_CAUS_IECONTENT; + while (len) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + } + return(0); + } + + /* + * Save final destination address + */ + dest = (void *)((int)ie + tbl[i].f_offs); + + /* + * Get the field value + */ + switch (tbl[i].len) { + case 0: + cv = 1; + goto savec; + + case 1: + rc = usf_byte(usf, &cv); + if (rc) + break; +savec: + /* + * Save field value + */ + switch (tbl[i].f_size) { + case 1: + *(u_int8_t *)dest = cv; + break; + case 2: + *(u_int16_t *)dest = cv; + break; + case 4: + *(u_int32_t *)dest = cv; + break; + default: + goto badtbl; + } + break; + + case 2: + rc = usf_short(usf, &sv); + if (rc) + break; + + /* + * Save field value + */ + switch (tbl[i].f_size) { + case 2: + *(u_int16_t *)dest = sv; + break; + case 4: + *(u_int32_t *)dest = sv; + break; + default: + goto badtbl; + } + break; + + case 3: + rc = usf_int3(usf, &iv); + goto savei; + + case 4: + rc = usf_int(usf, &iv); +savei: + /* + * Save field value + */ + if (rc) + break; + switch (tbl[i].f_size) { + case 4: + *(u_int32_t *)dest = iv; + break; + default: + goto badtbl; + } + break; + + default: +badtbl: + log(LOG_ERR, + "uni decode: id=%d,len=%d,off=%d,size=%d\n", + tbl[i].ident, tbl[i].len, + tbl[i].f_offs, tbl[i].f_size); + rc = EFAULT; + break; + } + + if (rc) + return(rc); + + len -= tbl[i].len; + + } + + return(0); +} + + +/* + * Decode an ATM address + * + * Arguments: + * usf pointer to a unisig formatting structure + * addr pointer to an ATM address structure + * len length of data remainig in the IE + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_dec_atm_addr(usf, addr, len) + struct usfmt *usf; + Atm_addr *addr; + int len; +{ + int rc; + u_char c, *cp; + + /* + * Check the address type + */ + addr->address_length = len; + switch (addr->address_format) { + case T_ATM_E164_ADDR: + if (len > sizeof(Atm_addr_e164)) { + goto flush; + } + cp = (u_char *) addr->address; + break; + case T_ATM_ENDSYS_ADDR: + if (len != sizeof(Atm_addr_nsap)) { + goto flush; + } + cp = (u_char *) addr->address; + break; + default: + /* Silence the compiler */ + cp = NULL; + } + + /* + * Get the ATM address + */ + while (len) { + rc = usf_byte(usf, cp); + if (rc) + return(rc); + len--; + cp++; + } + + return(0); + +flush: + while (len) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + len--; + } + + return(EINVAL); +} diff --git a/sys/netatm/uni/unisig_decode.h b/sys/netatm/uni/unisig_decode.h new file mode 100644 index 0000000..dbd184a --- /dev/null +++ b/sys/netatm/uni/unisig_decode.h @@ -0,0 +1,87 @@ +/* + * + * =================================== + * 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: unisig_decode.h,v 1.5 1998/08/26 23:29:21 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message formats + * + */ + +#ifndef _UNI_SIG_DECODE_H +#define _UNI_SIG_DECODE_H + + +/* + * Values specifying which IEs are required in messages + */ +#define IE_NA 0 +#define IE_MAND 1 +#define IE_OPT 2 + +/* + * Structure for information element decoding information + */ +struct ie_ent { + u_char ident; /* IE identifier */ + int min_len; /* Min. length */ + int max_len; /* Max. length */ + int p_idx; /* IE pointer index in msg */ + int (*decode) /* Decoding function */ + __P((struct usfmt *, struct ie_generic *)); +}; + +/* + * Macro to give the offset of a field in a generic IE structure + */ +#define IE_OFFSET(f) \ + ((int)&((struct ie_generic *) 0)->f) + +/* + * Macro to give the size of a field in a generic IE structure + */ +#define IE_FSIZE(f) \ + (sizeof(((struct ie_generic *) 0)->f)) + +#define IE_OFF_SIZE(f) IE_OFFSET(f),IE_FSIZE(f) + + +/* + * Structure to define a field-driven decoding table (for AAL + * parameters and ATM user cell rate IEs) + */ +struct ie_decode_tbl { + u_char ident; + int len; + int f_offs; + int f_size; +}; + +#endif /* _UNI_SIG_DECODE_H */ diff --git a/sys/netatm/uni/unisig_encode.c b/sys/netatm/uni/unisig_encode.c new file mode 100644 index 0000000..0920acc --- /dev/null +++ b/sys/netatm/uni/unisig_encode.c @@ -0,0 +1,1681 @@ +/* + * + * =================================== + * 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: unisig_encode.c,v 1.11 1998/08/26 23:29:21 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message formatting module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_encode.c,v 1.11 1998/08/26 23:29:21 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_mbuf.h> +#include <netatm/uni/unisig_decode.h> + + +/* + * Local functions + */ +static int usf_enc_ie __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_aalp __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_clrt __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_bbcp __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_bhli __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_blli __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_clst __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_cdad __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_cdsa __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_cgad __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_cgsa __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_caus __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_cnid __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_qosp __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_brpi __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_rsti __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_bsdc __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_trnt __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_uimp __P((struct usfmt *, struct ie_generic *)); +static int usf_enc_ie_ident __P((struct usfmt *, struct ie_generic *, + struct ie_decode_tbl *)); +static int usf_enc_atm_addr __P((struct usfmt *, Atm_addr *)); + + +/* + * Local variables + */ +static struct { + u_char ident; /* IE identifier */ + int (*encode) __P((struct usfmt *, struct ie_generic *)); + /* Encoding function */ +} ie_table[] = { + { UNI_IE_AALP, usf_enc_ie_aalp }, + { UNI_IE_CLRT, usf_enc_ie_clrt }, + { UNI_IE_BBCP, usf_enc_ie_bbcp }, + { UNI_IE_BHLI, usf_enc_ie_bhli }, + { UNI_IE_BLLI, usf_enc_ie_blli }, + { UNI_IE_CLST, usf_enc_ie_clst }, + { UNI_IE_CDAD, usf_enc_ie_cdad }, + { UNI_IE_CDSA, usf_enc_ie_cdsa }, + { UNI_IE_CGAD, usf_enc_ie_cgad }, + { UNI_IE_CGSA, usf_enc_ie_cgsa }, + { UNI_IE_CAUS, usf_enc_ie_caus }, + { UNI_IE_CNID, usf_enc_ie_cnid }, + { UNI_IE_QOSP, usf_enc_ie_qosp }, + { UNI_IE_BRPI, usf_enc_ie_brpi }, + { UNI_IE_RSTI, usf_enc_ie_rsti }, + { UNI_IE_BLSH, usf_enc_ie_uimp }, + { UNI_IE_BNSH, usf_enc_ie_uimp }, + { UNI_IE_BSDC, usf_enc_ie_bsdc }, + { UNI_IE_TRNT, usf_enc_ie_trnt }, + { UNI_IE_EPRF, usf_enc_ie_uimp }, + { UNI_IE_EPST, usf_enc_ie_uimp }, + { 0, 0 } +}; + +extern struct ie_decode_tbl ie_aal1_tbl[]; +extern struct ie_decode_tbl ie_aal4_tbl_30[]; +extern struct ie_decode_tbl ie_aal4_tbl_31[]; +extern struct ie_decode_tbl ie_aal5_tbl_30[]; +extern struct ie_decode_tbl ie_aal5_tbl_31[]; +extern struct ie_decode_tbl ie_clrt_tbl[]; + + +/* + * Encode a UNI signalling message + * + * Arguments: + * usf pointer to a unisig formatting structure + * msg pointer to a signalling message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_enc_msg(usf, msg) + struct usfmt *usf; + struct unisig_msg *msg; +{ + int i, len, rc; + u_char c; + u_char *lp0, *lp1; + struct ie_generic *ie; + + union { + short s; + u_char sb[sizeof(short)]; + } su; + + ATM_DEBUG2("usf_enc_msg: usf=0x%x, msg=0x%x\n", + (int)usf, (int)msg); + + /* + * Encode the protocol discriminator + */ + c = UNI_MSG_DISC_Q93B; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Encode the call reference length + */ + c = 3; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Encode the call reference + */ + rc = usf_int3(usf, &msg->msg_call_ref); + if (rc) + return(rc); + + /* + * Encode the message type + */ + rc = usf_byte(usf, &msg->msg_type); + if (rc) + return(rc); + + /* + * Encode the message type extension + */ + c = ((msg->msg_type_flag & UNI_MSG_TYPE_FLAG_MASK) << + UNI_MSG_TYPE_FLAG_SHIFT) + + (msg->msg_type_action & UNI_MSG_TYPE_ACT_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Save the location of the message length and encode a length + * of zero for now. We'll fix the length up at the end. + */ + su.s = 0; + rc = usf_byte_mark(usf, &su.sb[sizeof(short)-2], &lp0); + if (rc) + return(rc); + rc = usf_byte_mark(usf, &su.sb[sizeof(short)-1], &lp1); + if (rc) + return(rc); + + /* + * Process information elements + */ + len = 0; + for (i=0; i<UNI_MSG_IE_CNT; i++) { + ie = msg->msg_ie_vec[i]; + while (ie) { + rc = usf_enc_ie(usf, ie); + if (rc) + return(rc); + len += (ie->ie_length + UNI_IE_HDR_LEN); + ie = ie->ie_next; + } + } + + /* + * Fix the message length in the encoded message + */ + su.s = htons((u_short)len); + *lp0 = su.sb[sizeof(short)-2]; + *lp1 = su.sb[sizeof(short)-1]; + + return(0); +} + + +/* + * Encode an information element + * + * Arguments: + * usf pointer to a UNISIG formatting structure + * msg pointer to a UNISIG message structure + * ie pointer to a generic IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + u_char c; + u_char *lp0, *lp1; + + union { + short s; + u_char sb[sizeof(short)]; + } su; + + ATM_DEBUG2("usf_enc_ie: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the IE identifier + */ + rc = usf_byte(usf, &ie->ie_ident); + if (rc) + return(rc); + + /* + * Encode the extended type + */ + c = ((ie->ie_coding & UNI_IE_CODE_MASK) << UNI_IE_CODE_SHIFT) + + ((ie->ie_flag & UNI_IE_FLAG_MASK) << + UNI_IE_FLAG_SHIFT) + + (ie->ie_action & UNI_IE_ACT_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Mark the current location in the output stream. Encode a + * length of zero for now; we'll come back and fix it up at + * the end. + */ + su.s = 0; + rc = usf_byte_mark(usf, &su.sb[sizeof(short)-2], &lp0); + if (rc) + return(rc); + rc = usf_byte_mark(usf, &su.sb[sizeof(short)-1], &lp1); + if (rc) + return(rc); + + /* + * Look up the information element in the table + */ + for (i=0; (ie->ie_ident != ie_table[i].ident) && + (ie_table[i].encode != NULL); i++) { + } + if (ie_table[i].encode == NULL) { + /* + * Unrecognized IE + */ + return(EINVAL); + } + + /* + * Process the IE by calling the function indicated + * in the IE table + */ + rc = ie_table[i].encode(usf, ie); + if (rc) + return(rc); + + /* + * Set the length in the output stream + */ + su.s = htons((u_short)ie->ie_length); + *lp0 = su.sb[sizeof(short)-2]; + *lp1 = su.sb[sizeof(short)-1]; + + return(0); +} + + +/* + * Encode an AAL parameters information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to an AAL parms IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_aalp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc = 0; + + ATM_DEBUG2("usf_enc_ie_aalp: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + ie->ie_length = 0; + + /* + * Encode the AAL type + */ + if (ie->ie_aalp_aal_type == T_ATM_ABSENT) + return(0); + rc = usf_byte(usf, &ie->ie_aalp_aal_type); + if (rc) + return(rc); + + /* + * Process based on AAL type + */ + switch (ie->ie_aalp_aal_type) { + case UNI_IE_AALP_AT_AAL1: + rc = usf_enc_ie_ident(usf, ie, ie_aal1_tbl); + break; + case UNI_IE_AALP_AT_AAL3: + if (usf->usf_sig->us_proto == ATM_SIG_UNI30) + rc = usf_enc_ie_ident(usf, ie, ie_aal4_tbl_30); + else + rc = usf_enc_ie_ident(usf, ie, ie_aal4_tbl_31); + break; + case UNI_IE_AALP_AT_AAL5: + if (usf->usf_sig->us_proto == ATM_SIG_UNI30) + rc = usf_enc_ie_ident(usf, ie, ie_aal5_tbl_30); + else + rc = usf_enc_ie_ident(usf, ie, ie_aal5_tbl_31); + break; + case UNI_IE_AALP_AT_AALU: + /* + * Encode the user data + */ + i = 0; + while (i < sizeof(ie->ie_aalp_user_info)) { + rc = usf_byte(usf, &ie->ie_aalp_user_info[i]); + if (rc) + break; + i++; + ie->ie_length++; + } + break; + default: + return(EINVAL); + } + + ie->ie_length++; + return(rc); +} + + +/* + * Encode a user cell rate information element + * + * This routine just encodes the parameters required for best + * effort service. + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_clrt(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + + ATM_DEBUG2("usf_enc_ie_clrt: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + +#ifdef NOTDEF + /* + * Encode Peak Cell Rate Forward CLP = 0 + 1 + */ + c = UNI_IE_CLRT_FWD_PEAK_01_ID; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + rc = usf_int3(usf, &ie->ie_clrt_fwd_peak_01); + if (rc) + return(rc); + + /* + * Encode Peak Cell Rate Backward CLP = 0 + 1 + */ + c = UNI_IE_CLRT_BKWD_PEAK_01_ID; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + rc = usf_int3(usf, &ie->ie_clrt_bkwd_peak_01); + if (rc) + return(rc); + + /* + * Encode Best Effort Flag + */ + c = UNI_IE_CLRT_BEST_EFFORT_ID; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Set IE length + */ + ie->ie_length = 9; +#endif + + /* + * Encode the user cell rate IE using the table + */ + ie->ie_length = 0; + rc = usf_enc_ie_ident(usf, ie, ie_clrt_tbl); + + return(rc); +} + + +/* + * Encode a broadband bearer capability information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_bbcp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_bbcp: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + ie->ie_length = 0; + + /* + * Encode the broadband bearer class + */ + if (ie->ie_bbcp_bearer_class == T_ATM_ABSENT) + return(0); + c = ie->ie_bbcp_bearer_class & UNI_IE_BBCP_BC_MASK; + if (ie->ie_bbcp_bearer_class != UNI_IE_BBCP_BC_BCOB_X) + c |= UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * If the broadband bearer class was X, the next + * byte has the traffic type and timing requirements + */ + if (ie->ie_bbcp_bearer_class == UNI_IE_BBCP_BC_BCOB_X) { + c = ((ie->ie_bbcp_traffic_type & UNI_IE_BBCP_TT_MASK) << + UNI_IE_BBCP_TT_SHIFT) + + (ie->ie_bbcp_timing_req & + UNI_IE_BBCP_TR_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + + /* + * Encode the clipping and user plane connection configuration + */ + c = ((ie->ie_bbcp_clipping & UNI_IE_BBCP_SC_MASK) << + UNI_IE_BBCP_SC_SHIFT) + + (ie->ie_bbcp_conn_config & + UNI_IE_BBCP_CC_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + return(0); +} + + +/* + * Encode a broadband high layer information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_bhli(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + u_int type; + + ATM_DEBUG2("usf_enc_ie_bhli: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + ie->ie_length = 0; + + /* + * Encode the high layer information type + */ + if (ie->ie_bhli_type == T_ATM_ABSENT) + return(0); + type = ie->ie_bhli_type | UNI_IE_EXT_BIT; + rc = usf_ext(usf, &type); + if (rc) + return(rc); + ie->ie_length++; + + /* + * What comes next depends on the type + */ + switch (ie->ie_bhli_type) { + case UNI_IE_BHLI_TYPE_ISO: + case UNI_IE_BHLI_TYPE_USER: + /* + * ISO or user-specified parameters -- take the + * length of information from the IE length + */ + for (i=0; i<ie->ie_length-1; i++) { + rc = usf_byte(usf, &ie->ie_bhli_info[i]); + if (rc) + return(rc); + ie->ie_length++; + } + break; + case UNI_IE_BHLI_TYPE_HLP: + /* + * Make sure the IE is long enough for the high + * layer profile information, then get it + */ + if (usf->usf_sig->us_proto != ATM_SIG_UNI30) + return (EINVAL); + for (i=0; i<UNI_IE_BHLI_HLP_LEN; i++) { + rc = usf_byte(usf, &ie->ie_bhli_info[i]); + if (rc) + return(rc); + ie->ie_length++; + } + break; + case UNI_IE_BHLI_TYPE_VSA: + /* + * Make sure the IE is long enough for the vendor- + * specific application information, then get it + */ + for (i=0; i<UNI_IE_BHLI_VSA_LEN; i++) { + rc = usf_byte(usf, &ie->ie_bhli_info[i]); + if (rc) + return(rc); + ie->ie_length++; + } + break; + default: + return(EINVAL); + } + + return(0); +} + + +/* + * Encode a broadband low layer information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_blli(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c; + int rc; + u_int ipi; + + ATM_DEBUG2("usf_enc_ie_blli: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + ie->ie_length = 0; + + /* + * Encode paramteters for whichever protocol layers the + * user specified + */ + + /* + * Layer 1 information + */ + if (ie->ie_blli_l1_id && ie->ie_blli_l1_id != T_ATM_ABSENT) { + c = (UNI_IE_BLLI_L1_ID << UNI_IE_BLLI_LID_SHIFT) + + (ie->ie_blli_l1_id & + UNI_IE_BLLI_LP_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + + /* + * Layer 2 information + */ + if (ie->ie_blli_l2_id && ie->ie_blli_l2_id != T_ATM_ABSENT) { + c = (UNI_IE_BLLI_L2_ID << UNI_IE_BLLI_LID_SHIFT) + + (ie->ie_blli_l2_id & + UNI_IE_BLLI_LP_MASK); + + switch (ie->ie_blli_l2_id) { + case UNI_IE_BLLI_L2P_X25L: + case UNI_IE_BLLI_L2P_X25M: + case UNI_IE_BLLI_L2P_HDLC1: + case UNI_IE_BLLI_L2P_HDLC2: + case UNI_IE_BLLI_L2P_HDLC3: + case UNI_IE_BLLI_L2P_Q922: + case UNI_IE_BLLI_L2P_ISO7776: + /* + * Write the Layer 2 type + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * Encode the Layer 2 mode + */ + if (ie->ie_blli_l2_mode) { + c = (ie->ie_blli_l2_mode & + UNI_IE_BLLI_L2MODE_MASK) << + UNI_IE_BLLI_L2MODE_SHIFT; + if (!ie->ie_blli_l2_window) + c |= UNI_IE_EXT_BIT; + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + + /* + * Encode the Layer 2 window size + */ + if (ie->ie_blli_l2_window) { + c = (ie->ie_blli_l2_window & + UNI_IE_EXT_MASK) + + UNI_IE_EXT_BIT; + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + break; + case UNI_IE_BLLI_L2P_USER: + /* + * Write the Layer 2 type + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * Encode the user-specified layer 2 info + */ + c = (ie->ie_blli_l2_user_proto & + UNI_IE_EXT_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + break; + default: + /* + * Write the Layer 2 type + */ + c |= UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + break; + } + } + + /* + * Layer 3 information + */ + if (ie->ie_blli_l3_id && ie->ie_blli_l3_id != T_ATM_ABSENT) { + /* + * Encode the layer 3 protocol ID + */ + c = (UNI_IE_BLLI_L3_ID << UNI_IE_BLLI_LID_SHIFT) + + (ie->ie_blli_l3_id & + UNI_IE_BLLI_LP_MASK); + + /* + * Process other fields based on protocol ID + */ + switch(ie->ie_blli_l3_id) { + case UNI_IE_BLLI_L3P_X25: + case UNI_IE_BLLI_L3P_ISO8208: + case UNI_IE_BLLI_L3P_ISO8878: + /* + * Write the protocol ID + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + if (ie->ie_blli_l3_mode || + ie->ie_blli_l3_packet_size || + ie->ie_blli_l3_window) { + c = (ie->ie_blli_l3_mode & + UNI_IE_BLLI_L3MODE_MASK) << + UNI_IE_BLLI_L3MODE_SHIFT; + if (!ie->ie_blli_l3_packet_size && + !ie->ie_blli_l3_window) + c |= UNI_IE_EXT_BIT; + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + + if (ie->ie_blli_l3_packet_size || + ie->ie_blli_l3_window) { + c = ie->ie_blli_l3_packet_size & + UNI_IE_BLLI_L3PS_MASK; + if (!ie->ie_blli_l3_window) + c |= UNI_IE_EXT_BIT; + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + + if (ie->ie_blli_l3_window) { + c = (ie->ie_blli_l3_window & + UNI_IE_EXT_MASK) + + UNI_IE_EXT_BIT; + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + } + break; + case UNI_IE_BLLI_L3P_USER: + /* + * Write the protocol ID + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * Encode the user-specified protocol info + */ + c = (ie->ie_blli_l3_user_proto & + UNI_IE_EXT_MASK) + + UNI_IE_EXT_BIT; + + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + break; + case UNI_IE_BLLI_L3P_ISO9577: + /* + * Write the protocol ID + */ + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * Encode the IPI + */ + ipi = ie->ie_blli_l3_ipi << + UNI_IE_BLLI_L3IPI_SHIFT; + rc = usf_ext(usf, &ipi); + if (rc) + return(rc); + ie->ie_length += 2; + + if (ie->ie_blli_l3_ipi == + UNI_IE_BLLI_L3IPI_SNAP) { + c = UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + rc = usf_byte(usf, + &ie->ie_blli_l3_oui[0]); + if (rc) + return(rc); + + rc = usf_byte(usf, + &ie->ie_blli_l3_oui[1]); + if (rc) + return(rc); + + rc = usf_byte(usf, + &ie->ie_blli_l3_oui[2]); + if (rc) + return(rc); + + rc = usf_byte(usf, + &ie->ie_blli_l3_pid[0]); + if (rc) + return(rc); + + rc = usf_byte(usf, + &ie->ie_blli_l3_pid[1]); + if (rc) + return(rc); + + ie->ie_length += 6; + } + break; + default: + /* + * Write the layer 3 protocol ID + */ + c |= UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + break; + } + } + + return(0); +} + + +/* + * Encode a call state information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_clst(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_clst: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + c = ie->ie_clst_state & UNI_IE_CLST_STATE_MASK; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length = 1; + + return(0); +} + + +/* + * Encode a called party number information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_cdad(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c; + int rc; + + ATM_DEBUG2("usf_enc_ie_cdad: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the numbering plan + */ + switch(ie->ie_cdad_addr.address_format) { + case T_ATM_E164_ADDR: + c = UNI_IE_CDAD_PLAN_E164 + + (UNI_IE_CDAD_TYPE_INTL + << UNI_IE_CDAD_TYPE_SHIFT); + ie->ie_length = sizeof(Atm_addr_e164) + 1; + break; + case T_ATM_ENDSYS_ADDR: + c = UNI_IE_CDAD_PLAN_NSAP + + (UNI_IE_CDAD_TYPE_UNK + << UNI_IE_CDAD_TYPE_SHIFT); + ie->ie_length = sizeof(Atm_addr_nsap) + 1; + break; + default: + return(EINVAL); + } + c |= UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Encode the ATM address + */ + rc = usf_enc_atm_addr(usf, &ie->ie_cdad_addr); + + return(rc); +} + + +/* + * Encode a called party subaddress information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_cdsa(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c; + int rc; + + /* + * Encode the subaddress type + */ + switch(ie->ie_cdsa_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + c = UNI_IE_CDSA_TYPE_AESA << UNI_IE_CDSA_TYPE_SHIFT; + ie->ie_length = sizeof(Atm_addr_nsap) + 1; + break; + default: + return(EINVAL); + } + c |= UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Encode the ATM address + */ + rc = usf_enc_atm_addr(usf, &ie->ie_cdsa_addr); + + return(rc); +} + + +/* + * Encode a calling party number information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_cgad(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c; + int rc; + + ATM_DEBUG2("usf_enc_ie_cgad: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the numbering plan + */ + switch(ie->ie_cgad_addr.address_format) { + case T_ATM_E164_ADDR: + c = UNI_IE_CGAD_PLAN_E164 + + (UNI_IE_CGAD_TYPE_INTL + << UNI_IE_CGAD_TYPE_SHIFT) + + UNI_IE_EXT_BIT; + ie->ie_length = sizeof(Atm_addr_e164) + 1; + break; + case T_ATM_ENDSYS_ADDR: + c = UNI_IE_CGAD_PLAN_NSAP + + (UNI_IE_CGAD_TYPE_UNK + << UNI_IE_CGAD_TYPE_SHIFT) + + UNI_IE_EXT_BIT; + ie->ie_length = sizeof(Atm_addr_nsap) + 1; + break; + default: + return(EINVAL); + } + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Encode the presentation and screening indicators + */ +#ifdef NOTDEF + c = ((ie->ie_cgad_pres_ind & UNI_IE_CGAD_PRES_MASK) + << UNI_IE_CGAD_PRES_SHIFT) + + (ie->ie_cgad_screen_ind & + UNI_IE_CGAD_SCR_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); +#endif + + + /* + * Encode the ATM address + */ + rc = usf_enc_atm_addr(usf, &ie->ie_cgad_addr); + + return(rc); +} + + +/* + * Encode a calling party subaddress information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_cgsa(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + u_char c; + int rc; + + /* + * Encode the subaddress type + */ + switch(ie->ie_cgsa_addr.address_format) { + case T_ATM_ENDSYS_ADDR: + c = UNI_IE_CGSA_TYPE_AESA << UNI_IE_CGSA_TYPE_SHIFT; + ie->ie_length = sizeof(Atm_addr_nsap) + 1; + break; + default: + return(EINVAL); + } + c |= UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + /* + * Encode the ATM address + */ + rc = usf_enc_atm_addr(usf, &ie->ie_cgsa_addr); + + return(rc); +} + + +/* + * Encode a cause information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_caus(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_caus: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + ie->ie_length = 0; + + /* + * Encode the cause location + */ + c = (ie->ie_caus_loc & UNI_IE_CAUS_LOC_MASK) | UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * Encode the cause value + */ + c = ie->ie_caus_cause | UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length++; + + /* + * Encode any included diagnostics + */ + for (i = 0; i < ie->ie_caus_diag_len && + i < sizeof(ie->ie_caus_diagnostic); + i++) { + rc = usf_byte(usf, &ie->ie_caus_diagnostic[i]); + if (rc) + return(rc); + ie->ie_length++; + } + + return(0); +} + + +/* + * Encode a conection identifier information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_cnid(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_cnid: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + c = ((ie->ie_cnid_vp_sig & UNI_IE_CNID_VPSIG_MASK) + << UNI_IE_CNID_VPSIG_SHIFT) + + (ie->ie_cnid_pref_excl & UNI_IE_CNID_PREX_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + + rc = usf_short(usf, &ie->ie_cnid_vpci); + if (rc) + return(rc); + rc = usf_short(usf, &ie->ie_cnid_vci); + if (rc) + return(rc); + + ie->ie_length = 5; + return(0); +} + + +/* + * Encode a quality of service parameters information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_qosp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + + ATM_DEBUG2("usf_enc_ie_qosp: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode forward QoS class + */ + if (ie->ie_qosp_fwd_class == T_ATM_ABSENT || + ie->ie_qosp_bkwd_class == T_ATM_ABSENT) + return(0); + rc = usf_byte(usf, &ie->ie_qosp_fwd_class); + if (rc) + return(rc); + + /* + * Encode backward QoS class + */ + rc = usf_byte(usf, &ie->ie_qosp_bkwd_class); + + ie->ie_length = 2; + return(rc); +} + + +/* + * Encode a broadband repeat indicator information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_brpi(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_brpi: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the repeat indicator + */ + c = ie->ie_brpi_ind + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + + return(rc); +} + + +/* + * Encode a restart indicator information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_rsti(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_rsti: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the restart class + */ + c = (ie->ie_rsti_class & UNI_IE_RSTI_CLASS_MASK) | + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + ie->ie_length = 1; + + return(rc); +} + + +/* + * Encode a broadband sending complete information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a broadband sending complete IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_bsdc(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_bsdc: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the sending complete indicator + */ + c = UNI_IE_BSDC_IND | UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + ie->ie_length = 1; + + return(rc); +} + + +/* + * Encode a transit network selection information element + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a transit network selection rate IE structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_trnt(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + int i, rc; + u_char c; + + ATM_DEBUG2("usf_enc_ie_trnt: usf=0x%x, ie=0x%x\n", + (int)usf, (int)ie); + + /* + * Encode the sending complete indicator + */ + c = ((ie->ie_trnt_id_type & UNI_IE_TRNT_IDT_MASK) << + UNI_IE_TRNT_IDT_SHIFT) + + (ie->ie_trnt_id_plan & UNI_IE_TRNT_IDP_MASK) + + UNI_IE_EXT_BIT; + rc = usf_byte(usf, &c); + if (rc) + return(rc); + ie->ie_length = 1; + + /* + * Encode the network identification + */ + for (i=0; i<ie->ie_trnt_id_len; i++) { + rc = usf_byte(usf, &ie->ie_trnt_id[i]); + if (rc) + return(rc); + ie->ie_length++; + } + + return(rc); +} + + +/* + * Encode an unsupported IE type + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to an IE structure + * + * Returns: + * 0 success + * + */ +static int +usf_enc_ie_uimp(usf, ie) + struct usfmt *usf; + struct ie_generic *ie; +{ + return(0); +} + + +/* + * Encode an information element using field identifiers + * + * The AAL parameters and ATM user cell rate IEs are formatted + * with a one-byte identifier preceeding each field. The routine + * encodes these IEs by using a table which relates the field + * identifiers with the fields in the appropriate IE structure. + * + * Arguments: + * usf pointer to a unisig formatting structure + * ie pointer to a cell rate IE structure + * tbl pointer to an IE decoding table + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_ie_ident(usf, ie, tbl) + struct usfmt *usf; + struct ie_generic *ie; + struct ie_decode_tbl *tbl; +{ + int i, len, rc; + char *cp; + u_int8_t cv; + u_int16_t sv; + u_int32_t iv; + + ATM_DEBUG3("usf_enc_ie_ident: usf=0x%x, ie=0x%x, tbl=0x%x\n", + (int)usf, (int)ie, (int)tbl); + + /* + * Scan through the IE table + */ + len = 0; + for (i=0; tbl[i].ident; i++) { + /* + * Check whether to send the field + */ + cp = (char *) ((int)ie + tbl[i].f_offs); + if (tbl[i].len == 0) { + if ((*cp == T_NO || *cp == T_ATM_ABSENT)) + continue; + } else { + switch (tbl[i].f_size) { + case 1: + if (*(int8_t *)cp == T_ATM_ABSENT) + continue; + break; + case 2: + if (*(int16_t *)cp == T_ATM_ABSENT) + continue; + break; + case 4: + if (*(int32_t *)cp == T_ATM_ABSENT) + continue; + break; + default: +badtbl: + log(LOG_ERR, + "uni encode: id=%d,len=%d,off=%d,size=%d\n", + tbl[i].ident, tbl[i].len, + tbl[i].f_offs, tbl[i].f_size); + return (EFAULT); + } + } + + /* + * Encode the field identifier + */ + rc = usf_byte(usf, &tbl[i].ident); + if (rc) + return(rc); + len++; + + /* + * Encode the field value + */ + switch (tbl[i].len) { + case 0: + break; + case 1: + switch (tbl[i].f_size) { + case 1: + cv = *(u_int8_t *)cp; + break; + case 2: + cv = *(u_int16_t *)cp; + break; + case 4: + cv = *(u_int32_t *)cp; + break; + default: + goto badtbl; + } + rc = usf_byte(usf, &cv); + break; + + case 2: + switch (tbl[i].f_size) { + case 2: + sv = *(u_int16_t *)cp; + break; + case 4: + sv = *(u_int32_t *)cp; + break; + default: + goto badtbl; + } + rc = usf_short(usf, &sv); + break; + + case 3: + switch (tbl[i].f_size) { + case 4: + iv = *(u_int32_t *)cp; + break; + default: + goto badtbl; + } + rc = usf_int3(usf, &iv); + break; + + case 4: + switch (tbl[i].f_size) { + case 4: + iv = *(u_int32_t *)cp; + break; + default: + goto badtbl; + } + rc = usf_int(usf, &iv); + break; + + default: + goto badtbl; + } + + len += tbl[i].len; + + if (rc) + return(rc); + } + + ie->ie_length = len; + return(0); +} + + +/* + * Encode an ATM address + * + * Arguments: + * usf pointer to a unisig formatting structure + * addr pointer to an ATM address structure. The address + * type must already be set correctly. + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +usf_enc_atm_addr(usf, addr) + struct usfmt *usf; + Atm_addr *addr; +{ + int len, rc; + u_char *cp; + + /* + * Check the address type + */ + switch (addr->address_format) { + case T_ATM_E164_ADDR: + cp = (u_char *) addr->address; + len = sizeof(Atm_addr_e164); + break; + case T_ATM_ENDSYS_ADDR: + cp = (u_char *) addr->address; + len = sizeof(Atm_addr_nsap); + break; + default: + return(EINVAL); + } + + /* + * Get the address bytes + */ + while (len) { + rc = usf_byte(usf, cp); + if (rc) + return(rc); + len--; + cp++; + } + + return(0); +} diff --git a/sys/netatm/uni/unisig_if.c b/sys/netatm/uni/unisig_if.c new file mode 100644 index 0000000..784b6c5 --- /dev/null +++ b/sys/netatm/uni/unisig_if.c @@ -0,0 +1,1012 @@ +/* + * + * =================================== + * 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: unisig_if.c,v 1.12 1998/07/30 22:36:57 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * System interface module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_if.c,v 1.12 1998/07/30 22:36:57 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/ipatm/ipatm_var.h> +#include <netatm/ipatm/ipatm_serv.h> +#include <netatm/uni/uniip_var.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> + + +/* + * Global variables + */ +struct sp_info unisig_vcpool = { + "unisig vcc pool", /* si_name */ + sizeof(struct unisig_vccb), /* si_blksiz */ + 10, /* si_blkcnt */ + 50 /* si_maxallow */ +}; + +struct sp_info unisig_msgpool = { + "unisig message pool", /* si_name */ + sizeof(struct unisig_msg), /* si_blksiz */ + 10, /* si_blkcnt */ + 50 /* si_maxallow */ +}; + +struct sp_info unisig_iepool = { + "unisig ie pool", /* si_name */ + sizeof(struct ie_generic), /* si_blksiz */ + 10, /* si_blkcnt */ + 50 /* si_maxallow */ +}; + + +/* + * Local functions + */ +static int unisig_attach __P((struct sigmgr *, struct atm_pif *)); +static int unisig_detach __P((struct atm_pif *)); +static int unisig_setup __P((Atm_connvc *, int *)); +static int unisig_release __P((struct vccb *, int *)); +static int unisig_accept __P((struct vccb *, int *)); +static int unisig_reject __P((struct vccb *, int *)); +static int unisig_abort __P((struct vccb *)); +static int unisig_ioctl __P((int, caddr_t, caddr_t)); + + +/* + * Local variables + */ +static struct sigmgr unisig_mgr30 = { + NULL, + ATM_SIG_UNI30, + NULL, + unisig_attach, + unisig_detach, + unisig_setup, + unisig_accept, + unisig_reject, + unisig_release, + unisig_free, + unisig_ioctl +}; + +static struct sigmgr unisig_mgr31 = { + NULL, + ATM_SIG_UNI31, + NULL, + unisig_attach, + unisig_detach, + unisig_setup, + unisig_accept, + unisig_reject, + unisig_release, + unisig_free, + unisig_ioctl +}; + + +/* + * Initialize UNISIG processing + * + * This will be called during module loading. We'll just register + * the UNISIG protocol descriptor and wait for a UNISIG ATM interface + * to come online. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +int +unisig_start() +{ + int err = 0; + + /* + * Verify software version + */ + if (atm_version != ATM_VERSION) { + log(LOG_ERR, "version mismatch: unisig=%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); + } + + /* + * Register ourselves with system + */ + err = atm_sigmgr_register(&unisig_mgr30); + if (err) + goto done; + + err = atm_sigmgr_register(&unisig_mgr31); + +done: + return (err); +} + + +/* + * Halt UNISIG processing + * + * This should be called just prior to unloading the module from + * memory. All UNISIG interfaces must be deregistered before the + * protocol can be shutdown. + * + * Arguments: + * none + * + * Returns: + * 0 startup was successful + * errno startup failed - reason indicated + * + */ +int +unisig_stop() +{ + int err = 0; + int s = splnet(); + + + /* + * Any protocol instances still registered? + */ + if ((unisig_mgr30.sm_prinst != NULL) || + (unisig_mgr31.sm_prinst != NULL)) { + + /* Yes, can't stop now */ + err = EBUSY; + goto done; + } + + /* + * De-register from system + */ + (void) atm_sigmgr_deregister(&unisig_mgr30); + (void) atm_sigmgr_deregister(&unisig_mgr31); + + /* + * Free up our storage pools + */ + atm_release_pool(&unisig_vcpool); + atm_release_pool(&unisig_msgpool); + atm_release_pool(&unisig_iepool); + +done: + (void) splx(s); + return (err); +} + + +/* + * Attach a UNISIG-controlled interface + * + * Each ATM physical interface must be attached with the signalling + * manager for the interface's signalling protocol (via the + * atm_sigmgr_attach function). This function will handle the + * attachment for UNISIG-controlled interfaces. A new UNISIG protocol + * instance will be created and then we'll just sit around waiting for + * status or connection requests. + * + * Function must be called at splnet. + * + * Arguments: + * smp pointer to UNISIG signalling manager control block + * pip pointer to ATM physical interface control block + * + * Returns: + * 0 attach successful + * errno attach failed - reason indicated + * + */ +static int +unisig_attach(smp, pip) + struct sigmgr *smp; + struct atm_pif *pip; +{ + int err = 0, s; + struct unisig *usp = NULL; + + ATM_DEBUG2("unisig_attach: smp=%x, pip=%x\n", smp, pip); + + /* + * Allocate UNISIG protocol instance control block + */ + usp = (struct unisig *) + KM_ALLOC(sizeof(struct unisig), M_DEVBUF, M_NOWAIT); + if (usp == NULL) { + err = ENOMEM; + goto done; + } + KM_ZERO(usp, sizeof(struct unisig)); + + /* + * Set state in UNISIG protocol instance control block + */ + usp->us_state = UNISIG_NULL; + usp->us_proto = smp->sm_proto; + + /* + * Set initial call reference allocation value + */ + usp->us_cref = 1; + + /* + * Link instance into manager's chain + */ + LINK2TAIL((struct siginst *)usp, struct siginst, smp->sm_prinst, + si_next); + + /* + * Link in interface + */ + usp->us_pif = pip; + s = splimp(); + pip->pif_sigmgr = smp; + pip->pif_siginst = (struct siginst *) usp; + (void) splx(s); + + /* + * Clear our ATM address. The address will be set by user + * command or by registration via ILMI. + */ + usp->us_addr.address_format = T_ATM_ABSENT; + usp->us_addr.address_length = 0; + usp->us_subaddr.address_format = T_ATM_ABSENT; + usp->us_subaddr.address_length = 0; + + /* + * Set pointer to IP + */ + usp->us_ipserv = &uniip_ipserv; + + /* + * Kick-start the UNISIG protocol + */ + UNISIG_TIMER(usp, 0); + + /* + * Log the fact that we've attached + */ + log(LOG_INFO, "unisig: attached to interface %s%d\n", + pip->pif_name, pip->pif_unit); + +done: + /* + * Reset our work if attach fails + */ + if (err) { + if (usp) { + UNISIG_CANCEL(usp); + UNLINK((struct siginst *)usp, struct siginst, + smp->sm_prinst, si_next); + KM_FREE(usp, sizeof(struct unisig), M_DEVBUF); + } + s = splimp(); + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + (void) splx(s); + } + + return (err); +} + + +/* + * Detach a UNISIG-controlled interface + * + * Each ATM physical interface may be detached from its signalling + * manager (via the atm_sigmgr_detach function). This function will + * handle the detachment for all UNISIG-controlled interfaces. All + * circuits will be immediately terminated. + * + * Function must be called at splnet. + * + * Arguments: + * pip pointer to ATM physical interface control block + * + * Returns: + * 0 detach successful + * errno detach failed - reason indicated + * + */ +static int +unisig_detach(pip) + struct atm_pif *pip; +{ + struct unisig *usp; + int err; + + ATM_DEBUG1("unisig_detach: pip=0x%x\n", pip); + + /* + * Get UNISIG protocol instance + */ + usp = (struct unisig *)pip->pif_siginst; + + /* + * Return an error if we're already detaching + */ + if (usp->us_state == UNISIG_DETACH) { + return(EALREADY); + } + + /* + * Pass the detach event to the signalling manager + * state machine + */ + err = unisig_sigmgr_state(usp, UNISIG_SIGMGR_DETACH, + (KBuffer *)0); + + /* + * Log the fact that we've detached + */ + if (!err) + log(LOG_INFO, "unisig: detached from interface %s%d\n", + pip->pif_name, pip->pif_unit); + + return (0); +} + + +/* + * Open a UNISIG ATM Connection + * + * All service user requests to open a VC connection (via + * atm_open_connection) over an ATM interface attached to the UNISIG + * signalling manager are handled here. + * + * Function will be called at splnet. + * + * Arguments: + * cvp pointer to user's requested connection parameters + * errp pointer to an int for extended error information + * + * Returns: + * CALL_PROCEEDING connection establishment is in progress + * CALL_FAILED connection establishment failed + * CALL_CONNECTED connection has been successfully established + * + */ +static int +unisig_setup(cvp, errp) + Atm_connvc *cvp; + int *errp; +{ + struct atm_pif *pip = cvp->cvc_attr.nif->nif_pif; + struct unisig *usp = (struct unisig *)pip->pif_siginst; + int rc = 0; + + ATM_DEBUG1("unisig_setup: cvp=0x%x\n", cvp); + + /* + * Intialize the returned error code + */ + *errp = 0; + + /* + * Open the connection + */ + switch (cvp->cvc_attr.called.addr.address_format) { + case T_ATM_PVC_ADDR: + /* + * Create a PVC + */ + *errp = unisig_open_vcc(usp, cvp); + rc = (*errp ? CALL_FAILED : CALL_CONNECTED); + break; + + case T_ATM_ENDSYS_ADDR: + case T_ATM_E164_ADDR: + + /* + * Create an SVC + */ + *errp = unisig_open_vcc(usp, cvp); + rc = (*errp ? CALL_FAILED : CALL_PROCEEDING); + break; + + default: + *errp = EPROTONOSUPPORT; + rc = CALL_FAILED; + } + + return (rc); +} + + +/* + * Close a UNISIG ATM Connection + * + * All service user requests to terminate a previously open VC + * connection (via the atm_close_connection function), which is running + * over an interface attached to the UNISIG signalling manager, are + * handled here. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * errp pointer to an int for extended error information + * + * Returns: + * CALL_PROCEEDING connection termination is in progress + * CALL_FAILED connection termination failed + * CALL_CLEARED connection has been successfully terminated + * + */ +static int +unisig_release(vcp, errp) + struct vccb *vcp; + int *errp; +{ + int rc = 0; + struct atm_pif *pip = vcp->vc_pif; + struct unisig *usp = (struct unisig *)pip->pif_siginst; + + ATM_DEBUG1("unisig_release: vcp=0x%x\n", vcp); + + /* + * Initialize returned error code + */ + *errp = 0; + + /* + * Validate the connection type (PVC or SVC) + */ + if (!(vcp->vc_type & (VCC_PVC | VCC_SVC))) { + *errp = EPROTONOSUPPORT; + return(CALL_FAILED); + } + + /* + * Close the VCCB + */ + *errp = unisig_close_vcc(usp, (struct unisig_vccb *)vcp); + + /* + * Set the return code + */ + if (*errp) { + rc = CALL_FAILED; + } else if (vcp->vc_sstate == UNI_NULL || + vcp->vc_sstate == UNI_FREE) { + rc = CALL_CLEARED; + } else { + rc = CALL_PROCEEDING; + } + + return (rc); +} + + +/* + * Accept a UNISIG Open from a remote host + * + * A user calls this routine (via the atm_accept_call function) + * after it is notified that an open request was received for it. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to user's VCCB + * errp pointer to an int for extended error information + * + * Returns: + * CALL_PROCEEDING connection establishment is in progress + * CALL_FAILED connection establishment failed + * CALL_CONNECTED connection has been successfully established + * + */ +static int +unisig_accept(vcp, errp) + struct vccb *vcp; + int *errp; +{ + struct unisig_vccb *uvp = (struct unisig_vccb *)vcp; + struct atm_pif *pip = uvp->uv_pif; + struct unisig *usp = (struct unisig *)pip->pif_siginst; + + ATM_DEBUG1("unisig_accept: vcp=0x%x\n", vcp); + + /* + * Initialize the returned error code + */ + *errp = 0; + + /* + * Return an error if we're detaching + */ + if (usp->us_state == UNISIG_DETACH) { + *errp = ENETDOWN; + goto free; + } + + /* + * Return an error if we lost the connection + */ + if (uvp->uv_sstate == UNI_FREE) { + *errp = ENETDOWN; + goto free; + } + + /* + * Pass the acceptance to the VC state machine + */ + *errp = unisig_vc_state(usp, uvp, UNI_VC_ACCEPT_CALL, + (struct unisig_msg *) 0); + if (*errp) + goto failed; + + return(CALL_PROCEEDING); + +failed: + /* + * On error, free the VCCB and return CALL_FAILED + */ + +free: + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + DEQUEUE(uvp, struct unisig_vccb, uv_sigelem, usp->us_vccq); + unisig_free((struct vccb *)uvp); + + return(CALL_FAILED); +} + + +/* + * Reject a UNISIG Open from a remote host + * + * A user calls this routine (via the atm_reject_call function) + * after it is notified that an open request was received for it. + * + * Function will be called at splnet. + * + * Arguments: + * uvp pointer to user's VCCB + * errp pointer to an int for extended error information + * + * Returns: + * CALL_CLEARED call request rejected + * CALL_FAILED call rejection failed + * + */ +static int +unisig_reject(vcp, errp) + struct vccb *vcp; + int *errp; +{ + struct unisig_vccb *uvp = (struct unisig_vccb *)vcp; + struct atm_pif *pip = uvp->uv_pif; + struct unisig *usp = (struct unisig *)pip->pif_siginst; + + ATM_DEBUG1("unisig_reject: uvp=0x%x\n", uvp); + + /* + * Initialize the returned error code + */ + *errp = 0; + + + /* + * Return an error if we're detaching + */ + if (usp->us_state == UNISIG_DETACH) { + *errp = ENETDOWN; + goto failed; + } + + /* + * Call the VC state machine + */ + *errp = unisig_vc_state(usp, uvp, UNI_VC_REJECT_CALL, + (struct unisig_msg *) 0); + if (*errp) + goto failed; + + return(CALL_CLEARED); + +failed: + /* + * On error, free the VCCB and return CALL_FAILED + */ + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + DEQUEUE(uvp, struct unisig_vccb, uv_sigelem, usp->us_vccq); + (void) unisig_free((struct vccb *)uvp); + return(CALL_FAILED); +} + + +/* + * Abort a UNISIG ATM Connection + * + * All (non-user) requests to abort a previously open VC connection (via + * the atm_abort_connection function), which is running over an + * interface attached to the UNISIG signalling manager, are handled here. + * The VCC owner will be notified of the request, in order to initiate + * termination of the connection. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * + * Returns: + * 0 connection release was successful + * errno connection release failed - reason indicated + * + */ +static int +unisig_abort(vcp) + struct vccb *vcp; +{ + + ATM_DEBUG1("unisig_abort: vcp=0x%x\n", (int)vcp); + + /* + * Only abort once + */ + if (vcp->vc_ustate == VCCU_ABORT) { + return (EALREADY); + } + + /* + * Cancel any timer that might be running + */ + UNISIG_VC_CANCEL(vcp); + + /* + * Set immediate timer to schedule connection termination + */ + vcp->vc_ustate = VCCU_ABORT; + UNISIG_VC_TIMER(vcp, 0); + + return (0); +} + + +/* + * Free UNISIG ATM connection resources + * + * All service user requests to free the resources of a closed VCC + * connection (via the atm_free_connection function), which is running + * over an interface attached to the UNISIG signalling manager, are + *handled here. + * + * Function will be called at splnet. + * + * Arguments: + * vcp pointer to connection's VC control block + * + * Returns: + * 0 connection free was successful + * errno connection free failed - reason indicated + * + */ +int +unisig_free(vcp) + struct vccb *vcp; +{ + struct atm_pif *pip = vcp->vc_pif; + struct unisig *usp = (struct unisig *)pip->pif_siginst; + + ATM_DEBUG1("unisig_free: vcp = 0x%x\n", vcp); + + /* + * Make sure VCC has been closed + */ + if ((vcp->vc_ustate != VCCU_CLOSED && + vcp->vc_ustate != VCCU_ABORT) || + vcp->vc_sstate != UNI_FREE) { + ATM_DEBUG2("unisig_free: bad state, sstate=%d, ustate=%d\n", + vcp->vc_sstate, vcp->vc_ustate); + return(EEXIST); + } + + /* + * Remove VCCB from protocol queue + */ + DEQUEUE(vcp, struct vccb, vc_sigelem, usp->us_vccq); + + /* + * Free VCCB storage + */ + vcp->vc_ustate = VCCU_NULL; + vcp->vc_sstate = UNI_NULL; + atm_free((caddr_t)vcp); + + /* + * If we're detaching and this was the last VCC queued, + * get rid of the protocol instance + */ + if ((usp->us_state == UNISIG_DETACH) && + (Q_HEAD(usp->us_vccq, struct vccb) == NULL)) { + struct sigmgr *smp = pip->pif_sigmgr; + int s = splimp(); + + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + (void) splx(s); + + UNLINK((struct siginst *)usp, struct siginst, + smp->sm_prinst, si_next); + KM_FREE(usp, sizeof(struct unisig), M_DEVBUF); + } + + return (0); +} + + +/* + * UNISIG IOCTL support + * + * Function will be called at splnet. + * + * Arguments: + * code PF_ATM sub-operation code + * data pointer to code specific parameter data area + * arg1 pointer to code specific argument + * + * Returns: + * 0 request procesed + * errno error processing request - reason indicated + * + */ +static int +unisig_ioctl(code, data, arg1) + int code; + caddr_t data; + caddr_t arg1; +{ + struct atmdelreq *adp; + struct atminfreq *aip; + struct atmsetreq *asp; + struct unisig *usp; + struct unisig_vccb *uvp; + struct air_vcc_rsp rsp; + struct atm_pif *pip; + Atm_connection *cop; + u_int vpi, vci; + int err = 0, buf_len, i; + caddr_t buf_addr; + + ATM_DEBUG1("unisig_ioctl: code=%d\n", code); + + switch (code) { + + case AIOCS_DEL_PVC: + case AIOCS_DEL_SVC: + /* + * Delete a VCC + */ + adp = (struct atmdelreq *)data; + usp = (struct unisig *)arg1; + + /* + * Don't let a user close the UNISIG signalling VC + */ + vpi = adp->adr_pvc_vpi; + vci = adp->adr_pvc_vci; + if ((vpi == UNISIG_SIG_VPI && vci == UNISIG_SIG_VCI)) + return(EINVAL); + + /* + * Find requested VCC + */ + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; + uvp = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem)) { + if ((uvp->uv_vpi == vpi) && (uvp->uv_vci == vci)) + break; + } + if (uvp == NULL) + return (ENOENT); + + /* + * Check VCC type + */ + switch (code) { + case AIOCS_DEL_PVC: + if (!(uvp->uv_type & VCC_PVC)) { + return(EINVAL); + } + break; + case AIOCS_DEL_SVC: + if (!(uvp->uv_type & VCC_SVC)) { + return(EINVAL); + } + break; + } + + /* + * Schedule VCC termination + */ + unisig_set_cause_attr(&uvp->uv_connvc->cvc_attr, + T_ATM_CAUSE_UNSPECIFIED_NORMAL); + err = unisig_abort((struct vccb *)uvp); + break; + + case AIOCS_INF_VCC: + /* + * Return VCC information + */ + aip = (struct atminfreq *)data; + usp = (struct unisig *)arg1; + + buf_addr = aip->air_buf_addr; + buf_len = aip->air_buf_len; + + /* + * Loop through the VCC queue + */ + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; + uvp = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem)) { + /* + * Make sure there's room in the user's buffer + */ + if (buf_len < sizeof(rsp)) { + err = ENOSPC; + break; + } + + /* + * Fill out the response struct for the VCC + */ + (void) sprintf(rsp.avp_intf, "%s%d", + usp->us_pif->pif_name, + usp->us_pif->pif_unit); + rsp.avp_vpi = uvp->uv_vpi; + rsp.avp_vci = uvp->uv_vci; + rsp.avp_type = uvp->uv_type; + rsp.avp_aal = uvp->uv_connvc->cvc_attr.aal.type; + rsp.avp_sig_proto = uvp->uv_proto; + cop = uvp->uv_connvc->cvc_conn; + if (cop) + rsp.avp_encaps = cop->co_mpx; + else + rsp.avp_encaps = 0; + rsp.avp_state = uvp->uv_sstate; + if (uvp->uv_connvc->cvc_flags & CVCF_CALLER) { + rsp.avp_daddr = uvp->uv_connvc->cvc_attr.called.addr; + } else { + rsp.avp_daddr = uvp->uv_connvc->cvc_attr.calling.addr; + } + rsp.avp_dsubaddr.address_format = T_ATM_ABSENT; + rsp.avp_dsubaddr.address_length = 0; + rsp.avp_ipdus = uvp->uv_ipdus; + rsp.avp_opdus = uvp->uv_opdus; + rsp.avp_ibytes = uvp->uv_ibytes; + rsp.avp_obytes = uvp->uv_obytes; + rsp.avp_ierrors = uvp->uv_ierrors; + rsp.avp_oerrors = uvp->uv_oerrors; + rsp.avp_tstamp = uvp->uv_tstamp; + KM_ZERO(rsp.avp_owners, + sizeof(rsp.avp_owners)); + for (i = 0; cop && i < sizeof(rsp.avp_owners); + cop = cop->co_next, + i += T_ATM_APP_NAME_LEN+1) { + strncpy(&rsp.avp_owners[i], + cop->co_endpt->ep_getname(cop->co_toku), + T_ATM_APP_NAME_LEN); + } + + /* + * Copy the response into the user's buffer + */ + if (err = copyout((caddr_t)&rsp, buf_addr, + sizeof(rsp))) + break; + buf_addr += sizeof(rsp); + buf_len -= sizeof(rsp); + } + + /* + * Update the buffer pointer and length + */ + aip->air_buf_addr = buf_addr; + aip->air_buf_len = buf_len; + break; + + case AIOCS_INF_ARP: + case AIOCS_INF_ASV: + case AIOCS_SET_ASV: + /* + * Get ARP table information or get/set ARP server address + */ + err = uniarp_ioctl(code, data, arg1); + break; + + case AIOCS_SET_PRF: + /* + * Set NSAP prefix + */ + asp = (struct atmsetreq *)data; + usp = (struct unisig *)arg1; + pip = usp->us_pif; + if (usp->us_addr.address_format != T_ATM_ABSENT) { + if (KM_CMP(asp->asr_prf_pref, usp->us_addr.address, + sizeof(asp->asr_prf_pref)) != 0) + err = EALREADY; + break; + } + usp->us_addr.address_format = T_ATM_ENDSYS_ADDR; + usp->us_addr.address_length = sizeof(Atm_addr_nsap); + KM_COPY(&pip->pif_macaddr, + ((Atm_addr_nsap *)usp->us_addr.address)->aan_esi, + sizeof(pip->pif_macaddr)); + KM_COPY((caddr_t) asp->asr_prf_pref, + &((Atm_addr_nsap *)usp->us_addr.address)->aan_afi, + sizeof(asp->asr_prf_pref)); + log(LOG_INFO, "uni: set address %s on interface %s\n", + unisig_addr_print(&usp->us_addr), + asp->asr_prf_intf); + + /* + * Pass event to signalling manager state machine + */ + err = unisig_sigmgr_state(usp, UNISIG_SIGMGR_ADDR_SET, + (KBuffer *) NULL); + + /* + * Clean up if there was an error + */ + if (err) { + usp->us_addr.address_format = T_ATM_ABSENT; + usp->us_addr.address_length = 0; + break; + } + + /* + * Inform ARP code of new address + */ + uniarp_ifaddr((struct siginst *)usp); + break; + + default: + err = EOPNOTSUPP; + } + + return (err); +} diff --git a/sys/netatm/uni/unisig_mbuf.c b/sys/netatm/uni/unisig_mbuf.c new file mode 100644 index 0000000..84c78fe --- /dev/null +++ b/sys/netatm/uni/unisig_mbuf.c @@ -0,0 +1,485 @@ +/* + * + * =================================== + * 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: unisig_mbuf.c,v 1.6 1998/08/26 23:29:22 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message buffer handling routines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_mbuf.c,v 1.6 1998/08/26 23:29:22 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_mbuf.h> +#include <netatm/uni/unisig_msg.h> + + +/* + * Initialize a unisig formatting structure + * + * Arguments: + * usf pointer to a unisig formatting structure + * usp pointer to a unisig protocol instance + * buf pointer to a buffer chain (decode only) + * op operation code (encode or decode) + * headroom headroom to leave in first buffer + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_init(usf, usp, buf, op, headroom) + struct usfmt *usf; + struct unisig *usp; + KBuffer *buf; + int op; + int headroom; +{ + KBuffer *m; + + ATM_DEBUG3("usf_init: usf=0x%x, buf=0x%x, op=%d\n", + (int) usf, (int) buf, op); + + /* + * Check parameters + */ + if (!usf) + return(EINVAL); + + switch(op) { + + case USF_ENCODE: + /* + * Get a buffer + */ + KB_ALLOCPKT(m, USF_MIN_ALLOC, KB_F_NOWAIT, KB_T_DATA); + if (m == NULL) + return(ENOMEM); + KB_LEN(m) = 0; + if (headroom < KB_BFRLEN(m)) { + KB_HEADSET(m, headroom); + } + break; + + case USF_DECODE: + /* + * Verify buffer address + */ + if (!buf) + return(EINVAL); + m = buf; + break; + + default: + return(EINVAL); + } + + /* + * Save parameters in formatting structure + */ + usf->usf_m_addr = m; + usf->usf_m_base = m; + usf->usf_loc = 0; + usf->usf_op = op; + usf->usf_sig = usp; + + return(0); +} + + +/* + * Get or put the next byte of a signalling message + * + * Arguments: + * usf pointer to a unisig formatting structure + * c pointer to the byte to send from or receive into + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_byte(usf, c) + struct usfmt *usf; + u_char *c; +{ + u_char *mp; + KBuffer *m = usf->usf_m_addr, *m1; + int space; + + switch (usf->usf_op) { + + case USF_DECODE: + /* + * Make sure we're not past the end of the buffer + * (allowing for zero-length buffers) + */ + while (usf->usf_loc >= KB_LEN(m)) { + if (KB_NEXT(usf->usf_m_addr)) { + usf->usf_m_addr = m = KB_NEXT(usf->usf_m_addr); + usf->usf_loc = 0; + } else { + return(EMSGSIZE); + } + } + + /* + * Get the data from the buffer + */ + KB_DATASTART(m, mp, u_char *); + *c = mp[usf->usf_loc]; + usf->usf_loc++; + break; + + case USF_ENCODE: + /* + * If the current buffer is full, get another + */ + KB_TAILROOM(m, space); + if (space == 0) { + KB_ALLOC(m1, USF_MIN_ALLOC, KB_F_NOWAIT, KB_T_DATA); + if (m1 == NULL) + return(ENOMEM); + KB_LEN(m1) = 0; + KB_LINK(m1, m); + usf->usf_m_addr = m = m1; + usf->usf_loc = 0; + } + + /* + * Put the data into the buffer + */ + KB_DATASTART(m, mp, u_char *); + mp[usf->usf_loc] = *c; + KB_TAILADJ(m, 1); + usf->usf_loc++; + break; + + default: + /* + * Invalid operation code + */ + return(EINVAL); + } + + return(0); + +} + +/* + * Get or put a short integer + * + * Arguments: + * usf pointer to a unisig formatting structure + * s pointer to a short to send from or receive into + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_short(usf, s) + struct usfmt *usf; + u_short *s; + +{ + int rc; + union { + u_short value; + u_char b[sizeof(u_short)]; + } tval; + + tval.value = 0; + if (usf->usf_op == USF_ENCODE) + tval.value = htons(*s); + + if (rc = usf_byte(usf, &tval.b[0])) + return(rc); + if (rc = usf_byte(usf, &tval.b[1])) + return(rc); + + if (usf->usf_op == USF_DECODE) + *s = ntohs(tval.value); + + return(0); +} + + +/* + * Get or put a 3-byte integer + * + * Arguments: + * usf pointer to a unisig formatting structure + * i pointer to an integer to send from or receive into + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_int3(usf, i) + struct usfmt *usf; + u_int *i; + +{ + int j, rc; + union { + u_int value; + u_char b[sizeof(u_int)]; + } tval; + + tval.value = 0; + + if (usf->usf_op == USF_ENCODE) + tval.value = htonl(*i); + + for (j=0; j<3; j++) { + rc = usf_byte(usf, &tval.b[j+sizeof(u_int)-3]); + if (rc) + return(rc); + } + + if (usf->usf_op == USF_DECODE) + *i = ntohl(tval.value); + + return(rc); +} + + +/* + * Get or put an integer + * + * Arguments: + * usf pointer to a unisig formatting structure + * i pointer to an integer to send from or receive into + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_int(usf, i) + struct usfmt *usf; + u_int *i; + +{ + int j, rc; + union { + u_int value; + u_char b[sizeof(u_int)]; + } tval; + + if (usf->usf_op == USF_ENCODE) + tval.value = htonl(*i); + + for (j=0; j<4; j++) { + rc = usf_byte(usf, &tval.b[j+sizeof(u_int)-4]); + if (rc) + return(rc); + } + + if (usf->usf_op == USF_DECODE) + *i = ntohl(tval.value); + + return(rc); +} + + +/* + * Get or put an extented field + * + * An extented field consists of a string of bytes. All but the last + * byte of the field has the high-order bit set to zero. When decoding, + * this routine will read bytes until either the input is exhausted or + * a byte with a high-order one is found. Whe encoding, it will take an + * unsigned integer and write until the highest-order one bit has been + * written. + * + * Arguments: + * usf pointer to a unisig formatting structure + * i pointer to an integer to send from or receive into + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_ext(usf, i) + struct usfmt *usf; + u_int *i; + +{ + int j, rc; + u_char c, buff[sizeof(u_int)+1]; + u_int val; + union { + u_int value; + u_char b[sizeof(u_int)]; + } tval; + + switch(usf->usf_op) { + + case USF_ENCODE: + val = *i; + j = 0; + while (val) { + tval.value = htonl(val); + buff[j] = tval.b[sizeof(u_int)-1] & UNI_IE_EXT_MASK; + val >>= 7; + j++; + } + j--; + buff[0] |= UNI_IE_EXT_BIT; + for (; j>=0; j--) { + rc = usf_byte(usf, &buff[j]); + if (rc) + return(rc); + } + break; + + case USF_DECODE: + c = 0; + val = 0; + while (!(c & UNI_IE_EXT_BIT)) { + rc = usf_byte(usf, &c); + if (rc) + return(rc); + val = (val << 7) + (c & UNI_IE_EXT_MASK); + } + *i = val; + break; + + default: + return(EINVAL); + } + + return(0); +} + + +/* + * Count the bytes remaining to be decoded + * + * Arguments: + * usf pointer to a unisig formatting structure + * + * Returns: + * int the number of bytes in the buffer chain remaining to + * be decoded + * + */ +int +usf_count(usf) + struct usfmt *usf; +{ + int count; + KBuffer *m = usf->usf_m_addr; + + /* + * Return zero if we're not decoding + */ + if (usf->usf_op != USF_DECODE) + return (0); + + /* + * Calculate the length of data remaining in the current buffer + */ + count = KB_LEN(m) - usf->usf_loc; + + /* + * Loop through any remaining buffers, adding in their lengths + */ + while (KB_NEXT(m)) { + m = KB_NEXT(m); + count += KB_LEN(m); + } + + return(count); + +} + + +/* + * Get or put the next byte of a signalling message and return + * the byte's buffer address + * + * Arguments: + * usf pointer to a unisig formatting structure + * c pointer to the byte to send from or receive into + * bp address to store the byte's buffer address + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +usf_byte_mark(usf, c, bp) + struct usfmt *usf; + u_char *c; + u_char **bp; +{ + u_char *mp; + int rc; + + /* + * First, get/put the data byte + */ + rc = usf_byte(usf, c); + if (rc) { + + /* + * Error encountered + */ + *bp = NULL; + return (rc); + } + + /* + * Now return the buffer address of that byte + */ + KB_DATASTART(usf->usf_m_addr, mp, u_char *); + *bp = &mp[usf->usf_loc - 1]; + + return (0); +} + diff --git a/sys/netatm/uni/unisig_mbuf.h b/sys/netatm/uni/unisig_mbuf.h new file mode 100644 index 0000000..f394047 --- /dev/null +++ b/sys/netatm/uni/unisig_mbuf.h @@ -0,0 +1,58 @@ +/* + * + * =================================== + * 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: unisig_mbuf.h,v 1.5 1998/08/26 23:29:22 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message buffer formats + * + */ + +#ifndef _UNI_SIG_MBUF_H +#define _UNI_SIG_MBUF_H + + +/* + * Structure for message encoding/decoding information. + */ +struct usfmt { + KBuffer *usf_m_addr; /* Current buffer */ + KBuffer *usf_m_base; /* First buffer in chain */ + int usf_loc; /* Offset in current buffer */ + int usf_op; /* Operation (see below) */ + struct unisig *usf_sig; /* UNI signalling instance */ +}; + +#define USF_ENCODE 1 +#define USF_DECODE 2 + +#define USF_MIN_ALLOC MHLEN /* Minimum encoding buffer size */ + +#endif /* _UNI_SIG_MBUF_H */ diff --git a/sys/netatm/uni/unisig_msg.c b/sys/netatm/uni/unisig_msg.c new file mode 100644 index 0000000..22bf469 --- /dev/null +++ b/sys/netatm/uni/unisig_msg.c @@ -0,0 +1,1002 @@ +/* + * + * =================================== + * 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: unisig_msg.c,v 1.10 1998/08/26 23:29:22 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message handling module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_msg.c,v 1.10 1998/08/26 23:29:22 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_mbuf.h> +#include <netatm/uni/unisig_print.h> + + +/* + * Local functions + */ +static void unisig_rcv_restart __P((struct unisig *, struct unisig_msg *)); +static void unisig_rcv_setup __P((struct unisig *, struct unisig_msg *)); + + +/* + * Local variables + */ +#ifdef DIAGNOSTIC +static int unisig_print_msg = 0; +#endif + + +/* + * Set a Cause IE based on information in an ATM attribute block + * + * Arguments: + * iep pointer to a cause IE + * msg pointer to message + * cause cause code for the error + * + * Returns: + * 0 message sent OK + * errno error encountered + * + */ +void +unisig_cause_from_attr(iep, aap) + struct ie_generic *iep; + Atm_attributes *aap; +{ + /* + * Copy cause info from attribute block to IE + */ + iep->ie_ident = UNI_IE_CAUS; + iep->ie_coding = aap->cause.v.coding_standard; + iep->ie_caus_loc = aap->cause.v.location; + iep->ie_caus_cause = aap->cause.v.cause_value; +} + + +/* + * Set a Cause IE based on information in a UNI signalling message + * + * Arguments: + * iep pointer to a cause IE + * msg pointer to message + * cause cause code for the error + * + * Returns: + * 0 message sent OK + * errno error encountered + * + */ +void +unisig_cause_from_msg(iep, msg, cause) + struct ie_generic *iep; + struct unisig_msg *msg; + int cause; +{ + struct ie_generic *ie1; + int i; + + /* + * Fill out the cause IE fixed fields + */ + iep->ie_ident = UNI_IE_CAUS; + iep->ie_caus_loc = UNI_IE_CAUS_LOC_USER; + iep->ie_caus_cause = cause; + + /* + * Set diagnostics if indicated + */ + switch(cause) { + case UNI_IE_CAUS_IECONTENT: + iep->ie_caus_diag_len = 0; + for (i = 0, ie1 = msg->msg_ie_err; + ie1 && i < UNI_IE_CAUS_MAX_ID; + ie1 = ie1->ie_next) { + if (ie1->ie_err_cause == UNI_IE_CAUS_IECONTENT) { + iep->ie_caus_diagnostic[i] = + ie1->ie_ident; + iep->ie_caus_diag_len++; + i++; + } + } + break; + case UNI_IE_CAUS_REJECT: + iep->ie_caus_diag_len = 2; + iep->ie_caus_diagnostic[0] = UNI_IE_EXT_BIT + + (UNI_IE_CAUS_RR_USER << UNI_IE_CAUS_RR_SHIFT) + + UNI_IE_CAUS_RC_TRANS; + iep->ie_caus_diagnostic[1] = 0; + break; + case UNI_IE_CAUS_MISSING: + iep->ie_caus_diag_len = 0; + for (i = 0, ie1 = msg->msg_ie_err; + ie1 && i < UNI_IE_CAUS_MAX_ID; + ie1 = ie1->ie_next) { + if (ie1->ie_err_cause == UNI_IE_CAUS_MISSING) { + iep->ie_caus_diagnostic[i] = + ie1->ie_ident; + iep->ie_caus_diag_len++; + i++; + } + } + } +} + + +/* + * Send a UNISIG signalling message + * + * Called to send a Q.2931 message. This routine encodes the message + * and hands it to SSCF for transmission. + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * msg pointer to message + * + * Returns: + * 0 message sent OK + * errno error encountered + * + */ +int +unisig_send_msg(usp, msg) + struct unisig *usp; + struct unisig_msg *msg; +{ + int err = 0; + struct usfmt usf; + + ATM_DEBUG2("unisig_send_msg: msg=0x%x, type=%d\n", msg, + msg->msg_type); + + /* + * Make sure the network is up + */ + if (usp->us_state != UNISIG_ACTIVE) + return(ENETDOWN); + +#ifdef DIAGNOSTIC + /* + * Print the message we're sending. + */ + if (unisig_print_msg) + usp_print_msg(msg, UNISIG_MSG_OUT); +#endif + + /* + * Convert message to network order + */ + err = usf_init(&usf, usp, (KBuffer *) 0, USF_ENCODE, + usp->us_headout); + if (err) + return(err); + + err = usf_enc_msg(&usf, msg); + if (err) { + ATM_DEBUG1("unisig_send_msg: encode failed with %d\n", + err); + KB_FREEALL(usf.usf_m_base); + return(EIO); + } + +#ifdef DIAGNOSTIC + /* + * Print the converted message + */ + if (unisig_print_msg > 1) + unisig_print_mbuf(usf.usf_m_base); +#endif + + /* + * Send the message + */ + err = atm_cm_saal_data(usp->us_conn, usf.usf_m_base); + if (err) + KB_FREEALL(usf.usf_m_base); + + return(err); +} + + +/* + * Send a SETUP request + * + * Build and send a Q.2931 SETUP message. + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * uvp pointer to VCCB for which the request is being sent + * + * Returns: + * none + * + */ +int +unisig_send_setup(usp, uvp) + struct unisig *usp; + struct unisig_vccb *uvp; +{ + int err = 0; + struct unisig_msg *setup; + Atm_attributes *ap = &uvp->uv_connvc->cvc_attr; + + ATM_DEBUG1("unisig_send_setup: uvp=0x%x\n", (int) uvp); + + /* + * Make sure required connection attriutes are set + */ + if (ap->aal.tag != T_ATM_PRESENT || + ap->traffic.tag != T_ATM_PRESENT || + ap->bearer.tag != T_ATM_PRESENT || + ap->called.tag != T_ATM_PRESENT || + ap->qos.tag != T_ATM_PRESENT) { + err = EINVAL; + setup = NULL; + goto done; + } + + /* + * Get memory for a SETUP message + */ + setup = (struct unisig_msg *)atm_allocate(&unisig_msgpool); + if (setup == NULL) { + err = ENOMEM; + goto done; + } + + /* + * Fill in the SETUP message + */ + if (!uvp->uv_call_ref) + uvp->uv_call_ref = unisig_alloc_call_ref(usp); + setup->msg_call_ref = uvp->uv_call_ref; + setup->msg_type = UNI_MSG_SETU; + + /* + * Set IEs from connection attributes + */ + err = unisig_set_attrs(usp, setup, ap); + if (err) + goto done; + + /* + * Attach a Calling Party Number IE if the user didn't + * specify one in the attribute block + */ + if (ap->calling.tag != T_ATM_PRESENT) { + setup->msg_ie_cgad = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (setup->msg_ie_cgad == NULL) { + err = ENOMEM; + goto done; + } + setup->msg_ie_cgad->ie_ident = UNI_IE_CGAD; + ATM_ADDR_COPY(&usp->us_addr, + &setup->msg_ie_cgad->ie_cgad_addr); + ATM_ADDR_SEL_COPY(&usp->us_addr, + uvp->uv_nif ? uvp->uv_nif->nif_sel : 0, + &setup->msg_ie_cgad->ie_cgad_addr); + } + + /* + * Send the SETUP message + */ + err = unisig_send_msg(usp, setup); + +done: + if (setup) + unisig_free_msg(setup); + + return(err); +} + + +/* + * Send a RELEASE message + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * uvp pointer to VCCB for which the RELEASE is being sent + * msg pointer to UNI signalling message that the RELEASE + * responds to (may be NULL) + * cause the reason for the RELEASE; a value of + * T_ATM_ABSENT indicates that the cause code is + * in the VCC's ATM attributes block + * + * Returns: + * none + * + */ +int +unisig_send_release(usp, uvp, msg, cause) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; + int cause; +{ + int err = 0; + struct unisig_msg *rls_msg; + struct ie_generic *cause_ie; + + ATM_DEBUG2("unisig_send_release: usp=0x%x, uvp=0x%x\n", + (int) usp, (int) uvp); + + /* + * Get memory for a RELEASE message + */ + rls_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (rls_msg == NULL) { + return(ENOMEM); + } + cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (cause_ie == NULL) { + atm_free(rls_msg); + return(ENOMEM); + } + + /* + * Fill in the RELEASE message + */ + rls_msg->msg_call_ref = uvp->uv_call_ref; + rls_msg->msg_type = UNI_MSG_RLSE; + rls_msg->msg_type_flag = 0; + rls_msg->msg_type_action = 0; + rls_msg->msg_ie_caus = cause_ie; + + /* + * Fill out the cause IE + */ + cause_ie->ie_ident = UNI_IE_CAUS; + if (cause == T_ATM_ABSENT) { + unisig_cause_from_attr(cause_ie, + &uvp->uv_connvc->cvc_attr); + } else { + cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; + unisig_cause_from_msg(cause_ie, msg, cause); + } + + /* + * Send the RELEASE + */ + err = unisig_send_msg(usp, rls_msg); + unisig_free_msg(rls_msg); + + return(err); +} + + +/* + * Send a RELEASE COMPLETE message + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * uvp pointer to VCCB for which the RELEASE is being sent. + * NULL indicates that a VCCB wasn't found for a call + * reference value. + * msg pointer to the message which triggered the send + * cause the cause code for the message; a value of + * T_ATM_ABSENT indicates that the cause code is + * in the VCC's ATM attributes block + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +unisig_send_release_complete(usp, uvp, msg, cause) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; + int cause; +{ + int err = 0; + struct unisig_msg *rls_cmp; + struct ie_generic *cause_ie; + + ATM_DEBUG4("unisig_send_release_complete usp=0x%x, uvp=0x%x, msg=0x%x, cause=%d\n", + (int) usp, (int) uvp, (int) msg, cause); + + /* + * Get memory for a RELEASE COMPLETE message + */ + rls_cmp = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (rls_cmp == NULL) { + return(ENOMEM); + } + cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (cause_ie == NULL) { + atm_free(rls_cmp); + return(ENOMEM); + } + + /* + * Fill in the RELEASE COMPLETE message + */ + if (uvp) { + rls_cmp->msg_call_ref = uvp->uv_call_ref; + } else if (msg) { + rls_cmp->msg_call_ref = EXTRACT_CREF(msg->msg_call_ref); + } else { + rls_cmp->msg_call_ref = UNI_MSG_CALL_REF_GLOBAL; + } + rls_cmp->msg_type = UNI_MSG_RLSC; + rls_cmp->msg_type_flag = 0; + rls_cmp->msg_type_action = 0; + rls_cmp->msg_ie_caus = cause_ie; + + /* + * Fill out the cause IE + */ + cause_ie->ie_ident = UNI_IE_CAUS; + if (cause == T_ATM_ABSENT) { + unisig_cause_from_attr(cause_ie, + &uvp->uv_connvc->cvc_attr); + } else { + cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; + unisig_cause_from_msg(cause_ie, msg, cause); + } + + /* + * Send the RELEASE COMPLETE + */ + err = unisig_send_msg(usp, rls_cmp); + unisig_free_msg(rls_cmp); + + return(err); +} + + +/* + * Send a STATUS message + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * uvp pointer to VCCB for which the STATUS is being sent. + * NULL indicates that a VCCB wasn't found for a call + * reference value. + * msg pointer to the message which triggered the send + * cause the cause code to include in the message + * + * Returns: + * none + * + */ +int +unisig_send_status(usp, uvp, msg, cause) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; + int cause; +{ + int err = 0, i; + struct unisig_msg *stat_msg; + struct ie_generic *cause_ie, *clst_ie, *iep; + + ATM_DEBUG4("unisig_send_status: usp=0x%x, uvp=0x%x, msg=0x%x, cause=%d\n", + (int) usp, (int) uvp, (int) msg, cause); + + /* + * Get memory for a STATUS message + */ + stat_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (stat_msg == NULL) { + return(ENOMEM); + } + cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (cause_ie == NULL) { + atm_free(stat_msg); + return(ENOMEM); + } + clst_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (clst_ie == NULL) { + atm_free(stat_msg); + atm_free(cause_ie); + return(ENOMEM); + } + + /* + * Fill in the STATUS message + */ + if (uvp) { + stat_msg->msg_call_ref = uvp->uv_call_ref; + } else if (msg) { + stat_msg->msg_call_ref = + EXTRACT_CREF(msg->msg_call_ref); + } else { + stat_msg->msg_call_ref = UNI_MSG_CALL_REF_GLOBAL; + } + stat_msg->msg_type = UNI_MSG_STAT; + stat_msg->msg_type_flag = 0; + stat_msg->msg_type_action = 0; + stat_msg->msg_ie_clst = clst_ie; + stat_msg->msg_ie_caus = cause_ie; + + /* + * Fill out the call state IE + */ + clst_ie->ie_ident = UNI_IE_CLST; + clst_ie->ie_coding = 0; + clst_ie->ie_flag = 0; + clst_ie->ie_action = 0; + if (uvp) { + clst_ie->ie_clst_state = uvp->uv_sstate; + } else { + clst_ie->ie_clst_state = UNI_NULL; + } + + /* + * Fill out the cause IE + */ + cause_ie->ie_ident = UNI_IE_CAUS; + cause_ie->ie_coding = 0; + cause_ie->ie_flag = 0; + cause_ie->ie_action = 0; + cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; + cause_ie->ie_caus_cause = cause; + switch (cause) { + case UNI_IE_CAUS_MTEXIST: + case UNI_IE_CAUS_STATE: + if (msg) { + cause_ie->ie_caus_diagnostic[0] = msg->msg_type; + } + break; + case UNI_IE_CAUS_MISSING: + case UNI_IE_CAUS_IECONTENT: + case UNI_IE_CAUS_IEEXIST: + for (i=0, iep=msg->msg_ie_err; + iep && i<UNI_MSG_IE_CNT; + i++, iep = iep->ie_next) { + if (iep->ie_err_cause == cause) { + cause_ie->ie_caus_diagnostic[i] = + iep->ie_ident; + } + } + } + + /* + * Send the STATUS message + */ + err = unisig_send_msg(usp, stat_msg); + unisig_free_msg(stat_msg); + + return(err); +} + + +/* + * Process a RESTART message + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * msg pointer to the RESTART message + * + * Returns: + * none + * + */ +static void +unisig_rcv_restart(usp, msg) + struct unisig *usp; + struct unisig_msg *msg; +{ + struct unisig_vccb *uvp, *uvnext; + struct unisig_msg *rsta_msg; + int s; + + ATM_DEBUG2("unisig_rcv_restart: usp=0x%x, msg=0x%x\n", + usp, msg); + + /* + * Check what class of VCCs we're supposed to restart + */ + if (msg->msg_ie_rsti->ie_rsti_class == UNI_IE_RSTI_IND_VC) { + /* + * Just restart the indicated VCC + */ + if (msg->msg_ie_cnid) { + uvp = unisig_find_vpvc(usp, + msg->msg_ie_cnid->ie_cnid_vpci, + msg->msg_ie_cnid->ie_cnid_vci, + 0); + if (uvp && uvp->uv_type & VCC_SVC) { + (void) unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + } + } + } else { + /* + * Restart all VCCs + */ + s = splnet(); + for (uvp=Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; + uvp=uvnext) { + uvnext = Q_NEXT(uvp, struct unisig_vccb, + uv_sigelem); + if (uvp->uv_type & VCC_SVC) { + (void) unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + } + } + (void) splx(s); + } + + /* + * Get memory for a RESTART ACKNOWLEDGE message + */ + rsta_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (rsta_msg == NULL) { + return; + } + + /* + * Fill out the message + */ + rsta_msg->msg_call_ref = EXTRACT_CREF(msg->msg_call_ref); + rsta_msg->msg_type = UNI_MSG_RSTA; + rsta_msg->msg_type_flag = 0; + rsta_msg->msg_type_action = 0; + rsta_msg->msg_ie_rsti = msg->msg_ie_rsti; + if (msg->msg_ie_cnid) { + rsta_msg->msg_ie_cnid = msg->msg_ie_cnid; + } + + /* + * Send the message + */ + (void) unisig_send_msg(usp, rsta_msg); + rsta_msg->msg_ie_rsti = NULL; + rsta_msg->msg_ie_cnid = NULL; + unisig_free_msg(rsta_msg); + + return; +} + + +/* + * Process a SETUP message + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * msg pointer to the SETUP message + * + * Returns: + * none + * + */ +static void +unisig_rcv_setup(usp, msg) + struct unisig *usp; + struct unisig_msg *msg; +{ + struct unisig_vccb *uvp = NULL; + struct ie_generic *iep; + + ATM_DEBUG2("unisig_rcv_setup: usp=0x%x, msg=0x%x\n", usp, msg); + + /* + * If we already have a VCC with the call reference, + * ignore the SETUP message + */ + uvp = unisig_find_conn(usp, EXTRACT_CREF(msg->msg_call_ref)); + if (uvp) + return; + + /* + * If the call reference flag is incorrectly set, + * ignore the SETUP message + */ + if (msg->msg_call_ref & UNI_MSG_CALL_REF_RMT) + return; + + /* + * If there are missing mandatory IEs, send a + * RELEASE COMPLETE message and ignore the SETUP + */ + for (iep = msg->msg_ie_err; iep; iep = iep->ie_next) { + if (iep->ie_err_cause == UNI_IE_CAUS_MISSING) { + (void) unisig_send_release_complete(usp, + uvp, msg, UNI_IE_CAUS_MISSING); + return; + } + } + + /* + * If there are mandatory IEs with invalid content, send a + * RELEASE COMPLETE message and ignore the SETUP + */ + for (iep = msg->msg_ie_err; iep; iep = iep->ie_next) { + if (iep->ie_err_cause == UNI_IE_CAUS_IECONTENT) { + (void) unisig_send_release_complete(usp, + uvp, msg, + UNI_IE_CAUS_IECONTENT); + return; + } + } + + /* + * Get a new VCCB for the connection + */ + uvp = (struct unisig_vccb *)atm_allocate(&unisig_vcpool); + if (uvp == NULL) { + return; + } + + /* + * Put the VCCB on the UNISIG queue + */ + ENQUEUE(uvp, struct unisig_vccb, uv_sigelem, usp->us_vccq); + + /* + * Set the state and call reference value + */ + uvp->uv_sstate = UNI_NULL; + uvp->uv_call_ref = EXTRACT_CREF(msg->msg_call_ref); + + /* + * Pass the VCCB and message to the VC state machine + */ + (void) unisig_vc_state(usp, uvp, UNI_VC_SETUP_MSG, msg); + + /* + * If the VCCB state is NULL, the open failed and the + * VCCB should be released + */ + if (uvp->uv_sstate == UNI_NULL) { + DEQUEUE(uvp, struct unisig_vccb, uv_sigelem, + usp->us_vccq); + atm_free(uvp); + } + + return; +} + + +/* + * Process a UNISIG signalling message + * + * Called when a UNISIG message is received. The message is decoded + * and passed to the UNISIG state machine. Unrecognized and + * unexpected messages are logged. + * + * Arguments: + * usp pointer to UNISIG protocol instance block + * m pointer to a buffer chain containing the UNISIG message + * + * Returns: + * none + * + */ +int +unisig_rcv_msg(usp, m) + struct unisig *usp; + KBuffer *m; +{ + int err; + u_int cref; + struct usfmt usf; + struct unisig_msg *msg = 0; + struct unisig_vccb *uvp = 0; + struct ie_generic *iep; + + ATM_DEBUG2("unisig_rcv_msg: bfr=0x%x, len=%d\n", (int)m, KB_LEN(m)); + +#ifdef NOTDEF + unisig_print_mbuf(m); +#endif + + /* + * Get storage for the message + */ + msg = (struct unisig_msg *)atm_allocate(&unisig_msgpool); + if (msg == NULL) { + err = ENOMEM; + goto done; + } + + /* + * Convert the message from network order to internal format + */ + err = usf_init(&usf, usp, m, USF_DECODE, 0); + if (err) { + if (err == EINVAL) + panic("unisig_rcv_msg: invalid parameter\n"); + ATM_DEBUG1("unisig_rcv_msg: decode init failed with %d\n", + err); + goto done; + } + + err = usf_dec_msg(&usf, msg); + if (err) { + ATM_DEBUG1("unisig_rcv_msg: decode failed with %d\n", + err); + goto done; + } + +#ifdef DIAGNOSTIC + /* + * Debug--print some information about the message + */ + if (unisig_print_msg) + usp_print_msg(msg, UNISIG_MSG_IN); +#endif + + /* + * Get the call reference value + */ + cref = EXTRACT_CREF(msg->msg_call_ref); + + /* + * Any message with the global call reference value except + * RESTART, RESTART ACK, or STATUS is in error + */ + if (GLOBAL_CREF(cref) && + msg->msg_type != UNI_MSG_RSTR && + msg->msg_type != UNI_MSG_RSTA && + msg->msg_type != UNI_MSG_STAT) { + /* + * Send STATUS message indicating the error + */ + err = unisig_send_status(usp, (struct unisig_vccb *) 0, + msg, UNI_IE_CAUS_CREF); + goto done; + } + + /* + * Check for missing mandatory IEs. Checks for SETUP, + * RELEASE, and RELEASE COMPLETE are handled elsewhere. + */ + if (msg->msg_type != UNI_MSG_SETU && + msg->msg_type != UNI_MSG_RLSE && + msg->msg_type != UNI_MSG_RLSC) { + for (iep = msg->msg_ie_err; iep; iep = iep->ie_next) { + if (iep->ie_err_cause == UNI_IE_CAUS_MISSING) { + err = unisig_send_status(usp, + uvp, msg, + UNI_IE_CAUS_MISSING); + goto done; + } + } + } + + /* + * Find the VCCB associated with the message + */ + uvp = unisig_find_conn(usp, cref); + + /* + * Process the message based on its type + */ + switch(msg->msg_type) { + case UNI_MSG_CALP: + (void) unisig_vc_state(usp, uvp, + UNI_VC_CALLP_MSG, msg); + break; + case UNI_MSG_CONN: + (void) unisig_vc_state(usp, uvp, + UNI_VC_CONNECT_MSG, msg); + break; + case UNI_MSG_CACK: + (void) unisig_vc_state(usp, uvp, + UNI_VC_CNCTACK_MSG, msg); + break; + case UNI_MSG_SETU: + unisig_rcv_setup(usp, msg); + break; + case UNI_MSG_RLSE: + (void) unisig_vc_state(usp, uvp, + UNI_VC_RELEASE_MSG, msg); + break; + case UNI_MSG_RLSC: + /* + * Ignore a RELEASE COMPLETE with an unrecognized + * call reference value + */ + if (uvp) { + (void) unisig_vc_state(usp, uvp, + UNI_VC_RLSCMP_MSG, msg); + } + break; + case UNI_MSG_RSTR: + unisig_rcv_restart(usp, msg); + break; + case UNI_MSG_RSTA: + break; + case UNI_MSG_STAT: + (void) unisig_vc_state(usp, uvp, + UNI_VC_STATUS_MSG, msg); + break; + case UNI_MSG_SENQ: + (void) unisig_vc_state(usp, uvp, + UNI_VC_STATUSENQ_MSG, msg); + break; + case UNI_MSG_ADDP: + (void) unisig_vc_state(usp, uvp, + UNI_VC_ADDP_MSG, msg); + break; + case UNI_MSG_ADPA: + (void) unisig_vc_state(usp, uvp, + UNI_VC_ADDPACK_MSG, msg); + break; + case UNI_MSG_ADPR: + (void) unisig_vc_state(usp, uvp, + UNI_VC_ADDPREJ_MSG, msg); + break; + case UNI_MSG_DRPP: + (void) unisig_vc_state(usp, uvp, + UNI_VC_DROP_MSG, msg); + break; + case UNI_MSG_DRPA: + (void) unisig_vc_state(usp, uvp, + UNI_VC_DROPACK_MSG, msg); + break; + default: + /* + * Message size didn't match size received + */ + err = unisig_send_status(usp, uvp, msg, + UNI_IE_CAUS_MTEXIST); + } + +done: + /* + * Handle message errors that require a response + */ + switch(err) { + case EMSGSIZE: + /* + * Message size didn't match size received + */ + err = unisig_send_status(usp, uvp, msg, + UNI_IE_CAUS_LEN); + break; + } + + /* + * Free the incoming message (both buffer and internal format) + * if necessary. + */ + if (msg) + unisig_free_msg(msg); + if (m) + KB_FREEALL(m); + + return (err); +} diff --git a/sys/netatm/uni/unisig_msg.h b/sys/netatm/uni/unisig_msg.h new file mode 100644 index 0000000..4be0144 --- /dev/null +++ b/sys/netatm/uni/unisig_msg.h @@ -0,0 +1,953 @@ +/* + * + * =================================== + * 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: unisig_msg.h,v 1.8 1998/08/26 23:29:23 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Message formatting blocks + * + */ + +#ifndef _UNI_SIG_MSG_H +#define _UNI_SIG_MSG_H + +#define UNI_MSG_DISC_Q93B 0x09 +#define UNI_MSG_MIN_LEN 9 + +/* + * Values for Q.2931 message type. + */ +#define UNI_MSG_CALP 0x02 +#define UNI_MSG_CONN 0x07 +#define UNI_MSG_CACK 0x0F +#define UNI_MSG_SETU 0x05 +#define UNI_MSG_RLSE 0x4D +#define UNI_MSG_RLSC 0x5A +#define UNI_MSG_RSTR 0x46 +#define UNI_MSG_RSTA 0x4E +#define UNI_MSG_STAT 0x7D +#define UNI_MSG_SENQ 0x75 +#define UNI_MSG_ADDP 0x80 +#define UNI_MSG_ADPA 0x81 +#define UNI_MSG_ADPR 0x82 +#define UNI_MSG_DRPP 0x83 +#define UNI_MSG_DRPA 0x84 + + +/* + * Values for information element identifier. + */ +#define UNI_IE_CAUS 0x08 +#define UNI_IE_CLST 0x14 +#define UNI_IE_EPRF 0x54 +#define UNI_IE_EPST 0x55 +#define UNI_IE_AALP 0x58 +#define UNI_IE_CLRT 0x59 +#define UNI_IE_CNID 0x5A +#define UNI_IE_QOSP 0x5C +#define UNI_IE_BHLI 0x5D +#define UNI_IE_BBCP 0x5E +#define UNI_IE_BLLI 0x5F +#define UNI_IE_BLSH 0x60 +#define UNI_IE_BNSH 0x61 +#define UNI_IE_BSDC 0x62 +#define UNI_IE_BRPI 0x63 +#define UNI_IE_CGAD 0x6C +#define UNI_IE_CGSA 0x6D +#define UNI_IE_CDAD 0x70 +#define UNI_IE_CDSA 0x71 +#define UNI_IE_TRNT 0x78 +#define UNI_IE_RSTI 0x79 + +/* + * Masks for information element extension in bit 8 + */ +#define UNI_IE_EXT_BIT 0x80 +#define UNI_IE_EXT_MASK 0x7F + + +/* + * Signalling message in internal format. + */ +#define UNI_MSG_IE_CNT 22 + +struct unisig_msg { + u_int msg_call_ref; + u_char msg_type; + u_char msg_type_flag; + u_char msg_type_action; + int msg_length; + struct ie_generic *msg_ie_vec[UNI_MSG_IE_CNT]; +}; + +#define UNI_MSG_CALL_REF_RMT 0x800000 +#define UNI_MSG_CALL_REF_MASK 0x7FFFFF +#define UNI_MSG_CALL_REF_GLOBAL 0 +#define UNI_MSG_CALL_REF_DUMMY 0x7FFFFF + +#define EXTRACT_CREF(x) \ + ((x) & UNI_MSG_CALL_REF_RMT ? (x) & UNI_MSG_CALL_REF_MASK : (x) | UNI_MSG_CALL_REF_RMT) +#define GLOBAL_CREF(x) ((x) & UNI_MSG_CALL_REF_MASK == UNI_MSG_CALL_REF_GLOBAL) +#define DUMMY_CREF(x) ((x) & UNI_MSG_CALL_REF_MASK == UNI_MSG_CALL_REF_DUMMY) + +#define UNI_MSG_TYPE_FLAG_MASK 1 +#define UNI_MSG_TYPE_FLAG_SHIFT 4 + +#define UNI_MSG_TYPE_ACT_CLEAR 0 +#define UNI_MSG_TYPE_ACT_DISC 1 +#define UNI_MSG_TYPE_ACT_RPRT 2 +#define UNI_MSG_TYPE_ACT_RSVD 3 +#define UNI_MSG_TYPE_ACT_MASK 3 + +#define UNI_MSG_IE_AALP 0 +#define UNI_MSG_IE_CLRT 1 +#define UNI_MSG_IE_BBCP 2 +#define UNI_MSG_IE_BHLI 3 +#define UNI_MSG_IE_BLLI 4 +#define UNI_MSG_IE_CLST 5 +#define UNI_MSG_IE_CDAD 6 +#define UNI_MSG_IE_CDSA 7 +#define UNI_MSG_IE_CGAD 8 +#define UNI_MSG_IE_CGSA 9 +#define UNI_MSG_IE_CAUS 10 +#define UNI_MSG_IE_CNID 11 +#define UNI_MSG_IE_QOSP 12 +#define UNI_MSG_IE_BRPI 13 +#define UNI_MSG_IE_RSTI 14 +#define UNI_MSG_IE_BLSH 15 +#define UNI_MSG_IE_BNSH 16 +#define UNI_MSG_IE_BSDC 17 +#define UNI_MSG_IE_TRNT 18 +#define UNI_MSG_IE_EPRF 19 +#define UNI_MSG_IE_EPST 20 +#define UNI_MSG_IE_ERR 21 + +#define msg_ie_aalp msg_ie_vec[UNI_MSG_IE_AALP] +#define msg_ie_clrt msg_ie_vec[UNI_MSG_IE_CLRT] +#define msg_ie_bbcp msg_ie_vec[UNI_MSG_IE_BBCP] +#define msg_ie_bhli msg_ie_vec[UNI_MSG_IE_BHLI] +#define msg_ie_blli msg_ie_vec[UNI_MSG_IE_BLLI] +#define msg_ie_clst msg_ie_vec[UNI_MSG_IE_CLST] +#define msg_ie_cdad msg_ie_vec[UNI_MSG_IE_CDAD] +#define msg_ie_cdsa msg_ie_vec[UNI_MSG_IE_CDSA] +#define msg_ie_cgad msg_ie_vec[UNI_MSG_IE_CGAD] +#define msg_ie_cgsa msg_ie_vec[UNI_MSG_IE_CGSA] +#define msg_ie_caus msg_ie_vec[UNI_MSG_IE_CAUS] +#define msg_ie_cnid msg_ie_vec[UNI_MSG_IE_CNID] +#define msg_ie_qosp msg_ie_vec[UNI_MSG_IE_QOSP] +#define msg_ie_brpi msg_ie_vec[UNI_MSG_IE_BRPI] +#define msg_ie_rsti msg_ie_vec[UNI_MSG_IE_RSTI] +#define msg_ie_blsh msg_ie_vec[UNI_MSG_IE_BLSH] +#define msg_ie_bnsh msg_ie_vec[UNI_MSG_IE_BNSH] +#define msg_ie_bsdc msg_ie_vec[UNI_MSG_IE_BSDC] +#define msg_ie_trnt msg_ie_vec[UNI_MSG_IE_TRNT] +#define msg_ie_eprf msg_ie_vec[UNI_MSG_IE_EPRF] +#define msg_ie_epst msg_ie_vec[UNI_MSG_IE_EPST] +#define msg_ie_err msg_ie_vec[UNI_MSG_IE_ERR] + + +/* + * Information element header. + */ +struct ie_hdr { + u_char ie_hdr_ident; + u_char ie_hdr_coding; + u_char ie_hdr_flag; + u_char ie_hdr_action; + int ie_hdr_length; + int ie_hdr_err_cause; + struct ie_generic *ie_hdr_next; +}; + +#define UNI_IE_HDR_LEN 4 + +#define UNI_IE_CODE_CCITT 0 +#define UNI_IE_CODE_STD 3 +#define UNI_IE_CODE_MASK 3 +#define UNI_IE_CODE_SHIFT 5 + +#define UNI_IE_FLAG_MASK 1 +#define UNI_IE_FLAG_SHIFT 4 + +#define UNI_IE_ACT_CLEAR 0 +#define UNI_IE_ACT_DIS 1 +#define UNI_IE_ACT_RPRT 2 +#define UNI_IE_ACT_DMSGIGN 5 +#define UNI_IE_ACT_DMSGRPRT 6 +#define UNI_IE_ACT_MASK 7 + + +/* + * ATM AAL parameters information element in internal format. + */ +struct ie_aalp { + int8_t ie_aal_type; + union { + struct aal_type_1_parm { + u_char subtype; + u_char cbr_rate; + u_short multiplier; + u_char clock_recovery; + u_char error_correction; + u_char struct_data_tran; + u_char partial_cells; + } type_1; + struct aal_type_4_parm { + int32_t fwd_max_sdu; + int32_t bkwd_max_sdu; + int32_t mid_range; + u_char mode; + u_char sscs_type; + } type_4; + struct aal_type_5_parm { + int32_t fwd_max_sdu; + int32_t bkwd_max_sdu; + u_char mode; + u_char sscs_type; + } type_5; + struct user_aal_type { + u_char aal_info[4]; + } type_user; + } aal_u; +}; + +#define UNI_IE_AALP_AT_AAL1 1 +#define UNI_IE_AALP_AT_AAL3 3 +#define UNI_IE_AALP_AT_AAL5 5 +#define UNI_IE_AALP_AT_AALU 16 + +#define UNI_IE_AALP_A1_ST_NULL 0 +#define UNI_IE_AALP_A1_ST_VCE 1 +#define UNI_IE_AALP_A1_ST_SCE 2 +#define UNI_IE_AALP_A1_ST_ACE 3 +#define UNI_IE_AALP_A1_ST_HQA 4 +#define UNI_IE_AALP_A1_ST_VID 5 + +#define UNI_IE_AALP_A1_CB_64 1 +#define UNI_IE_AALP_A1_CB_DS1 4 +#define UNI_IE_AALP_A1_CB_DS2 5 +#define UNI_IE_AALP_A1_CB_32064 6 +#define UNI_IE_AALP_A1_CB_DS3 7 +#define UNI_IE_AALP_A1_CB_97728 8 +#define UNI_IE_AALP_A1_CB_E1 16 +#define UNI_IE_AALP_A1_CB_E2 17 +#define UNI_IE_AALP_A1_CB_E3 18 +#define UNI_IE_AALP_A1_CB_139264 19 +#define UNI_IE_AALP_A1_CB_N64 64 + +#define UNI_IE_AALP_A1_CR_NULL 0 +#define UNI_IE_AALP_A1_CR_SRTS 1 +#define UNI_IE_AALP_A1_CR_ACR 2 + +#define UNI_IE_AALP_A1_EC_NULL 0 +#define UNI_IE_AALP_A1_EC_FEC 1 + +#define UNI_IE_AALP_A1_SD_NULL 0 +#define UNI_IE_AALP_A1_SD_SDT 1 + +#define UNI_IE_AALP_A3_R_MASK 1023 +#define UNI_IE_AALP_A3_R_SHIFT 16 + +#define UNI_IE_AALP_A5_M_MSG 1 +#define UNI_IE_AALP_A5_M_STR 2 + +#define UNI_IE_AALP_A5_ST_NULL 0 +#define UNI_IE_AALP_A5_ST_AO 1 +#define UNI_IE_AALP_A5_ST_NAO 2 +#define UNI_IE_AALP_A5_ST_FR 4 + + +/* + * ATM user cell rate information element in internal format. + */ +struct ie_clrt { + int32_t ie_fwd_peak; + int32_t ie_bkwd_peak; + int32_t ie_fwd_peak_01; + int32_t ie_bkwd_peak_01; + int32_t ie_fwd_sust; + int32_t ie_bkwd_sust; + int32_t ie_fwd_sust_01; + int32_t ie_bkwd_sust_01; + int32_t ie_fwd_burst; + int32_t ie_bkwd_burst; + int32_t ie_fwd_burst_01; + int32_t ie_bkwd_burst_01; + int8_t ie_best_effort; + int8_t ie_tm_options; +}; + +#define UNI_IE_CLRT_FWD_PEAK_ID 130 +#define UNI_IE_CLRT_BKWD_PEAK_ID 131 +#define UNI_IE_CLRT_FWD_PEAK_01_ID 132 +#define UNI_IE_CLRT_BKWD_PEAK_01_ID 133 +#define UNI_IE_CLRT_FWD_SUST_ID 136 +#define UNI_IE_CLRT_BKWD_SUST_ID 137 +#define UNI_IE_CLRT_FWD_SUST_01_ID 144 +#define UNI_IE_CLRT_BKWD_SUST_01_ID 145 +#define UNI_IE_CLRT_FWD_BURST_ID 160 +#define UNI_IE_CLRT_BKWD_BURST_ID 161 +#define UNI_IE_CLRT_FWD_BURST_01_ID 176 +#define UNI_IE_CLRT_BKWD_BURST_01_ID 177 +#define UNI_IE_CLRT_BEST_EFFORT_ID 190 +#define UNI_IE_CLRT_TM_OPTIONS_ID 191 + +#define UNI_IE_CLRT_TM_FWD_TAG 0x01 +#define UNI_IE_CLRT_TM_BKWD_TAG 0x02 + + +/* + * Broadband bearer capability information element in internal format. + */ +struct ie_bbcp { + int8_t ie_bearer_class; + int8_t ie_traffic_type; + int8_t ie_timing_req; + int8_t ie_clipping; + int8_t ie_conn_config; +}; + + +#define UNI_IE_BBCP_BC_BCOB_A 1 +#define UNI_IE_BBCP_BC_BCOB_C 3 +#define UNI_IE_BBCP_BC_BCOB_X 16 +#define UNI_IE_BBCP_BC_MASK 0x1F + +#define UNI_IE_BBCP_TT_NIND 0 +#define UNI_IE_BBCP_TT_CBR 1 +#define UNI_IE_BBCP_TT_VBR 2 +#define UNI_IE_BBCP_TT_MASK 3 +#define UNI_IE_BBCP_TT_SHIFT 2 + +#define UNI_IE_BBCP_TR_NIND 0 +#define UNI_IE_BBCP_TR_EER 1 +#define UNI_IE_BBCP_TR_EENR 2 +#define UNI_IE_BBCP_TR_RSVD 3 +#define UNI_IE_BBCP_TR_MASK 3 + +#define UNI_IE_BBCP_SC_NSUS 0 +#define UNI_IE_BBCP_SC_SUS 1 +#define UNI_IE_BBCP_SC_MASK 3 +#define UNI_IE_BBCP_SC_SHIFT 5 + +#define UNI_IE_BBCP_CC_PP 0 +#define UNI_IE_BBCP_CC_PM 1 +#define UNI_IE_BBCP_CC_MASK 3 + + +/* + * Broadband high layer information information element in internal + * format. + */ +struct ie_bhli { + int8_t ie_type; + u_char ie_info[8]; +}; + +#define UNI_IE_BHLI_TYPE_ISO 0 +#define UNI_IE_BHLI_TYPE_USER 1 +#define UNI_IE_BHLI_TYPE_HLP 2 +#define UNI_IE_BHLI_TYPE_VSA 3 + +#define UNI_IE_BHLI_HLP_LEN 4 +#define UNI_IE_BHLI_VSA_LEN 7 + + +/* + * Broadband low-layer information information element in internal + * format. + */ +struct ie_blli { + int8_t ie_l1_id; + int8_t ie_l2_id; + int8_t ie_l2_mode; + int8_t ie_l2_q933_use; + int8_t ie_l2_window; + int8_t ie_l2_user_proto; + int8_t ie_l3_id; + int8_t ie_l3_mode; + int8_t ie_l3_packet_size; + int8_t ie_l3_window; + int8_t ie_l3_user_proto; + int16_t ie_l3_ipi; + int8_t ie_l3_snap_id; + u_char ie_l3_oui[3]; + u_char ie_l3_pid[2]; +}; + +#define UNI_IE_BLLI_L1_ID 1 +#define UNI_IE_BLLI_L2_ID 2 +#define UNI_IE_BLLI_L3_ID 3 +#define UNI_IE_BLLI_LID_MASK 3 +#define UNI_IE_BLLI_LID_SHIFT 5 +#define UNI_IE_BLLI_LP_MASK 31 + +#define UNI_IE_BLLI_L2P_ISO1745 1 +#define UNI_IE_BLLI_L2P_Q921 2 +#define UNI_IE_BLLI_L2P_X25L 6 +#define UNI_IE_BLLI_L2P_X25M 7 +#define UNI_IE_BLLI_L2P_LAPB 8 +#define UNI_IE_BLLI_L2P_HDLC1 9 +#define UNI_IE_BLLI_L2P_HDLC2 10 +#define UNI_IE_BLLI_L2P_HDLC3 11 +#define UNI_IE_BLLI_L2P_LLC 12 +#define UNI_IE_BLLI_L2P_X75 13 +#define UNI_IE_BLLI_L2P_Q922 14 +#define UNI_IE_BLLI_L2P_USER 16 +#define UNI_IE_BLLI_L2P_ISO7776 17 + +#define UNI_IE_BLLI_L2MODE_NORM 1 +#define UNI_IE_BLLI_L2MODE_EXT 2 +#define UNI_IE_BLLI_L2MODE_SHIFT 5 +#define UNI_IE_BLLI_L2MODE_MASK 3 + +#define UNI_IE_BLLI_Q933_ALT 0 + +#define UNI_IE_BLLI_L3P_X25 6 +#define UNI_IE_BLLI_L3P_ISO8208 7 +#define UNI_IE_BLLI_L3P_ISO8878 8 +#define UNI_IE_BLLI_L3P_ISO8473 9 +#define UNI_IE_BLLI_L3P_T70 10 +#define UNI_IE_BLLI_L3P_ISO9577 11 +#define UNI_IE_BLLI_L3P_USER 16 + +#define UNI_IE_BLLI_L3MODE_NORM 1 +#define UNI_IE_BLLI_L3MODE_EXT 2 +#define UNI_IE_BLLI_L3MODE_SHIFT 5 +#define UNI_IE_BLLI_L3MODE_MASK 3 + +#define UNI_IE_BLLI_L3PS_16 4 +#define UNI_IE_BLLI_L3PS_32 5 +#define UNI_IE_BLLI_L3PS_64 6 +#define UNI_IE_BLLI_L3PS_128 7 +#define UNI_IE_BLLI_L3PS_256 8 +#define UNI_IE_BLLI_L3PS_512 9 +#define UNI_IE_BLLI_L3PS_1024 10 +#define UNI_IE_BLLI_L3PS_2048 11 +#define UNI_IE_BLLI_L3PS_4096 12 +#define UNI_IE_BLLI_L3PS_MASK 15 + +#define UNI_IE_BLLI_L3IPI_SHIFT 6 +#define UNI_IE_BLLI_L3IPI_SNAP 0x80 + + +/* + * Call state information element in internal format. + */ +struct ie_clst { + int8_t ie_state; +}; + +#define UNI_IE_CLST_STATE_U0 0 +#define UNI_IE_CLST_STATE_U1 1 +#define UNI_IE_CLST_STATE_U3 3 +#define UNI_IE_CLST_STATE_U6 6 +#define UNI_IE_CLST_STATE_U8 8 +#define UNI_IE_CLST_STATE_U9 9 +#define UNI_IE_CLST_STATE_U10 10 +#define UNI_IE_CLST_STATE_U11 11 +#define UNI_IE_CLST_STATE_U12 12 + +#define UNI_IE_CLST_STATE_N0 0 +#define UNI_IE_CLST_STATE_N1 1 +#define UNI_IE_CLST_STATE_N3 3 +#define UNI_IE_CLST_STATE_N6 6 +#define UNI_IE_CLST_STATE_N8 8 +#define UNI_IE_CLST_STATE_N9 9 +#define UNI_IE_CLST_STATE_N10 10 +#define UNI_IE_CLST_STATE_N11 11 +#define UNI_IE_CLST_STATE_N12 12 + +#define UNI_IE_CLST_GLBL_REST0 0x00 +#define UNI_IE_CLST_GLBL_REST1 0x3d +#define UNI_IE_CLST_GLBL_REST2 0x3e + +#define UNI_IE_CLST_STATE_MASK 0x3f + + +/* + * Called party number information element in internal format. + */ +struct ie_cdad { + int8_t ie_type; + int8_t ie_plan; + Atm_addr ie_addr; +}; + +#define UNI_IE_CDAD_TYPE_UNK 0 +#define UNI_IE_CDAD_TYPE_INTL 1 +#define UNI_IE_CDAD_TYPE_MASK 7 +#define UNI_IE_CDAD_TYPE_SHIFT 4 + +#define UNI_IE_CDAD_PLAN_E164 1 +#define UNI_IE_CDAD_PLAN_NSAP 2 +#define UNI_IE_CDAD_PLAN_MASK 15 + + +/* + * Called party subaddress information element in internal format. + */ +struct ie_cdsa { + Atm_addr ie_addr; +}; + +#define UNI_IE_CDSA_TYPE_NSAP 0 +#define UNI_IE_CDSA_TYPE_AESA 1 +#define UNI_IE_CDSA_TYPE_MASK 7 +#define UNI_IE_CDSA_TYPE_SHIFT 4 + + +/* + * Calling party number information element in internal format. + */ +struct ie_cgad { + int8_t ie_type; + int8_t ie_plan; + int8_t ie_pres_ind; + int8_t ie_screen_ind; + Atm_addr ie_addr; +}; + +#define UNI_IE_CGAD_TYPE_UNK 0 +#define UNI_IE_CGAD_TYPE_INTL 1 +#define UNI_IE_CGAD_TYPE_MASK 7 +#define UNI_IE_CGAD_TYPE_SHIFT 4 + +#define UNI_IE_CGAD_PLAN_E164 1 +#define UNI_IE_CGAD_PLAN_NSAP 2 +#define UNI_IE_CGAD_PLAN_MASK 15 + +#define UNI_IE_CGAD_PRES_ALLOW 0 +#define UNI_IE_CGAD_PRES_RSTR 1 +#define UNI_IE_CGAD_PRES_NNA 2 +#define UNI_IE_CGAD_PRES_RSVD 3 +#define UNI_IE_CGAD_PRES_MASK 3 +#define UNI_IE_CGAD_PRES_SHIFT 5 + +#define UNI_IE_CGAD_SCR_UNS 0 +#define UNI_IE_CGAD_SCR_UVP 1 +#define UNI_IE_CGAD_SCR_UVF 2 +#define UNI_IE_CGAD_SCR_NET 3 +#define UNI_IE_CGAD_SCR_MASK 3 + + +/* + * Calling party subaddress information element in internal format. + */ +struct ie_cgsa { + Atm_addr ie_addr; +}; + +#define UNI_IE_CGSA_TYPE_NSAP 0 +#define UNI_IE_CGSA_TYPE_AESA 1 +#define UNI_IE_CGSA_TYPE_MASK 7 +#define UNI_IE_CGSA_TYPE_SHIFT 4 + + +/* + * Cause information element in internal format. + */ +#define UNI_IE_CAUS_MAX_ID 24 +#define UNI_IE_CAUS_MAX_QOS_SUB 24 +struct ie_caus { + int8_t ie_loc; + int8_t ie_cause; + int8_t ie_diag_len; + u_int8_t ie_diagnostic[24]; +}; + +#define UNI_IE_CAUS_LOC_USER 0 +#define UNI_IE_CAUS_LOC_PRI_LCL 1 +#define UNI_IE_CAUS_LOC_PUB_LCL 2 +#define UNI_IE_CAUS_LOC_TRANSIT 3 +#define UNI_IE_CAUS_LOC_PUB_RMT 4 +#define UNI_IE_CAUS_LOC_PRI_RMT 5 +#define UNI_IE_CAUS_LOC_INTL 7 +#define UNI_IE_CAUS_LOC_BEYOND 10 +#define UNI_IE_CAUS_LOC_MASK 15 + +#define UNI_IE_CAUS_UN_NS_SHIFT 3 +#define UNI_IE_CAUS_UN_NS_MASK 1 + +#define UNI_IE_CAUS_UN_NA_SHIFT 2 +#define UNI_IE_CAUS_UN_NA_MASK 1 + +#define UNI_IE_CAUS_UN_CAU_MASK 3 + +#define UNI_IE_CAUS_RR_USER 0 +#define UNI_IE_CAUS_RR_IE 1 +#define UNI_IE_CAUS_RR_INSUFF 2 +#define UNI_IE_CAUS_RR_SHIFT 2 +#define UNI_IE_CAUS_RR_MASK 31 + +#define UNI_IE_CAUS_RC_UNK 0 +#define UNI_IE_CAUS_RC_PERM 1 +#define UNI_IE_CAUS_RC_TRANS 2 +#define UNI_IE_CAUS_RC_MASK 3 + +/* + * Cause codes from UNI 3.0, section 5.4.5.15 + */ +#define UNI_IE_CAUS_UNO 1 /* Unallocated number */ +#define UNI_IE_CAUS_NOTROUTE 2 /* No route to transit net */ +#define UNI_IE_CAUS_NODROUTE 3 /* No route to destination */ +#define UNI_IE_CAUS_BAD_VCC 10 /* VPI/VCI unacceptable */ +#define UNI_IE_CAUS_NORM 16 /* Normal call clearing */ +#define UNI_IE_CAUS_BUSY 17 /* User busy */ +#define UNI_IE_CAUS_NORSP 18 /* No user responding */ +#define UNI_IE_CAUS_REJECT 21 /* Call rejected */ +#define UNI_IE_CAUS_CHANGED 22 /* Number changed */ +#define UNI_IE_CAUS_CLIR 23 /* User rejects CLIR */ +#define UNI_IE_CAUS_DORDER 27 /* Dest out of order */ +#define UNI_IE_CAUS_INVNO 28 /* Invalid number format */ +#define UNI_IE_CAUS_SENQ 30 /* Rsp to Status Enquiry */ +#define UNI_IE_CAUS_NORM_UNSP 31 /* Normal, unspecified */ +#define UNI_IE_CAUS_NA_VCC 35 /* VCC not available */ +#define UNI_IE_CAUS_ASSIGN_VCC 36 /* VPCI/VCI assignment failure */ +#define UNI_IE_CAUS_NORDER 38 /* Network out of order */ +#define UNI_IE_CAUS_TEMP 41 /* Temporary failure */ +#define UNI_IE_CAUS_DISCARD 43 /* Access info discarded */ +#define UNI_IE_CAUS_NO_VCC 45 /* No VPI/VCI available */ +#define UNI_IE_CAUS_UNAVAIL 47 /* Resource unavailable */ +#define UNI_IE_CAUS_NO_QOS 49 /* QoS unavailable */ +#define UNI_IE_CAUS_NO_CR 51 /* User cell rate not avail */ +#define UNI_IE_CAUS_NO_BC 57 /* Bearer capability not auth */ +#define UNI_IE_CAUS_NA_BC 58 /* Bearer capability n/a */ +#define UNI_IE_CAUS_SERVICE 63 /* Service or opt not avail */ +#define UNI_IE_CAUS_NI_BC 65 /* Bearer cap not implemented */ +#define UNI_IE_CAUS_COMB 73 /* Unsupported combination */ +#define UNI_IE_CAUS_CREF 81 /* Invalid call reference */ +#define UNI_IE_CAUS_CEXIST 82 /* Channel does not exist */ +#define UNI_IE_CAUS_IDEST 88 /* Incompatible destination */ +#define UNI_IE_CAUS_ENDPT 89 /* Invalid endpoint reference */ +#define UNI_IE_CAUS_TRNET 91 /* Invalid transit net */ +#define UNI_IE_CAUS_APPEND 92 /* Too many pending add party */ +#define UNI_IE_CAUS_UAAL 93 /* AAL parms can't be supp */ +#define UNI_IE_CAUS_MISSING 96 /* Mandatory IE missing */ +#define UNI_IE_CAUS_MTEXIST 97 /* Message type nonexistent */ +#define UNI_IE_CAUS_IEEXIST 99 /* IE type nonexistent */ +#define UNI_IE_CAUS_IECONTENT 100 /* IE content invalid */ +#define UNI_IE_CAUS_STATE 101 /* Message incomp with state */ +#define UNI_IE_CAUS_TIMER 102 /* Recovery on timer expire */ +#define UNI_IE_CAUS_LEN 104 /* Incorrect message length */ +#define UNI_IE_CAUS_PROTO 111 /* Protocol error */ + + +/* + * Connection identifier information element in internal format. + */ +struct ie_cnid { + int8_t ie_vp_sig; + int8_t ie_pref_excl; + u_short ie_vpci; + u_short ie_vci; +}; + +#define UNI_IE_CNID_VPSIG_MASK 3 +#define UNI_IE_CNID_VPSIG_SHIFT 3 +#define UNI_IE_CNID_PREX_MASK 7 + +#define UNI_IE_CNID_MIN_VCI 32 + + +/* + * Quality of service parameter information element in internal format. + */ +struct ie_qosp { + int8_t ie_fwd_class; + int8_t ie_bkwd_class; +}; + +#define UNI_IE_QOSP_FWD_CLASS_0 0 +#define UNI_IE_QOSP_FWD_CLASS_1 1 +#define UNI_IE_QOSP_FWD_CLASS_2 2 +#define UNI_IE_QOSP_FWD_CLASS_3 3 +#define UNI_IE_QOSP_FWD_CLASS_4 4 + +#define UNI_IE_QOSP_BKWD_CLASS_0 0 +#define UNI_IE_QOSP_BKWD_CLASS_1 1 +#define UNI_IE_QOSP_BKWD_CLASS_2 2 +#define UNI_IE_QOSP_BKWD_CLASS_3 3 +#define UNI_IE_QOSP_BKWD_CLASS_4 4 + + +/* + * Broadband repeat indicator information element in internal format. + */ +struct ie_brpi { + int8_t ie_ind; +}; + +#define UNI_IE_BRPI_PRI_LIST 2 +#define UNI_IE_BRPI_IND_MASK 15 + + +/* + * Restart indicator information element in internal format. + */ +struct ie_rsti { + int8_t ie_class; +}; + +#define UNI_IE_RSTI_IND_VC 0 +#define UNI_IE_RSTI_ALL_VC 2 +#define UNI_IE_RSTI_CLASS_MASK 3 + + +/* + * Broadband locking shift information element in internal format. + */ +struct ie_blsh { + int8_t ie_dummy; +}; + + +/* + * Broadband non-locking shift information element in internal format. + */ +struct ie_bnsh { + int8_t ie_dummy; +}; + + +/* + * Broadband sending complete information element in internal format. + */ +struct ie_bsdc { + int8_t ie_ind; +}; + +#define UNI_IE_BSDC_IND 0x21 + + +/* + * Transit net selection information element in internal format. + */ +struct ie_trnt { + int8_t ie_id_type; + int8_t ie_id_plan; + u_char ie_id_len; + u_char ie_id[4]; +}; + +#define UNI_IE_TRNT_IDT_MASK 7 +#define UNI_IE_TRNT_IDT_SHIFT 4 +#define UNI_IE_TRNT_IDP_MASK 15 + +#define UNI_IE_TRNT_IDT_NATL 2 +#define UNI_IE_TRNT_IDP_CIC 1 + + +/* + * Endpoint reference information element in internal format. + */ +struct ie_eprf { + int8_t ie_type; + int16_t ie_id; +}; + +#define UNI_IE_EPRF_LDI 0 + + +/* + * Endpoint state information element in internal format. + */ +struct ie_epst { + int8_t ie_state; +}; + +#define UNI_IE_EPST_NULL 0 +#define UNI_IE_EPST_API 1 +#define UNI_IE_EPST_APR 6 +#define UNI_IE_EPST_DPI 11 +#define UNI_IE_EPST_DPR 12 +#define UNI_IE_EPST_ACTIVE 10 +#define UNI_IE_EPST_STATE_MASK 0x3F + + +/* + * Generic information element + */ +struct ie_generic { + struct ie_hdr ie_hdr; + union { + struct ie_aalp ie_aalp; + struct ie_clrt ie_clrt; + struct ie_bbcp ie_bbcp; + struct ie_bhli ie_bhli; + struct ie_blli ie_blli; + struct ie_clst ie_clst; + struct ie_cdad ie_cdad; + struct ie_cdsa ie_cdsa; + struct ie_cgad ie_cgad; + struct ie_cgsa ie_cgsa; + struct ie_caus ie_caus; + struct ie_cnid ie_cnid; + struct ie_qosp ie_qosp; + struct ie_brpi ie_brpi; + struct ie_rsti ie_rsti; + struct ie_blsh ie_blsh; + struct ie_bnsh ie_bnsh; + struct ie_bsdc ie_bsdc; + struct ie_trnt ie_trnt; + struct ie_eprf ie_eprf; + struct ie_epst ie_epst; + } ie_u; +}; + +#define ie_ident ie_hdr.ie_hdr_ident +#define ie_coding ie_hdr.ie_hdr_coding +#define ie_flag ie_hdr.ie_hdr_flag +#define ie_action ie_hdr.ie_hdr_action +#define ie_length ie_hdr.ie_hdr_length +#define ie_err_cause ie_hdr.ie_hdr_err_cause +#define ie_next ie_hdr.ie_hdr_next + +#define ie_aalp_aal_type ie_u.ie_aalp.ie_aal_type +#define ie_aalp_1_subtype ie_u.ie_aalp.aal_u.type_1.subtype +#define ie_aalp_1_cbr_rate ie_u.ie_aalp.aal_u.type_1.cbr_rate +#define ie_aalp_1_multiplier ie_u.ie_aalp.aal_u.type_1.multiplier +#define ie_aalp_1_clock_recovery ie_u.ie_aalp.aal_u.type_1.clock_recovery +#define ie_aalp_1_error_correction ie_u.ie_aalp.aal_u.type_1.error_correction +#define ie_aalp_1_struct_data_tran ie_u.ie_aalp.aal_u.type_1.struct_data_tran +#define ie_aalp_1_partial_cells ie_u.ie_aalp.aal_u.type_1.partial_cells + +#define ie_aalp_4_fwd_max_sdu ie_u.ie_aalp.aal_u.type_4.fwd_max_sdu +#define ie_aalp_4_bkwd_max_sdu ie_u.ie_aalp.aal_u.type_4.bkwd_max_sdu +#define ie_aalp_4_mid_range ie_u.ie_aalp.aal_u.type_4.mid_range +#define ie_aalp_4_mode ie_u.ie_aalp.aal_u.type_4.mode +#define ie_aalp_4_sscs_type ie_u.ie_aalp.aal_u.type_4.sscs_type + +#define ie_aalp_5_fwd_max_sdu ie_u.ie_aalp.aal_u.type_5.fwd_max_sdu +#define ie_aalp_5_bkwd_max_sdu ie_u.ie_aalp.aal_u.type_5.bkwd_max_sdu +#define ie_aalp_5_mode ie_u.ie_aalp.aal_u.type_5.mode +#define ie_aalp_5_sscs_type ie_u.ie_aalp.aal_u.type_5.sscs_type +#define ie_aalp_user_info ie_u.ie_aalp.aal_u.type_user.aal_info + +#define ie_clrt_fwd_peak ie_u.ie_clrt.ie_fwd_peak +#define ie_clrt_bkwd_peak ie_u.ie_clrt.ie_bkwd_peak +#define ie_clrt_fwd_peak_01 ie_u.ie_clrt.ie_fwd_peak_01 +#define ie_clrt_bkwd_peak_01 ie_u.ie_clrt.ie_bkwd_peak_01 +#define ie_clrt_fwd_sust ie_u.ie_clrt.ie_fwd_sust +#define ie_clrt_bkwd_sust ie_u.ie_clrt.ie_bkwd_sust +#define ie_clrt_fwd_sust_01 ie_u.ie_clrt.ie_fwd_sust_01 +#define ie_clrt_bkwd_sust_01 ie_u.ie_clrt.ie_bkwd_sust_01 +#define ie_clrt_fwd_burst ie_u.ie_clrt.ie_fwd_burst +#define ie_clrt_bkwd_burst ie_u.ie_clrt.ie_bkwd_burst +#define ie_clrt_fwd_burst_01 ie_u.ie_clrt.ie_fwd_burst_01 +#define ie_clrt_bkwd_burst_01 ie_u.ie_clrt.ie_bkwd_burst_01 +#define ie_clrt_best_effort ie_u.ie_clrt.ie_best_effort +#define ie_clrt_tm_options ie_u.ie_clrt.ie_tm_options + +#define ie_bbcp_bearer_class ie_u.ie_bbcp.ie_bearer_class +#define ie_bbcp_traffic_type ie_u.ie_bbcp.ie_traffic_type +#define ie_bbcp_timing_req ie_u.ie_bbcp.ie_timing_req +#define ie_bbcp_clipping ie_u.ie_bbcp.ie_clipping +#define ie_bbcp_conn_config ie_u.ie_bbcp.ie_conn_config + +#define ie_bhli_type ie_u.ie_bhli.ie_type +#define ie_bhli_info ie_u.ie_bhli.ie_info + +#define ie_blli_l1_id ie_u.ie_blli.ie_l1_id +#define ie_blli_l2_id ie_u.ie_blli.ie_l2_id +#define ie_blli_l2_mode ie_u.ie_blli.ie_l2_mode +#define ie_blli_l2_q933_use ie_u.ie_blli.ie_l2_q933_use +#define ie_blli_l2_window ie_u.ie_blli.ie_l2_window +#define ie_blli_l2_user_proto ie_u.ie_blli.ie_l2_user_proto +#define ie_blli_l3_id ie_u.ie_blli.ie_l3_id +#define ie_blli_l3_mode ie_u.ie_blli.ie_l3_mode +#define ie_blli_l3_packet_size ie_u.ie_blli.ie_l3_packet_size +#define ie_blli_l3_window ie_u.ie_blli.ie_l3_window +#define ie_blli_l3_user_proto ie_u.ie_blli.ie_l3_user_proto +#define ie_blli_l3_ipi ie_u.ie_blli.ie_l3_ipi +#define ie_blli_l3_snap_id ie_u.ie_blli.ie_l3_snap_id +#define ie_blli_l3_oui ie_u.ie_blli.ie_l3_oui +#define ie_blli_l3_pid ie_u.ie_blli.ie_l3_pid + +#define ie_clst_state ie_u.ie_clst.ie_state + +#define ie_cdad_type ie_u.ie_cdad.ie_type +#define ie_cdad_plan ie_u.ie_cdad.ie_plan +#define ie_cdad_addr ie_u.ie_cdad.ie_addr + +#define ie_cdsa_addr ie_u.ie_cdsa.ie_addr + +#define ie_cgad_type ie_u.ie_cgad.ie_type +#define ie_cgad_plan ie_u.ie_cgad.ie_plan +#define ie_cgad_pres_ind ie_u.ie_cgad.ie_pres_ind +#define ie_cgad_screen_ind ie_u.ie_cgad.ie_screen_ind +#define ie_cgad_addr ie_u.ie_cgad.ie_addr + +#define ie_cgsa_addr ie_u.ie_cgsa.ie_addr + +#define ie_caus_loc ie_u.ie_caus.ie_loc +#define ie_caus_cause ie_u.ie_caus.ie_cause +#define ie_caus_diag_len ie_u.ie_caus.ie_diag_len +#define ie_caus_diagnostic ie_u.ie_caus.ie_diagnostic + +#define ie_cnid_vp_sig ie_u.ie_cnid.ie_vp_sig +#define ie_cnid_pref_excl ie_u.ie_cnid.ie_pref_excl +#define ie_cnid_vpci ie_u.ie_cnid.ie_vpci +#define ie_cnid_vci ie_u.ie_cnid.ie_vci + +#define ie_qosp_fwd_class ie_u.ie_qosp.ie_fwd_class +#define ie_qosp_bkwd_class ie_u.ie_qosp.ie_bkwd_class + +#define ie_brpi_ind ie_u.ie_brpi.ie_ind + +#define ie_rsti_class ie_u.ie_rsti.ie_class + +#define ie_bsdc_ind ie_u.ie_bsdc.ie_ind + +#define ie_trnt_id_type ie_u.ie_trnt.ie_id_type +#define ie_trnt_id_plan ie_u.ie_trnt.ie_id_plan +#define ie_trnt_id_len ie_u.ie_trnt.ie_id_len +#define ie_trnt_id ie_u.ie_trnt.ie_id + +#define ie_eprf_type ie_u.ie_eprf.ie_type +#define ie_eprf_id ie_u.ie_eprf.ie_id + +#define ie_epst_state ie_u.ie_epst.ie_state + +/* + * Macro to add an IE to the end of a list of IEs + */ +#define MSG_IE_ADD(m, i, ind) \ + if (m->msg_ie_vec[ind]) { \ + struct ie_generic *_iep = msg->msg_ie_vec[ind]; \ + while (_iep->ie_next) { \ + _iep = _iep->ie_next; \ + } \ + _iep->ie_next = i; \ + } else { \ + m->msg_ie_vec[ind] = i; \ + } + +#endif /* _UNI_SIG_MSG_H */ diff --git a/sys/netatm/uni/unisig_print.c b/sys/netatm/uni/unisig_print.c new file mode 100644 index 0000000..f335ce4 --- /dev/null +++ b/sys/netatm/uni/unisig_print.c @@ -0,0 +1,877 @@ +/* + * + * =================================== + * 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: unisig_print.c,v 1.9 1998/08/26 23:29:23 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Print Q.2931 messages + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_print.c,v 1.9 1998/08/26 23:29:23 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_print.h> + + +/* + * Local declarations + */ +struct type_name { + char *name; + u_char type; +}; + + +/* + * Local functions + */ +static char * find_type __P((struct type_name *, u_char)); +static void usp_print_atm_addr __P((Atm_addr *)); +static void usp_print_ie __P((struct ie_generic *)); +static void usp_print_ie_aalp __P((struct ie_generic *)); +static void usp_print_ie_clrt __P((struct ie_generic *)); +static void usp_print_ie_bbcp __P((struct ie_generic *)); +static void usp_print_ie_bhli __P((struct ie_generic *)); +static void usp_print_ie_blli __P((struct ie_generic *)); +static void usp_print_ie_clst __P((struct ie_generic *)); +static void usp_print_ie_cdad __P((struct ie_generic *)); +static void usp_print_ie_cdsa __P((struct ie_generic *)); +static void usp_print_ie_cgad __P((struct ie_generic *)); +static void usp_print_ie_cgsa __P((struct ie_generic *)); +static void usp_print_ie_caus __P((struct ie_generic *)); +static void usp_print_ie_cnid __P((struct ie_generic *)); +static void usp_print_ie_qosp __P((struct ie_generic *)); +static void usp_print_ie_brpi __P((struct ie_generic *)); +static void usp_print_ie_rsti __P((struct ie_generic *)); +static void usp_print_ie_blsh __P((struct ie_generic *)); +static void usp_print_ie_bnsh __P((struct ie_generic *)); +static void usp_print_ie_bsdc __P((struct ie_generic *)); +static void usp_print_ie_trnt __P((struct ie_generic *)); +static void usp_print_ie_eprf __P((struct ie_generic *)); +static void usp_print_ie_epst __P((struct ie_generic *)); + + +/* + * Values for Q.2931 message type. + */ +static struct type_name msg_types[] = { + { "Call proceeding", 0x02 }, + { "Connect", 0x07 }, + { "Connect ACK", 0x0F }, + { "Setup", 0x05 }, + { "Release", 0x4D }, + { "Release complete", 0x5A }, + { "Restart", 0x46 }, + { "Restart ACK", 0x4E }, + { "Status", 0x7D }, + { "Status enquiry", 0x75 }, + { "Add party", 0x80 }, + { "Add party ACK", 0x81 }, + { "Add party reject", 0x82 }, + { "Drop party", 0x83 }, + { "Drop party ACK", 0x84 }, + {0, 0} +}; + + +/* + * Values for information element identifier. + */ +static struct type_name ie_types[] = { + { "Cause", 0x08 }, + { "Call state", 0x14 }, + { "Endpoint reference", 0x54 }, + { "Endpoint state", 0x55 }, + { "ATM AAL parameters", 0x58 }, + { "ATM user cell rate", 0x59 }, + { "Connection ID", 0x5A }, + { "QoS parameter", 0x5C }, + { "Broadband high layer info", 0x5D }, + { "Broadband bearer capability", 0x5E }, + { "Broadband low layer info", 0x5F }, + { "Broadband locking shift", 0x60 }, + { "Broadband non-locking shift", 0x61 }, + { "Broadband sending complete", 0x62 }, + { "Broadband repeat indicator", 0x63 }, + { "Calling party number", 0x6C }, + { "Calling party subaddress", 0x6D }, + { "Called party number", 0x70 }, + { "Called party subaddress", 0x71 }, + { "Transit net selection", 0x78 }, + { "Restart indicator", 0x79 }, + { 0, 0 } +}; + + +/* + * Search a name - type translation table + * + * Arguments: + * tbl a pointer to the table to search + * type the type to look for + * + * Returns: + * name a pointer to a character string with the name + * + */ +static char * +find_type(tbl, type) + struct type_name *tbl; + u_char type; +{ + while (type != tbl->type && tbl->name) + tbl++; + + if (tbl->name) + return(tbl->name); + else + return("-"); +} + + +/* + * Print an ATM address + * + * Arguments: + * p pointer to a Atm_address + * + * Returns: + * none + * + */ +static void +usp_print_atm_addr(p) + Atm_addr *p; +{ + char *cp; + + cp = unisig_addr_print(p); + printf("%s", cp); +} + + +/* + * Print a Q.2931 message structure + * + * Arguments: + * msg pointer to the message to print + * + * Returns: + * None + * + */ +void +usp_print_msg(msg, dir) + struct unisig_msg *msg; + int dir; +{ + char *name; + int i; + struct ie_generic *ie, *inxt; + + name = find_type(msg_types, msg->msg_type); + switch (dir) { + case UNISIG_MSG_IN: + printf("Received "); + break; + case UNISIG_MSG_OUT: + printf("Sent "); + break; + } + printf("message: %s (%x)\n", name, msg->msg_type); + printf(" Call reference: 0x%x\n", msg->msg_call_ref); +#ifdef LONG_PRINT + printf(" Message type flag: 0x%x\n", msg->msg_type_flag); + printf(" Message type action: 0x%x\n", msg->msg_type_action); + printf(" Message length: %d\n", msg->msg_length); + for (i=0; i<UNI_MSG_IE_CNT; i++) { + ie = msg->msg_ie_vec[i]; + while (ie) { + inxt = ie->ie_next; + usp_print_ie(ie); + ie = inxt; + } + } +#else + for (i=0; i<UNI_MSG_IE_CNT; i++) + { + ie = msg->msg_ie_vec[i]; + while (ie) { + inxt = ie->ie_next; + name = find_type(ie_types, ie->ie_ident); + if (ie->ie_ident == UNI_IE_CAUS || + ie->ie_ident == UNI_IE_RSTI || + ie->ie_ident == UNI_IE_CLST) { + usp_print_ie(ie); + } else { + printf(" Information element: %s (0x%x)\n", + name, ie->ie_ident); + } + ie = inxt; + } + } +#endif +} + + +/* + * Print a Q.2931 information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie(ie) + struct ie_generic *ie; +{ + char *name; + + while (ie) { + name = find_type(ie_types, ie->ie_ident); + printf(" Information element: %s (0x%x)\n", + name, ie->ie_ident); +#ifdef LONG_PRINT + printf(" Coding: 0x%x\n", + ie->ie_coding); + printf(" Flag: 0x%x\n", ie->ie_flag); + printf(" Action: 0x%x\n", + ie->ie_action); + printf(" Length: %d\n", ie->ie_length); +#endif + + switch (ie->ie_ident) { + case UNI_IE_AALP: + usp_print_ie_aalp(ie); + break; + case UNI_IE_CLRT: + usp_print_ie_clrt(ie); + break; + case UNI_IE_BBCP: + usp_print_ie_bbcp(ie); + break; + case UNI_IE_BHLI: + usp_print_ie_bhli(ie); + break; + case UNI_IE_BLLI: + usp_print_ie_blli(ie); + break; + case UNI_IE_CLST: + usp_print_ie_clst(ie); + break; + case UNI_IE_CDAD: + usp_print_ie_cdad(ie); + break; + case UNI_IE_CDSA: + usp_print_ie_cdsa(ie); + break; + case UNI_IE_CGAD: + usp_print_ie_cgad(ie); + break; + case UNI_IE_CGSA: + usp_print_ie_cgsa(ie); + break; + case UNI_IE_CAUS: + usp_print_ie_caus(ie); + break; + case UNI_IE_CNID: + usp_print_ie_cnid(ie); + break; + case UNI_IE_QOSP: + usp_print_ie_qosp(ie); + break; + case UNI_IE_BRPI: + usp_print_ie_brpi(ie); + break; + case UNI_IE_RSTI: + usp_print_ie_rsti(ie); + break; + case UNI_IE_BLSH: + usp_print_ie_blsh(ie); + break; + case UNI_IE_BNSH: + usp_print_ie_bnsh(ie); + break; + case UNI_IE_BSDC: + usp_print_ie_bsdc(ie); + break; + case UNI_IE_TRNT: + usp_print_ie_trnt(ie); + break; + case UNI_IE_EPRF: + usp_print_ie_eprf(ie); + break; + case UNI_IE_EPST: + usp_print_ie_epst(ie); + break; + } + ie = ie->ie_next; + } +} + + +/* + * Print an AAL parameters information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_aalp(ie) + struct ie_generic *ie; +{ + printf(" AAL type: %d\n", ie->ie_aalp_aal_type); + switch(ie->ie_aalp_aal_type) { + case UNI_IE_AALP_AT_AAL1: + printf(" Subtype: 0x%x\n", + ie->ie_aalp_1_subtype); + printf(" CBR rate: 0x%x\n", + ie->ie_aalp_1_cbr_rate); + printf(" Multiplier: 0x%x\n", + ie->ie_aalp_1_multiplier); + printf(" Clock rcvry: 0x%x\n", + ie->ie_aalp_1_clock_recovery); + printf(" Err corr: 0x%x\n", + ie->ie_aalp_1_error_correction); + printf(" Struct data: 0x%x\n", + ie->ie_aalp_1_struct_data_tran); + printf(" Partial cells: 0x%x\n", + ie->ie_aalp_1_partial_cells); + break; + case UNI_IE_AALP_AT_AAL3: + printf(" Fwd max SDU: %d\n", + ie->ie_aalp_4_fwd_max_sdu); + printf(" Bkwd max SDU: %d\n", + ie->ie_aalp_4_bkwd_max_sdu); + printf(" MID range: %d\n", + ie->ie_aalp_4_mid_range); + printf(" Mode: 0x%x\n", + ie->ie_aalp_4_mode); + printf(" SSCS type: 0x%x\n", + ie->ie_aalp_4_sscs_type); + break; + case UNI_IE_AALP_AT_AAL5: + printf(" Fwd max SDU: %d\n", + ie->ie_aalp_5_fwd_max_sdu); + printf(" Bkwd max SDU: %d\n", + ie->ie_aalp_5_bkwd_max_sdu); + printf(" Mode: 0x%x\n", + ie->ie_aalp_5_mode); + printf(" SSCS type: 0x%x\n", + ie->ie_aalp_5_sscs_type); + break; + case UNI_IE_AALP_AT_AALU: + printf(" User info: 0x%x %x %x %x\n", + ie->ie_aalp_user_info[0], + ie->ie_aalp_user_info[1], + ie->ie_aalp_user_info[2], + ie->ie_aalp_user_info[3]); + break; + } +} + + +/* + * Print a user cell rate information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_clrt(ie) + struct ie_generic *ie; +{ + printf(" Forward peak: %d\n", ie->ie_clrt_fwd_peak); + printf(" Backward peak: %d\n", ie->ie_clrt_bkwd_peak); + printf(" Fwd peak 01: %d\n", ie->ie_clrt_fwd_peak_01); + printf(" Bkwd peak 01: %d\n", ie->ie_clrt_bkwd_peak_01); + printf(" Fwd sust: %d\n", ie->ie_clrt_fwd_sust); + printf(" Bkwd sust: %d\n", ie->ie_clrt_bkwd_sust); + printf(" Fwd sust 01: %d\n", ie->ie_clrt_fwd_sust_01); + printf(" Bkwd sust 01: %d\n", ie->ie_clrt_bkwd_sust_01); + printf(" Fwd burst: %d\n", ie->ie_clrt_fwd_burst); + printf(" Bkwd burst: %d\n", ie->ie_clrt_bkwd_burst); + printf(" Fwd burst 01: %d\n", ie->ie_clrt_fwd_burst_01); + printf(" Bkwd burst 01: %d\n", + ie->ie_clrt_bkwd_burst_01); + printf(" Best effort: %d\n", ie->ie_clrt_best_effort); + printf(" TM optons: 0x%x\n", + ie->ie_clrt_tm_options); +} + + +/* + * Print a broadband bearer capability information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_bbcp(ie) + struct ie_generic *ie; +{ + printf(" Bearer class: 0x%x\n", + ie->ie_bbcp_bearer_class); + printf(" Traffic type: 0x%x\n", + ie->ie_bbcp_traffic_type); + printf(" Timing req: 0x%x\n", + ie->ie_bbcp_timing_req); + printf(" Clipping: 0x%x\n", ie->ie_bbcp_clipping); + printf(" Conn config: 0x%x\n", + ie->ie_bbcp_conn_config); +} + + +/* + * Print a broadband high layer information information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_bhli(ie) + struct ie_generic *ie; +{ + int i; + + printf(" Type: 0x%x\n", ie->ie_bhli_type); + printf(" HL info: 0x"); + for (i=0; i<ie->ie_length-1; i++) { + printf("%x ", ie->ie_bhli_info[i]); + } + printf("\n"); +} + + +/* + * Print a broadband low-layer information information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_blli(ie) + struct ie_generic *ie; +{ + printf(" Layer 1 ID: 0x%x\n", ie->ie_blli_l1_id); + printf(" Layer 2 ID: 0x%x\n", ie->ie_blli_l2_id); + printf(" Layer 2 mode: 0x%x\n", ie->ie_blli_l2_mode); + printf(" Layer 2 Q.933: 0x%x\n", + ie->ie_blli_l2_q933_use); + printf(" Layer 2 win: 0x%x\n", + ie->ie_blli_l2_window); + printf(" Layer 2 user: 0x%x\n", + ie->ie_blli_l2_user_proto); + printf(" Layer 3 ID: 0x%x\n", ie->ie_blli_l3_id); + printf(" Layer 3 mode: 0x%x\n", ie->ie_blli_l3_mode); + printf(" Layer 3 pkt: 0x%x\n", + ie->ie_blli_l3_packet_size); + printf(" Layer 3 win: 0x%x\n", + ie->ie_blli_l3_window); + printf(" Layer 3 user: 0x%x\n", + ie->ie_blli_l3_user_proto); + printf(" Layer 3 IPI: 0x%x\n", ie->ie_blli_l3_ipi); + printf(" Layer 3 SNAP: 0x%x\n", + ie->ie_blli_l3_snap_id); + printf(" Layer 3 OUI: 0x%x %x %x\n", + ie->ie_blli_l3_oui[0], + ie->ie_blli_l3_oui[1], + ie->ie_blli_l3_oui[2]); + printf(" Layer 3 PID: 0x%x %x\n", + ie->ie_blli_l3_pid[0], + ie->ie_blli_l3_pid[1]); +} + + +/* + * Print a call state information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_clst(ie) + struct ie_generic *ie; +{ + printf(" Call state: %d\n", + ie->ie_clst_state); +} + + +/* + * Print a called party number information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_cdad(ie) + struct ie_generic *ie; +{ + printf(" ATM addr: "); + usp_print_atm_addr(&ie->ie_cdad_addr); + printf("\n"); +} + + +/* + * Print a called party subaddress information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_cdsa(ie) + struct ie_generic *ie; +{ + printf(" ATM subaddr: "); + usp_print_atm_addr(&ie->ie_cdsa_addr); + printf("\n"); +} + + +/* + * Print a calling party number information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_cgad(ie) + struct ie_generic *ie; +{ + printf(" ATM addr: "); + usp_print_atm_addr(&ie->ie_cgad_addr); + printf("\n"); +} + + +/* + * Print a calling party subaddress information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_cgsa(ie) + struct ie_generic *ie; +{ + printf(" ATM subaddr: "); + usp_print_atm_addr(&ie->ie_cgsa_addr); + printf("\n"); +} + + +/* + * Print a cause information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_caus(ie) + struct ie_generic *ie; +{ + int i; + + printf(" Location: %d\n", ie->ie_caus_loc); + printf(" Cause: %d\n", ie->ie_caus_cause); + switch(ie->ie_caus_cause) { + case UNI_IE_CAUS_IECONTENT: + printf(" Flagged IEs: "); + for (i=0; ie->ie_caus_diagnostic[i]; i++) { + printf("0x%x ", ie->ie_caus_diagnostic[i]); + } + printf("\n"); + break; + case UNI_IE_CAUS_TIMER: + printf(" Timer ID: %c%c%c\n", + ie->ie_caus_diagnostic[0], + ie->ie_caus_diagnostic[1], + ie->ie_caus_diagnostic[2]); + break; + default: + printf(" Diag length: %d\n", + ie->ie_caus_diag_len); + printf(" Diagnostic: "); + for (i=0; i<ie->ie_caus_diag_len; i++) { + printf("0x%x ", ie->ie_caus_diagnostic[i]); + } + printf("\n"); + } +} + + +/* + * Print a connection identifier information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_cnid(ie) + struct ie_generic *ie; +{ + printf(" VP assoc sig: 0x%x\n", ie->ie_cnid_vp_sig); + printf(" Pref/excl: 0x%x\n", + ie->ie_cnid_pref_excl); + printf(" VPCI: %d\n", ie->ie_cnid_vpci); + printf(" VCI: %d\n", ie->ie_cnid_vci); +} + + +/* + * Print a quality of service parameter information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_qosp(ie) + struct ie_generic *ie; +{ + printf(" QoS fwd: 0x%x\n", + ie->ie_qosp_fwd_class); + printf(" QoS bkwd: 0x%x\n", + ie->ie_qosp_bkwd_class); +} + + +/* + * Print a broadband repeat indicator information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_brpi(ie) + struct ie_generic *ie; +{ + printf(" Indicator: 0x%x\n", ie->ie_brpi_ind); +} + + +/* + * Print a restart indicator information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_rsti(ie) + struct ie_generic *ie; +{ + printf(" Class: 0x%x\n", ie->ie_rsti_class); +} + + +/* + * Print a broadband locking shift information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_blsh(ie) + struct ie_generic *ie; +{ +} + + +/* + * Print a broadband non-locking shift information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_bnsh(ie) + struct ie_generic *ie; +{ +} + + +/* + * Print a broadband sending complete information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_bsdc(ie) + struct ie_generic *ie; +{ + printf(" Indication: 0x%x\n", ie->ie_bsdc_ind); +} + + +/* + * Print a transit net selection information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_trnt(ie) + struct ie_generic *ie; +{ +#ifdef NOTDEF + struct ie_generic ie_trnt_hdr; + u_char ie_trnt_id_type; + u_char ie_trnt_id_plan; + Atm_addr ie_trnt_id; +#endif +} + + +/* + * Print an endpoint reference information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_eprf(ie) + struct ie_generic *ie; +{ + printf(" Ref type: 0x%x\n", + ie->ie_eprf_type); + printf(" Endpt ref: 0x%x\n", + ie->ie_eprf_id); +} + + +/* + * Print an endpoint state information element + * + * Arguments: + * ie pointer to the IE to print + * + * Returns: + * None + * + */ +static void +usp_print_ie_epst(ie) + struct ie_generic *ie; +{ + printf(" Endpt state: %d\n", + ie->ie_epst_state); +} diff --git a/sys/netatm/uni/unisig_print.h b/sys/netatm/uni/unisig_print.h new file mode 100644 index 0000000..5e906c8 --- /dev/null +++ b/sys/netatm/uni/unisig_print.h @@ -0,0 +1,47 @@ +/* + * + * =================================== + * 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: unisig_print.h,v 1.2 1997/05/06 22:22:27 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Protocol control blocks + * + */ + +#ifndef _UNISIG_PRINT_H +#define _UNISIG_PRINT_H + +/* + * Message direction for print routine + */ +#define UNISIG_MSG_IN 1 +#define UNISIG_MSG_OUT 2 + +#endif /* _UNISIG_PRINT_H */ diff --git a/sys/netatm/uni/unisig_proto.c b/sys/netatm/uni/unisig_proto.c new file mode 100644 index 0000000..914e76d --- /dev/null +++ b/sys/netatm/uni/unisig_proto.c @@ -0,0 +1,324 @@ +/* + * + * =================================== + * 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: unisig_proto.c,v 1.9 1998/08/26 23:29:23 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Protocol processing module. + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_proto.c,v 1.9 1998/08/26 23:29:23 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_mbuf.h> + + +/* + * Process a UNISIG timeout + * + * Called when a previously scheduled protocol instance control block + * timer expires. This routine passes a timeout event to the UNISIG + * signalling manager state machine. + * + * Called at splnet. + * + * Arguments: + * tip pointer to UNISIG timer control block + * + * Returns: + * none + * + */ +void +unisig_timer(tip) + struct atm_time *tip; +{ + struct unisig *usp; + + /* + * Back-off to UNISIG control block + */ + usp = (struct unisig *) + ((caddr_t)tip - (int)(&((struct unisig *)0)->us_time)); + + ATM_DEBUG2("unisig_timer: usp=0x%x,state=%d\n", + (int)usp, usp->us_state); + + /* + * Pass the timeout to the signalling manager state machine + */ + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_TIMEOUT, + (KBuffer *) 0); +} + + +/* + * Process a UNISIG VCC timeout + * + * Called when a previously scheduled UNISIG VCCB timer expires. + * Processing will based on the current VCC state. + * + * Called at splnet. + * + * Arguments: + * tip pointer to vccb timer control block + * + * Returns: + * none + * + */ +void +unisig_vctimer(tip) + struct atm_time *tip; +{ + struct unisig *usp; + struct unisig_vccb *uvp; + + /* + * Get VCCB and UNISIG control block addresses + */ + uvp = (struct unisig_vccb *) ((caddr_t)tip - + (int)(&((struct vccb *)0)->vc_time)); + usp = (struct unisig *)uvp->uv_pif->pif_siginst; + + ATM_DEBUG3("unisig_vctimer: uvp=0x%x, sstate=%d, ustate=%d\n", + (int)uvp, uvp->uv_sstate, uvp->uv_ustate); + + /* + * Hand the timeout to the VC finite state machine + */ + if (uvp->uv_ustate == VCCU_ABORT) { + /* + * If we're aborting, this is an ABORT call + */ + (void) unisig_vc_state(usp, uvp, UNI_VC_ABORT_CALL, + (struct unisig_msg *) 0); + } else { + /* + * If we're not aborting, it's a timeout + */ + (void) unisig_vc_state(usp, uvp, UNI_VC_TIMEOUT, + (struct unisig_msg *) 0); + } +} + + +/* + * UNISIG SAAL Control Handler + * + * This is the module which receives data on the UNISIG signalling + * channel. Processing is based on the indication received from the + * SSCF and the protocol state. + * + * Arguments: + * cmd command code + * tok session token (pointer to UNISIG protocol control block) + * a1 argument 1 + * + * Returns: + * none + * + */ +void +unisig_saal_ctl(cmd, tok, a1) + int cmd; + void *tok; + void *a1; +{ + struct unisig *usp = tok; + + ATM_DEBUG4("unisig_upper: usp=0x%x,state=%d,cmd=%d,a1=0x%x,\n", + (u_long)usp, usp->us_state, cmd, (u_long)a1); + + /* + * Process command + */ + switch (cmd) { + + case SSCF_UNI_ESTABLISH_IND: + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_SSCF_EST_IND, + (KBuffer *) 0); + break; + + case SSCF_UNI_ESTABLISH_CNF: + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_SSCF_EST_CNF, + (KBuffer *) 0); + break; + + case SSCF_UNI_RELEASE_IND: + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_SSCF_RLS_IND, + (KBuffer *) 0); + break; + + case SSCF_UNI_RELEASE_CNF: + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_SSCF_RLS_CNF, + (KBuffer *) 0); + break; + + default: + log(LOG_ERR, + "unisig: unknown SAAL cmd: usp=0x%x, state=%d, cmd=%d\n", + (int)usp, usp->us_state, cmd); + } +} + + +/* + * UNISIG SAAL Data Handler + * + * This is the module which receives data on the UNISIG signalling + * channel. Processing is based on the protocol state. + * + * Arguments: + * tok session token (pointer to UNISIG protocol control block) + * m pointer to data + * + * Returns: + * none + * + */ +void +unisig_saal_data(tok, m) + void *tok; + KBuffer *m; +{ + struct unisig *usp = tok; + + ATM_DEBUG3("unisig_saal_data: usp=0x%x,state=%d,m=0x%x,\n", + (int)usp, usp->us_state, m); + + /* + * Pass data to signalling manager state machine + */ + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_SSCF_DATA_IND, + m); +} + + +/* + * Get Connection's Application/Owner Name + * + * Arguments: + * tok UNI signalling connection token (pointer to protocol instance) + * + * Returns: + * addr pointer to string containing our name + * + */ +caddr_t +unisig_getname(tok) + void *tok; +{ + struct unisig *usp = tok; + + if (usp->us_proto == ATM_SIG_UNI30) + return ("UNI3.0"); + else if (usp->us_proto == ATM_SIG_UNI31) + return ("UNI3.1"); + else if (usp->us_proto == ATM_SIG_UNI40) + return ("UNI4.0"); + else + return ("UNI"); +} + + +/* + * Process a VCC connection notification + * + * Should never be called. + * + * Arguments: + * tok user's connection token (unisig protocol block) + * + * Returns: + * none + * + */ +void +unisig_connected(tok) + void *tok; +{ + struct unisig *usp = tok; + + ATM_DEBUG2("unisig_connected: usp=0x%x,state=%d\n", + (u_long)usp, usp->us_state); + + /* + * Connected routine shouldn't ever get called for a PVC + */ + log(LOG_ERR, "unisig: connected notification, usp=0x%x\n", + (u_long)usp); +} + + +/* + * Process a VCC closed notification + * + * Called when UNISIG signalling channel is closed. + * + * Arguments: + * tok user's connection token (unisig protocol block) + * cp pointer to cause structure + * + * Returns: + * none + * + */ +void +unisig_cleared(tok, cp) + void *tok; + struct t_atm_cause *cp; +{ + struct unisig *usp = tok; + + ATM_DEBUG3("unisig_cleared: usp=0x%x, state=%d, cause=%d\n", + (u_long)usp, usp->us_state, cp->cause_value); + + /* + * VCC has been closed. Notify the signalling + * manager state machine. + */ + (void) unisig_sigmgr_state(usp, + UNISIG_SIGMGR_CALL_CLEARED, + (KBuffer *) 0); +} diff --git a/sys/netatm/uni/unisig_sigmgr_state.c b/sys/netatm/uni/unisig_sigmgr_state.c new file mode 100644 index 0000000..2fbbfca --- /dev/null +++ b/sys/netatm/uni/unisig_sigmgr_state.c @@ -0,0 +1,860 @@ +/* + * + * =================================== + * 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: unisig_sigmgr_state.c,v 1.10 1998/08/26 23:29:24 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Signalling manager finite state machine + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_sigmgr_state.c,v 1.10 1998/08/26 23:29:24 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.h> +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_mbuf.h> +#include <netatm/uni/unisig_decode.h> + +#include <netatm/uni/sscf_uni.h> + + +/* + * Local functions + */ +static int unisig_sigmgr_invalid __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act01 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act02 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act03 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act04 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act05 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act06 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act07 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act08 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act09 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act10 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act11 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act12 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act13 __P((struct unisig *, KBuffer *)); +static int unisig_sigmgr_act14 __P((struct unisig *, KBuffer *)); + + +/* + * State table. + */ +static int sigmgr_state_table[10][7] = { + /* 0 1 2 3 4 5 */ + { 1, 0, 0, 0, 0 }, /* 0 - Time out */ + { 0, 0, 3, 5, 0 }, /* 1 - SSCF estab ind */ + { 0, 0, 3, 5, 0 }, /* 2 - SSCF estab cnf */ + { 0, 0, 4, 6, 0 }, /* 3 - SSCF release ind */ + { 0, 0, 0, 6, 0 }, /* 4 - SSCF release cnf */ + { 0, 0, 0, 7, 0 }, /* 5 - SSCF data ind */ + { 0, 0, 2, 2, 0 }, /* 6 - SSCF unit data ind */ + { 0, 0, 8, 8, 8 }, /* 7 - Call cleared */ + { 14, 14, 14, 14, 0 }, /* 8 - Detach */ + { 13, 13, 0, 0, 0 } /* 9 - Address set */ +}; + +/* + * Action vector + */ +#define MAX_ACTION 15 +static int (*unisig_sigmgr_act_vec[MAX_ACTION]) + __P((struct unisig *, KBuffer *)) = { + unisig_sigmgr_invalid, + unisig_sigmgr_act01, + unisig_sigmgr_act02, + unisig_sigmgr_act03, + unisig_sigmgr_act04, + unisig_sigmgr_act05, + unisig_sigmgr_act06, + unisig_sigmgr_act07, + unisig_sigmgr_act08, + unisig_sigmgr_act09, + unisig_sigmgr_act10, + unisig_sigmgr_act11, + unisig_sigmgr_act12, + unisig_sigmgr_act13, + unisig_sigmgr_act14 +}; + + +/* + * ATM endpoint for UNI signalling channel + */ +static Atm_endpoint unisig_endpt = { + NULL, /* ep_next */ + ENDPT_UNI_SIG, /* ep_id */ + NULL, /* ep_ioctl */ + unisig_getname, /* ep_getname */ + unisig_connected, /* ep_connected */ + unisig_cleared, /* ep_cleared */ + NULL, /* ep_incoming */ + NULL, /* ep_addparty */ + NULL, /* ep_dropparty */ + NULL, /* ep_cpcs_ctl */ + NULL, /* ep_cpcs_data */ + unisig_saal_ctl, /* ep_saal_ctl */ + unisig_saal_data, /* ep_saal_data */ + NULL, /* ep_sscop_ctl */ + NULL /* ep_sscop_data */ +}; + + +/* + * ATM connection attributes for UNI signalling channel + */ +static Atm_attributes unisig_attr = { + NULL, /* nif */ + CMAPI_SAAL, /* api */ + UNI_VERS_3_0, /* api_init */ + 0, /* headin */ + 0, /* headout */ + { /* aal */ + T_ATM_PRESENT, /* aal.tag */ + ATM_AAL5 /* aal.aal_type */ + }, + { /* traffic */ + T_ATM_PRESENT, /* traffic.tag */ + { /* traffic.v */ + { /* traffic.v.forward */ + T_ATM_ABSENT, /* PCR_high */ + 0, /* PCR_all */ + T_ATM_ABSENT, /* SCR_high */ + T_ATM_ABSENT, /* SCR_all */ + T_ATM_ABSENT, /* MBS_high */ + T_ATM_ABSENT, /* MBS_all */ + T_NO, /* tagging */ + }, + { /* traffic.v.backward */ + T_ATM_ABSENT, /* PCR_high */ + 0, /* PCR_all */ + T_ATM_ABSENT, /* SCR_high */ + T_ATM_ABSENT, /* SCR_all */ + T_ATM_ABSENT, /* MBS_high */ + T_ATM_ABSENT, /* MBS_all */ + T_NO, /* tagging */ + }, + T_YES, /* best_effort */ + } + }, + { /* bearer */ + T_ATM_PRESENT, /* bearer.tag */ + { /* bearer.v */ + T_ATM_CLASS_X, /* class */ + T_ATM_NULL, /* traffic_type */ + T_ATM_NO_END_TO_END, /* timing_req */ + T_NO, /* clipping */ + T_ATM_1_TO_1, /* conn_conf */ + } + }, + { /* bhli */ + T_ATM_ABSENT, /* bhli.tag */ + }, + { /* blli */ + T_ATM_ABSENT, /* blli.tag_l2 */ + T_ATM_ABSENT, /* blli.tag_l3 */ + }, + { /* llc */ + T_ATM_ABSENT, /* llc.tag */ + }, + { /* called */ + T_ATM_PRESENT, /* called.tag */ + }, + { /* calling */ + T_ATM_ABSENT, /* calling.tag */ + }, + { /* qos */ + T_ATM_PRESENT, /* qos.tag */ + { /* qos.v */ + T_ATM_NETWORK_CODING, /* coding_standard */ + { /* qos.v.forward */ + T_ATM_QOS_CLASS_0, /* class */ + }, + { /* qos.v.backward */ + T_ATM_QOS_CLASS_0, /* class */ + } + } + }, + { /* transit */ + T_ATM_ABSENT, /* transit.tag */ + }, + { /* cause */ + T_ATM_ABSENT, /* cause.tag */ + } +}; + + +/* + * Finite state machine for the UNISIG signalling manager + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * event indication of the event to be processed + * m pointer to a buffer with a message (optional) + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +unisig_sigmgr_state(usp, event, m) + struct unisig *usp; + int event; + KBuffer *m; +{ + int action, err = 0; + + /* + * Cancel any signalling manager timer + */ + UNISIG_CANCEL(usp); + + /* + * Select an action based on the incoming event and + * the signalling manager's state + */ + action = sigmgr_state_table[event][usp->us_state]; + ATM_DEBUG4("unisig_sigmgr_state: usp=0x%x, state=%d, event=%d, action=%d\n", + (u_int) usp, usp->us_state, event, action); + if (action >= MAX_ACTION || action < 0) { + panic("unisig_sigmgr_state: invalid action\n"); + } + + /* + * Perform the requested action + */ + err = unisig_sigmgr_act_vec[action](usp, m); + + return(err); +} + + +/* + * Signalling manager state machine action 0 + * + * Invalid action + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_invalid(usp, m) + struct unisig *usp; + KBuffer *m; +{ + log(LOG_ERR, "unisig_sigmgr_state: unexpected action\n"); + if (m) + KB_FREEALL(m); + return(0); +} + + +/* + * Signalling manager state machine action 1 + * + * The kickoff timer has expired at attach time; go to + * UNISIG_ADDR_WAIT state. + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act01(usp, m) + struct unisig *usp; + KBuffer *m; +{ + /* + * Set the new state + */ + usp->us_state = UNISIG_ADDR_WAIT; + + return(0); +} + + +/* + * Signalling manager state machine action 2 + * + * Ignore the event + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act02(usp, m) + struct unisig *usp; + KBuffer *m; +{ + /* + * Ignore event, discard message if present + */ + if (m) + KB_FREEALL(m); + + return(0); +} + + +/* + * Signalling manager state machine action 3 + * + * SSCF session on signalling channel has come up + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act03(usp, m) + struct unisig *usp; + KBuffer *m; +{ + struct unisig_vccb *uvp, *vnext; + + /* + * Log the activation + */ + log(LOG_INFO, "unisig: signalling channel active\n"); + + /* + * Go to ACTIVE state + */ + usp->us_state = UNISIG_ACTIVE; + + /* + * Notify the VC state machine that the channel is up + */ + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); + uvp; uvp = vnext) { + vnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + (void) unisig_vc_state(usp, uvp, UNI_VC_SAAL_ESTAB, + (struct unisig_msg *) 0); + } + + return(0); +} + + +/* + * Signalling manager state machine action 4 + * + * A SSCF release indication was received. Try to establish an + * SSCF session on the signalling PVC. + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act04(usp, m) + struct unisig *usp; + KBuffer *m; +{ + int err; + + /* + * Try to establish an SSCF session. + */ + err = atm_cm_saal_ctl(SSCF_UNI_ESTABLISH_REQ, + usp->us_conn, + (void *)0); + if (err) + panic("unisig_sigmgr_act04: SSCF_UNI_ESTABLISH_REQ"); + + return(0); +} + + +/* + * Signalling manager state machine action 5 + * + * SSCF session on signalling channel has been reset + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act05(usp, m) + struct unisig *usp; + KBuffer *m; +{ + struct unisig_vccb *uvp, *vnext; + + /* + * Log the reset + */ + log(LOG_INFO, "unisig: signalling channel reset\n"); + + /* + * Notify the VC state machine of the reset + */ + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); + uvp; uvp = vnext) { + vnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + (void) unisig_vc_state(usp, uvp, UNI_VC_SAAL_ESTAB, + (struct unisig_msg *) 0); + } + + return(0); +} + + +/* + * Signalling manager state machine action 6 + * + * SSCF session on signalling channel has been lost + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act06(usp, m) + struct unisig *usp; + KBuffer *m; +{ + struct unisig_vccb *uvp, *vnext; + + /* + * Log the fact that the session has been lost + */ + log(LOG_INFO, "unisig: signalling channel SSCF session lost\n"); + + /* + * Notify the VC state machine of the loss + */ + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); + uvp; uvp = vnext) { + vnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + (void) unisig_vc_state(usp, uvp, UNI_VC_SAAL_FAIL, + (struct unisig_msg *) 0); + } + + /* + * Try to restart the SSCF session + */ + (void) unisig_sigmgr_act04(usp, (KBuffer *) 0); + + /* + * Go to INIT state + */ + usp->us_state = UNISIG_INIT; + + return(0); +} + + +/* + * Signalling manager state machine action 7 + * + * A Q.2931 signalling message has been received + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act07(usp, m) + struct unisig *usp; + KBuffer *m; +{ + int err; + + /* + * Pass the Q.2931 signalling message on + * to the VC state machine + */ + err = unisig_rcv_msg(usp, m); + + return(err); +} + + +/* + * Signalling manager state machine action 8 + * + * Process a CALL_CLOSED event for the signalling PVC + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act08(usp, m) + struct unisig *usp; + KBuffer *m; +{ + + /* + * Signalling manager is now incommunicado + */ + if (usp->us_state != UNISIG_DETACH) { + /* + * Log an error and set the state to NULL if + * we're not detaching + */ + log(LOG_ERR, "unisig: signalling channel closed\n"); + usp->us_state = UNISIG_NULL; + } + usp->us_conn = 0; + + return(0); +} + + +/* + * Signalling manager state machine action 9 + * + * Not used + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act09(usp, m) + struct unisig *usp; + KBuffer *m; +{ + log(LOG_ERR, "unisig_sigmgr_act09: unexpected action\n"); + if (m) + KB_FREEALL(m); + return (0); +} + + +/* + * Signalling manager state machine action 10 + * + * Not used + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act10(usp, m) + struct unisig *usp; + KBuffer *m; +{ + return(0); +} + + +/* + * Signalling manager state machine action 11 + * + * Not used + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act11(usp, m) + struct unisig *usp; + KBuffer *m; +{ + log(LOG_ERR, "unisig_sigmgr_act11: unexpected action\n"); + if (m) + KB_FREEALL(m); + return(0); +} + + +/* + * Signalling manager state machine action 12 + * + * Not used + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act12(usp, m) + struct unisig *usp; + KBuffer *m; +{ + log(LOG_ERR, "unisig_sigmgr_act11: unexpected action\n"); + if (m) + KB_FREEALL(m); + return(0); +} + + +/* + * Signalling manager state machine action 13 + * + * NSAP prefix has been set + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act13(usp, m) + struct unisig *usp; + KBuffer *m; +{ + int err; + Atm_addr_pvc *pvcp; + + /* + * Set UNI signalling channel connection attributes + */ + if (usp->us_proto == ATM_SIG_UNI30) + unisig_attr.api_init = UNI_VERS_3_0; + else + unisig_attr.api_init = UNI_VERS_3_1; + + unisig_attr.nif = usp->us_pif->pif_nif; + + unisig_attr.aal.v.aal5.forward_max_SDU_size = ATM_NIF_MTU; + unisig_attr.aal.v.aal5.backward_max_SDU_size = ATM_NIF_MTU; + unisig_attr.aal.v.aal5.SSCS_type = T_ATM_SSCS_SSCOP_REL; + + unisig_attr.called.tag = T_ATM_PRESENT; + unisig_attr.called.addr.address_format = T_ATM_PVC_ADDR; + unisig_attr.called.addr.address_length = sizeof(Atm_addr_pvc); + pvcp = (Atm_addr_pvc *)unisig_attr.called.addr.address; + ATM_PVC_SET_VPI(pvcp, UNISIG_SIG_VPI); + ATM_PVC_SET_VCI(pvcp, UNISIG_SIG_VCI); + unisig_attr.called.subaddr.address_format = T_ATM_ABSENT; + unisig_attr.called.subaddr.address_length = 0; + + unisig_attr.traffic.v.forward.PCR_all_traffic = + usp->us_pif->pif_pcr; + unisig_attr.traffic.v.backward.PCR_all_traffic = + usp->us_pif->pif_pcr; + + /* + * Create UNISIG signalling channel + */ + err = atm_cm_connect(&unisig_endpt, usp, &unisig_attr, + &usp->us_conn); + if (err) { + return(err); + } + + /* + * Establish the SSCF session + */ + err = atm_cm_saal_ctl(SSCF_UNI_ESTABLISH_REQ, + usp->us_conn, + (void *)0); + if (err) + panic("unisig_sigmgr_act13: SSCF_UNI_ESTABLISH_REQ"); + + /* + * Set the new state + */ + usp->us_state = UNISIG_INIT; + + return(0); +} + + +/* + * Signalling manager state machine action 14 + * + * Process a detach event + * + * Arguments: + * usp pointer to the UNISIG protocol control block + * m buffer pointer (may be NULL) + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_sigmgr_act14(usp, m) + struct unisig *usp; + KBuffer *m; +{ + int err; + struct unisig_vccb *sig_vccb, *uvp, *vnext; + struct atm_pif *pip; + struct t_atm_cause cause; + + /* + * Locate the signalling channel's VCCB + */ + sig_vccb = (struct unisig_vccb *)0; + if (usp->us_conn && usp->us_conn->co_connvc) + sig_vccb = (struct unisig_vccb *) + usp->us_conn->co_connvc->cvc_vcc; + + /* + * Terminate all of our VCCs + */ + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); + uvp; uvp = vnext) { + vnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + + /* + * Don't close the signalling VCC yet + */ + if (uvp == sig_vccb) + continue; + + /* + * Close VCC and notify owner + */ + err = unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + } + + /* + * Set the signalling manager state + */ + usp->us_state = UNISIG_DETACH; + + /* + * Close the signalling channel + */ + if (usp->us_conn) { + cause.coding_standard = T_ATM_ITU_CODING; + cause.coding_standard = T_ATM_LOC_USER; + cause.coding_standard = T_ATM_CAUSE_UNSPECIFIED_NORMAL; + err = atm_cm_release(usp->us_conn, &cause); + if (err) + panic("unisig_sigmgr_act14: close failed\n"); + } + + /* + * Get rid of protocol instance if there are no VCCs queued + */ + pip = usp->us_pif; + if (Q_HEAD(usp->us_vccq, struct vccb) == NULL && + pip->pif_sigmgr) { + struct sigmgr *smp = pip->pif_sigmgr; + int s = splimp(); + + pip->pif_sigmgr = NULL; + pip->pif_siginst = NULL; + (void) splx(s); + + UNLINK((struct siginst *)usp, struct siginst, + smp->sm_prinst, si_next); + KM_FREE(usp, sizeof(struct unisig), M_DEVBUF); + } + + /* + * Otherwise, wait for protocol instance to be freed + * during unisig_free processing for the last queued VCC + */ + + return (0); +} diff --git a/sys/netatm/uni/unisig_subr.c b/sys/netatm/uni/unisig_subr.c new file mode 100644 index 0000000..f8dc067 --- /dev/null +++ b/sys/netatm/uni/unisig_subr.c @@ -0,0 +1,1276 @@ +/* + * + * =================================== + * 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: unisig_subr.c,v 1.12 1998/08/26 23:29:24 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_subr.c,v 1.12 1998/08/26 23:29:24 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> + + +/* + * External variables + */ +extern struct ie_aalp ie_aalp_absent; +extern struct ie_clrt ie_clrt_absent; +extern struct ie_bbcp ie_bbcp_absent; +extern struct ie_bhli ie_bhli_absent; +extern struct ie_blli ie_blli_absent; +extern struct ie_clst ie_clst_absent; +extern struct ie_cdad ie_cdad_absent; +extern struct ie_cdsa ie_cdsa_absent; +extern struct ie_cgad ie_cgad_absent; +extern struct ie_cgsa ie_cgsa_absent; +extern struct ie_caus ie_caus_absent; +extern struct ie_cnid ie_cnid_absent; +extern struct ie_qosp ie_qosp_absent; +extern struct ie_brpi ie_brpi_absent; +extern struct ie_rsti ie_rsti_absent; +extern struct ie_blsh ie_blsh_absent; +extern struct ie_bnsh ie_bnsh_absent; +extern struct ie_bsdc ie_bsdc_absent; +extern struct ie_trnt ie_trnt_absent; +extern struct ie_eprf ie_eprf_absent; +extern struct ie_epst ie_epst_absent; + + +/* + * Set a cause code in an ATM attribute block + * + * Arguments: + * aap pointer to attribute block + * cause cause code + * + * Returns: + * none + * + */ +void +unisig_set_cause_attr(aap, cause) + Atm_attributes *aap; + int cause; +{ + /* + * Set the fields in the attribute block + */ + aap->cause.tag = T_ATM_PRESENT; + aap->cause.v.coding_standard = T_ATM_ITU_CODING; + aap->cause.v.location = T_ATM_LOC_USER; + aap->cause.v.cause_value = cause; + KM_ZERO(aap->cause.v.diagnostics, + sizeof(aap->cause.v.diagnostics)); +} + + +/* + * Open a UNI VCC + * + * Called when a user wants to open a VC. This function will construct + * a VCCB and, if we are opening an SVC, call the Q.2931 VC state + * machine. The user will have to wait for a notify event to be sure + * the SVC is fully open. + * + * Must be called at splnet. + * + * Arguments: + * usp pointer to UNISIG protocol instance + * cvp pointer to connection parameters for the VCC + * + * Returns: + * 0 VCC creation successful + * errno VCC setup failed - reason indicated + * + */ +int +unisig_open_vcc(usp, cvp) + struct unisig *usp; + Atm_connvc *cvp; +{ + struct atm_pif *pip = usp->us_pif; + struct unisig_vccb *uvp; + Atm_addr_pvc *pvp; + int err, pvc; + + ATM_DEBUG2("unisig_open_vcc: usp=0x%x, cvp=0x%x\n", usp, cvp); + + /* + * Validate user parameters. AAL and encapsulation are + * checked by the connection manager + */ + + /* + * Check called party address(es) + */ + if(cvp->cvc_attr.called.tag != T_ATM_PRESENT || + cvp->cvc_attr.called.addr.address_format == + T_ATM_ABSENT) { + return(EINVAL); + } + switch (cvp->cvc_attr.called.addr.address_format) { + case T_ATM_PVC_ADDR: + /* + * Make sure VPI/VCI is valid + */ + pvc = 1; + pvp = (Atm_addr_pvc *)cvp->cvc_attr.called.addr.address; + if ((ATM_PVC_GET_VPI(pvp) > pip->pif_maxvpi) || + (ATM_PVC_GET_VCI(pvp) == 0) || + (ATM_PVC_GET_VCI(pvp) > pip->pif_maxvci)) { + return(ERANGE); + } + + /* + * Make sure VPI/VCI is not already in use + */ + if (unisig_find_vpvc(usp, + ATM_PVC_GET_VPI(pvp), + ATM_PVC_GET_VCI(pvp), 0)) { + return(EEXIST); + } + ATM_DEBUG2("unisig_open_vcc: VPI.VCI=%d.%d\n", + ATM_PVC_GET_VPI(pvp), + ATM_PVC_GET_VCI(pvp)); + break; + + case T_ATM_ENDSYS_ADDR: + /* + * Check signalling state + */ + pvc = 0; + pvp = NULL; + if (usp->us_state != UNISIG_ACTIVE) { + return(ENETDOWN); + } + + /* + * Make sure there's no subaddress + */ + if (cvp->cvc_attr.called.subaddr.address_format != + T_ATM_ABSENT) { + return(EINVAL); + } + break; + + case T_ATM_E164_ADDR: + /* + * Check signalling state + */ + pvc = 0; + pvp = NULL; + if (usp->us_state != UNISIG_ACTIVE) { + return(ENETDOWN); + } + + /* + * Check destination address format + */ + if (cvp->cvc_attr.called.subaddr.address_format != + T_ATM_ENDSYS_ADDR && + cvp->cvc_attr.called.subaddr.address_format != + T_ATM_ABSENT) { + return(EINVAL); + } + break; + + default: + return(EPROTONOSUPPORT); + } + + /* + * Check that this is for the same interface UNISIG uses + */ + if (!cvp->cvc_attr.nif || + cvp->cvc_attr.nif->nif_pif != usp->us_pif) { + return(EINVAL); + } + + /* + * Allocate control block for VCC + */ + uvp = (struct unisig_vccb *)atm_allocate(&unisig_vcpool); + if (uvp == NULL) { + return(ENOMEM); + } + + /* + * Fill in VCCB + */ + if (pvc) { + uvp->uv_type = VCC_PVC | VCC_IN | VCC_OUT; + uvp->uv_vpi = ATM_PVC_GET_VPI(pvp); + uvp->uv_vci = ATM_PVC_GET_VCI(pvp); + uvp->uv_sstate = (usp->us_state == UNISIG_ACTIVE ? + UNI_PVC_ACTIVE : UNI_PVC_ACT_DOWN); + uvp->uv_ustate = VCCU_OPEN; + } else { + uvp->uv_type = VCC_SVC | VCC_IN | VCC_OUT; + uvp->uv_sstate = UNI_NULL; + uvp->uv_ustate = VCCU_POPEN; + } + uvp->uv_proto = usp->us_pif->pif_sigmgr->sm_proto; + uvp->uv_pif = usp->us_pif; + uvp->uv_nif = cvp->cvc_attr.nif; + uvp->uv_connvc = cvp; + uvp->uv_tstamp = time_second; + + /* + * Put VCCB on UNISIG queue + */ + ENQUEUE(uvp, struct unisig_vccb, uv_sigelem, usp->us_vccq); + + /* + * Call the VC state machine if this is an SVC + */ + if (!pvc) { + err = unisig_vc_state(usp, uvp, UNI_VC_SETUP_CALL, + (struct unisig_msg *) 0); + if (err) { + /* + * On error, delete the VCCB + */ + DEQUEUE(uvp, struct unisig_vccb, uv_sigelem, + usp->us_vccq); + atm_free((caddr_t)uvp); + return(err); + } + } + + /* + * Link VCCB to VCC connection block + */ + cvp->cvc_vcc = (struct vccb *) uvp; + + return(0); +} + + +/* + * Close a UNISIG VCC + * + * Called when a user wants to close a VCC. This function will clean + * up the VCCB and, for an SVC, send a close request. + * + * Must be called at splnet. + * + * Arguments: + * usp pointer to UNISIG protocol instance + * uvp pointer to VCCB for the VCC to be closed + * + * Returns: + * 0 VCC is now closed + * errno error encountered + */ +int +unisig_close_vcc(usp, uvp) + struct unisig *usp; + struct unisig_vccb *uvp; +{ + int err = 0; + + ATM_DEBUG2("unisig_close_vcc: uvp=0x%x, state=%d\n", uvp, + uvp->uv_sstate); + + /* + * Check that this is for the same interface UNISIG uses + */ + if (uvp->uv_pif != usp->us_pif) { + return (EINVAL); + } + + /* + * Mark the close time. + */ + uvp->uv_tstamp = time_second; + + /* + * Process based on the connection type + */ + if (uvp->uv_type & VCC_PVC) { + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + } else if (uvp->uv_type & VCC_SVC) { + /* + * Call the VC state machine + */ + uvp->uv_ustate = VCCU_CLOSED; + err = unisig_vc_state(usp, uvp, UNI_VC_RELEASE_CALL, + (struct unisig_msg *) 0); + } + + /* + * Wait for user to free resources + */ + return(err); +} + + +/* + * Clear a UNISIG VCC + * + * Called to internally clear a VCC. No external protocol is + * initiated, the VCC is just closed and the owner is notified. + * + * Must be called at splnet. + * + * Arguments: + * usp pointer to UNISIG protocol instance + * uvp pointer to VCCB for the VCC to be closed + * cause cause code giving the reason for the close + * + * Returns: + * 0 VCC is closed + * errno error encountered + */ +int +unisig_clear_vcc(usp, uvp, cause) + struct unisig *usp; + struct unisig_vccb *uvp; + int cause; +{ + u_char outstate; + + ATM_DEBUG3("unisig_clear_vcc: uvp=0x%x, state=%d, cause=%d\n", + (int)uvp, uvp->uv_sstate, cause); + + /* + * Check that this is for the same interface UNISIG uses + */ + if (uvp->uv_pif != usp->us_pif) { + return (EINVAL); + } + + /* + * Kill any possible timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Mark the close time. + */ + uvp->uv_tstamp = time_second; + + /* + * Close the VCC and notify the user + */ + outstate = uvp->uv_sstate; + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + if (outstate == UNI_ACTIVE || + outstate == UNI_CALL_INITIATED || + outstate == UNI_CALL_OUT_PROC || + outstate == UNI_CONNECT_REQUEST || + outstate == UNI_RELEASE_REQUEST || + outstate == UNI_RELEASE_IND || + outstate == UNI_SSCF_RECOV || + outstate == UNI_PVC_ACT_DOWN || + outstate == UNI_PVC_ACTIVE) { + unisig_set_cause_attr(&uvp->uv_connvc->cvc_attr, cause); + atm_cm_cleared(uvp->uv_connvc); + } + + /* + * Wait for user to free resources + */ + return(0); +} + + +#ifdef NOTDEF +/* + * Reset the switch state + * + * Arguments: + * usp pointer to UNISIG protocol instance + * + * Returns: + * none + * + */ +void +unisig_switch_reset(usp, cause) + struct unisig *usp; + int cause; +{ + int s; + struct unisig_vccb *uvp, *vnext; + + ATM_DEBUG2("unisig_switch_reset: usp=0x%x, cause=%d\n", + usp, cause); + + /* + * Terminate all of our VCCs + */ + s = splnet(); + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; + uvp = vnext) { + vnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + + if (uvp->uv_type & VCC_SVC) { + /* + * Close the SVC and notify the owner + */ + (void)unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + } else if (uvp->uv_type & VCC_PVC) { + /* + * Notify PVC owner of the state change + */ + switch(cause) { + case UNI_DOWN: + uvp->uv_sstate = UNI_PVC_ACT_DOWN; + break; + case UNI_UP: + uvp->uv_sstate = UNI_PVC_ACTIVE; + break; + } + atm_cm_cleared(uvp->uv_connvc, cause); + } else { + log(LOG_ERR, "unisig: invalid VCC type: vccb=0x%x, type=%d\n", + uvp, uvp->uv_type); + } + } + (void) splx(s); +} +#endif + + +/* + * Copy connection parameters from UNI 3.0 message IEs into + * an attribute block + * + * Arguments: + * usp pointer to UNISIG protocol instance + * msg pointer to the SETUP message + * ap pointer to the attribute block + * + * Returns: + * none + * + */ +void +unisig_save_attrs(usp, msg, ap) + struct unisig *usp; + struct unisig_msg *msg; + Atm_attributes *ap; +{ + /* + * Sanity check + */ + if (!msg || !ap) + return; + + /* + * Save the AAL parameters (AAL 3/4 and AAL 5 only) + */ + if (msg->msg_ie_aalp) { + struct ie_generic *aalp = msg->msg_ie_aalp; + + switch(msg->msg_ie_aalp->ie_aalp_aal_type) { + case UNI_IE_AALP_AT_AAL3: + ap->aal.tag = T_ATM_PRESENT; + ap->aal.type = + msg->msg_ie_aalp->ie_aalp_aal_type; + ap->aal.v.aal4.forward_max_SDU_size = + msg->msg_ie_aalp->ie_aalp_4_fwd_max_sdu; + ap->aal.v.aal4.backward_max_SDU_size = + msg->msg_ie_aalp->ie_aalp_4_bkwd_max_sdu; + ap->aal.v.aal4.SSCS_type = + msg->msg_ie_aalp->ie_aalp_4_sscs_type; + if (aalp->ie_aalp_4_mid_range == T_ATM_ABSENT) { + ap->aal.v.aal4.mid_low = T_ATM_ABSENT; + ap->aal.v.aal4.mid_high = T_ATM_ABSENT; + } else { + if (usp->us_proto == ATM_SIG_UNI30) { + ap->aal.v.aal4.mid_low = 0; + ap->aal.v.aal4.mid_high = + aalp->ie_aalp_4_mid_range + & UNI_IE_AALP_A3_R_MASK; + } else { + ap->aal.v.aal4.mid_low = + (aalp->ie_aalp_4_mid_range >> + UNI_IE_AALP_A3_R_SHIFT) + & UNI_IE_AALP_A3_R_MASK; + ap->aal.v.aal4.mid_high = + aalp->ie_aalp_4_mid_range + & UNI_IE_AALP_A3_R_MASK; + } + } + break; + case UNI_IE_AALP_AT_AAL5: + ap->aal.tag = T_ATM_PRESENT; + ap->aal.type = + msg->msg_ie_aalp->ie_aalp_aal_type; + ap->aal.v.aal5.forward_max_SDU_size = + msg->msg_ie_aalp->ie_aalp_5_fwd_max_sdu; + ap->aal.v.aal5.backward_max_SDU_size = + msg->msg_ie_aalp->ie_aalp_5_bkwd_max_sdu; + ap->aal.v.aal5.SSCS_type = + msg->msg_ie_aalp->ie_aalp_5_sscs_type; + break; + } + } + + /* + * Save traffic descriptor attributes + */ + if (msg->msg_ie_clrt) { + ap->traffic.tag = T_ATM_PRESENT; + ap->traffic.v.forward.PCR_high_priority = + msg->msg_ie_clrt->ie_clrt_fwd_peak; + ap->traffic.v.forward.PCR_all_traffic = + msg->msg_ie_clrt->ie_clrt_fwd_peak_01; + ap->traffic.v.forward.SCR_high_priority = + msg->msg_ie_clrt->ie_clrt_fwd_sust; + ap->traffic.v.forward.SCR_all_traffic = + msg->msg_ie_clrt->ie_clrt_fwd_sust_01; + ap->traffic.v.forward.MBS_high_priority = + msg->msg_ie_clrt->ie_clrt_fwd_burst; + ap->traffic.v.forward.MBS_all_traffic = + msg->msg_ie_clrt->ie_clrt_fwd_burst_01; + ap->traffic.v.backward.PCR_high_priority = + msg->msg_ie_clrt->ie_clrt_bkwd_peak; + ap->traffic.v.backward.PCR_all_traffic = + msg->msg_ie_clrt->ie_clrt_bkwd_peak_01; + ap->traffic.v.backward.SCR_high_priority = + msg->msg_ie_clrt->ie_clrt_bkwd_sust; + ap->traffic.v.backward.SCR_all_traffic = + msg->msg_ie_clrt->ie_clrt_bkwd_sust_01; + ap->traffic.v.backward.MBS_high_priority = + msg->msg_ie_clrt->ie_clrt_bkwd_burst; + ap->traffic.v.backward.MBS_all_traffic = + msg->msg_ie_clrt->ie_clrt_bkwd_burst_01; + ap->traffic.v.best_effort = + msg->msg_ie_clrt->ie_clrt_best_effort; + if (msg->msg_ie_clrt->ie_clrt_tm_options == + T_ATM_ABSENT) { + ap->traffic.v.forward.tagging = T_NO; + ap->traffic.v.backward.tagging = T_NO; + } else { + ap->traffic.v.forward.tagging = + (msg->msg_ie_clrt->ie_clrt_tm_options & + UNI_IE_CLRT_TM_FWD_TAG) != 0; + ap->traffic.v.backward.tagging = + (msg->msg_ie_clrt->ie_clrt_tm_options & + UNI_IE_CLRT_TM_BKWD_TAG) != 0; + } + } + + /* + * Save broadband bearer attributes + */ + if (msg->msg_ie_bbcp) { + ap->bearer.tag = T_ATM_PRESENT; + ap->bearer.v.bearer_class = + msg->msg_ie_bbcp->ie_bbcp_bearer_class; + ap->bearer.v.traffic_type = + msg->msg_ie_bbcp->ie_bbcp_traffic_type; + ap->bearer.v.timing_requirements = + msg->msg_ie_bbcp->ie_bbcp_timing_req; + ap->bearer.v.clipping_susceptibility = + msg->msg_ie_bbcp->ie_bbcp_clipping; + ap->bearer.v.connection_configuration = + msg->msg_ie_bbcp->ie_bbcp_conn_config; + } + + /* + * Save broadband high layer attributes + */ + if (msg->msg_ie_bhli) { + ap->bhli.tag = T_ATM_PRESENT; + ap->bhli.v.ID_type = msg->msg_ie_bhli->ie_bhli_type; + switch(ap->bhli.v.ID_type) { + case T_ATM_ISO_APP_ID: + KM_COPY(msg->msg_ie_bhli->ie_bhli_info, + ap->bhli.v.ID.ISO_ID, + sizeof(ap->bhli.v.ID.ISO_ID)); + break; + case T_ATM_USER_APP_ID: + KM_COPY(msg->msg_ie_bhli->ie_bhli_info, + ap->bhli.v.ID.user_defined_ID, + sizeof(ap->bhli.v.ID.user_defined_ID)); + break; + case T_ATM_VENDOR_APP_ID: + KM_COPY(msg->msg_ie_bhli->ie_bhli_info, + ap->bhli.v.ID.vendor_ID.OUI, + sizeof(ap->bhli.v.ID.vendor_ID.OUI)); + KM_COPY(&msg->msg_ie_bhli->ie_bhli_info[sizeof(ap->bhli.v.ID.vendor_ID.OUI)-1], + ap->bhli.v.ID.vendor_ID.app_ID, + sizeof(ap->bhli.v.ID.vendor_ID.app_ID)); + break; + } + } + + /* + * Save Broadband low layer, user layer 2 and 3 attributes + */ + if (msg->msg_ie_blli) { + /* + * Layer 2 parameters + */ + switch(msg->msg_ie_blli->ie_blli_l2_id) { + case UNI_IE_BLLI_L2P_ISO1745: + case UNI_IE_BLLI_L2P_Q921: + case UNI_IE_BLLI_L2P_X25L: + case UNI_IE_BLLI_L2P_X25M: + case UNI_IE_BLLI_L2P_LAPB: + case UNI_IE_BLLI_L2P_HDLC1: + case UNI_IE_BLLI_L2P_HDLC2: + case UNI_IE_BLLI_L2P_HDLC3: + case UNI_IE_BLLI_L2P_LLC: + case UNI_IE_BLLI_L2P_X75: + case UNI_IE_BLLI_L2P_Q922: + case UNI_IE_BLLI_L2P_ISO7776: + ap->blli.tag_l2 = T_ATM_PRESENT; + ap->blli.v.layer_2_protocol.ID_type = + T_ATM_SIMPLE_ID; + ap->blli.v.layer_2_protocol.ID.simple_ID = + msg->msg_ie_blli->ie_blli_l2_id; + break; + case UNI_IE_BLLI_L2P_USER: + ap->blli.tag_l2 = T_ATM_PRESENT; + ap->blli.v.layer_2_protocol.ID_type = + T_ATM_USER_ID; + ap->blli.v.layer_2_protocol.ID.user_defined_ID = + msg->msg_ie_blli->ie_blli_l2_user_proto; + break; + default: + ap->blli.tag_l2 = T_ATM_ABSENT; + } + if (ap->blli.tag_l2 == T_ATM_PRESENT) { + ap->blli.v.layer_2_protocol.mode = + msg->msg_ie_blli->ie_blli_l2_mode; + ap->blli.v.layer_2_protocol.window_size = + msg->msg_ie_blli->ie_blli_l2_window; + } + + /* + * Layer 3 parameters + */ + switch(msg->msg_ie_blli->ie_blli_l3_id) { + case UNI_IE_BLLI_L3P_X25: + case UNI_IE_BLLI_L3P_ISO8208: + case UNI_IE_BLLI_L3P_ISO8878: + case UNI_IE_BLLI_L3P_ISO8473: + case UNI_IE_BLLI_L3P_T70: + ap->blli.tag_l3 = T_ATM_PRESENT; + ap->blli.v.layer_3_protocol.ID_type = + T_ATM_SIMPLE_ID; + ap->blli.v.layer_3_protocol.ID.simple_ID = + msg->msg_ie_blli->ie_blli_l3_id; + break; + case UNI_IE_BLLI_L3P_ISO9577: + ap->blli.tag_l3 = T_ATM_PRESENT; + ap->blli.v.layer_3_protocol.ID_type = + T_ATM_SIMPLE_ID; + ap->blli.v.layer_3_protocol.ID.simple_ID = + msg->msg_ie_blli->ie_blli_l3_id; + if (msg->msg_ie_blli->ie_blli_l3_ipi == + UNI_IE_BLLI_L3IPI_SNAP) { + KM_COPY(msg->msg_ie_blli->ie_blli_l3_oui, + ap->blli.v.layer_3_protocol.ID.SNAP_ID.OUI, + sizeof(ap->blli.v.layer_3_protocol.ID.SNAP_ID.OUI)); + KM_COPY(msg->msg_ie_blli->ie_blli_l3_pid, + ap->blli.v.layer_3_protocol.ID.SNAP_ID.PID, + sizeof(ap->blli.v.layer_3_protocol.ID.SNAP_ID.PID)); + } else { + ap->blli.v.layer_3_protocol.ID.IPI_ID = + msg->msg_ie_blli->ie_blli_l3_ipi; + } + break; + case UNI_IE_BLLI_L3P_USER: + ap->blli.tag_l3 = T_ATM_PRESENT; + ap->blli.v.layer_3_protocol.ID_type = + T_ATM_USER_ID; + ap->blli.v.layer_3_protocol.ID.user_defined_ID = + msg->msg_ie_blli->ie_blli_l3_user_proto; + break; + default: + ap->blli.tag_l3 = T_ATM_ABSENT; + } + if (ap->blli.tag_l3 == T_ATM_PRESENT) { + ap->blli.v.layer_3_protocol.mode = + msg->msg_ie_blli->ie_blli_l3_mode; + ap->blli.v.layer_3_protocol.packet_size = + msg->msg_ie_blli->ie_blli_l3_packet_size; + ap->blli.v.layer_3_protocol.window_size = + msg->msg_ie_blli->ie_blli_l3_window; + } + } + + /* + * Save the called party address and subaddress + */ + if (msg->msg_ie_cdad) { + ap->called.tag = T_ATM_PRESENT; + ATM_ADDR_COPY(&msg->msg_ie_cdad->ie_cdad_addr, + &ap->called.addr); + ap->called.subaddr.address_format = T_ATM_ABSENT; + ap->called.subaddr.address_length = 0; + } + if (msg->msg_ie_cdsa) { + ATM_ADDR_COPY(&msg->msg_ie_cdsa->ie_cdsa_addr, + &ap->called.subaddr); + } + + /* + * Save the calling party address and subaddress + */ + if (msg->msg_ie_cgad) { + ap->calling.tag = T_ATM_PRESENT; + ATM_ADDR_COPY(&msg->msg_ie_cgad->ie_cgad_addr, + &ap->calling.addr); + ap->calling.subaddr.address_format = T_ATM_ABSENT; + ap->calling.subaddr.address_length = 0; + } + + if (msg->msg_ie_cgsa) { + ATM_ADDR_COPY(&msg->msg_ie_cgsa->ie_cgsa_addr, + &ap->calling.subaddr); + } + + /* + * Save quality of service attributes + */ + if (msg->msg_ie_qosp) { + ap->qos.tag = T_ATM_PRESENT; + ap->qos.v.coding_standard = msg->msg_ie_qosp->ie_coding; + ap->qos.v.forward.qos_class = msg->msg_ie_qosp->ie_qosp_fwd_class; + ap->qos.v.forward.qos_class = + msg->msg_ie_qosp->ie_qosp_bkwd_class; + } + + /* + * Save transit network attributes + */ + if (msg->msg_ie_trnt) { + ap->transit.tag = T_ATM_PRESENT; + ap->transit.v.length = + MIN(msg->msg_ie_trnt->ie_trnt_id_len, + sizeof(ap->transit.v.network_id)); + KM_COPY(msg->msg_ie_trnt->ie_trnt_id, + ap->transit.v.network_id, + ap->transit.v.length); + } + + /* + * Save cause code + */ + if (msg->msg_ie_caus) { + ap->cause.tag = T_ATM_PRESENT; + ap->cause.v.coding_standard = + msg->msg_ie_caus->ie_coding; + ap->cause.v.location = + msg->msg_ie_caus->ie_caus_loc; + ap->cause.v.cause_value = + msg->msg_ie_caus->ie_caus_cause; + KM_ZERO(ap->cause.v.diagnostics, + sizeof(ap->cause.v.diagnostics)); +#ifdef NOTDEF + KM_COPY(msg->msg_ie_caus->ie_caus_diagnostic, + ap->transit.v.diagnostics, + MIN(sizeof(ap->transit.v.diagnostics), + msg->msg_ie_caus->ie_caus_diag_len)); +#endif + } +} + + +/* + * Copy connection parameters from an attribute block into + * UNI 3.0 message IEs + * + * Arguments: + * usp pointer to UNISIG protocol instance + * msg pointer to the SETUP message + * ap pointer to the attribute block + * + * Returns: + * 0 everything OK + * else error encountered + * + */ +int +unisig_set_attrs(usp, msg, ap) + struct unisig *usp; + struct unisig_msg *msg; + Atm_attributes *ap; +{ + int err = 0; + + /* + * Sanity check + */ + if (!msg || !ap) + return(EINVAL); + + /* + * Set the AAL parameters (AAL 3/4 and AAL 5 only) + */ + if (ap->aal.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_aalp) { + msg->msg_ie_aalp = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_aalp == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_aalp_absent, + &msg->msg_ie_aalp->ie_u.ie_aalp, + sizeof(ie_aalp_absent)); + msg->msg_ie_aalp->ie_ident = UNI_IE_AALP; + msg->msg_ie_aalp->ie_aalp_aal_type = ap->aal.type; + switch(ap->aal.type) { + case ATM_AAL3_4: + msg->msg_ie_aalp->ie_aalp_4_fwd_max_sdu = + ap->aal.v.aal4.forward_max_SDU_size; + msg->msg_ie_aalp->ie_aalp_4_bkwd_max_sdu = + ap->aal.v.aal4.backward_max_SDU_size; + msg->msg_ie_aalp->ie_aalp_4_mode = UNI_IE_AALP_A5_M_MSG; + msg->msg_ie_aalp->ie_aalp_4_sscs_type = + ap->aal.v.aal4.SSCS_type; + if (ap->aal.v.aal4.mid_low == T_ATM_ABSENT) { + msg->msg_ie_aalp->ie_aalp_4_mid_range = + T_ATM_ABSENT; + } else { + if (usp->us_proto == ATM_SIG_UNI30) { + msg->msg_ie_aalp->ie_aalp_4_mid_range = + ap->aal.v.aal4.mid_high & + UNI_IE_AALP_A3_R_MASK; + } else { + msg->msg_ie_aalp->ie_aalp_4_mid_range = + ((ap->aal.v.aal4.mid_low & + UNI_IE_AALP_A3_R_MASK) + << UNI_IE_AALP_A3_R_SHIFT) + | + (ap->aal.v.aal4.mid_high & + UNI_IE_AALP_A3_R_MASK); + } + } + break; + case ATM_AAL5: + msg->msg_ie_aalp->ie_aalp_5_fwd_max_sdu = + ap->aal.v.aal5.forward_max_SDU_size; + msg->msg_ie_aalp->ie_aalp_5_bkwd_max_sdu = + ap->aal.v.aal5.backward_max_SDU_size; + msg->msg_ie_aalp->ie_aalp_5_mode = + UNI_IE_AALP_A5_M_MSG; + msg->msg_ie_aalp->ie_aalp_5_sscs_type = + ap->aal.v.aal5.SSCS_type; + break; + } + } + + /* + * Set traffic descriptor attributes + */ + if (ap->traffic.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_clrt) { + msg->msg_ie_clrt = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_clrt == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_clrt_absent, + &msg->msg_ie_clrt->ie_u.ie_clrt, + sizeof(ie_clrt_absent)); + msg->msg_ie_clrt->ie_ident = UNI_IE_CLRT; + msg->msg_ie_clrt->ie_clrt_fwd_peak = + ap->traffic.v.forward.PCR_high_priority; + msg->msg_ie_clrt->ie_clrt_fwd_peak_01 = + ap->traffic.v.forward.PCR_all_traffic; + msg->msg_ie_clrt->ie_clrt_fwd_sust = + ap->traffic.v.forward.SCR_high_priority; + msg->msg_ie_clrt->ie_clrt_fwd_sust_01 = + ap->traffic.v.forward.SCR_all_traffic; + msg->msg_ie_clrt->ie_clrt_fwd_burst = + ap->traffic.v.forward.MBS_high_priority; + msg->msg_ie_clrt->ie_clrt_fwd_burst_01 = + ap->traffic.v.forward.MBS_all_traffic; + msg->msg_ie_clrt->ie_clrt_bkwd_peak = + ap->traffic.v.backward.PCR_high_priority; + msg->msg_ie_clrt->ie_clrt_bkwd_peak_01 = + ap->traffic.v.backward.PCR_all_traffic; + msg->msg_ie_clrt->ie_clrt_bkwd_sust = + ap->traffic.v.backward.SCR_high_priority; + msg->msg_ie_clrt->ie_clrt_bkwd_sust_01 = + ap->traffic.v.backward.SCR_all_traffic; + msg->msg_ie_clrt->ie_clrt_bkwd_burst = + ap->traffic.v.backward.MBS_high_priority; + msg->msg_ie_clrt->ie_clrt_bkwd_burst_01 = + ap->traffic.v.backward.MBS_all_traffic; + msg->msg_ie_clrt->ie_clrt_best_effort = + ap->traffic.v.best_effort; + msg->msg_ie_clrt->ie_clrt_tm_options = 0; + if (ap->traffic.v.forward.tagging) { + msg->msg_ie_clrt->ie_clrt_tm_options |= + UNI_IE_CLRT_TM_FWD_TAG; + } + if (ap->traffic.v.backward.tagging) { + msg->msg_ie_clrt->ie_clrt_tm_options |= + UNI_IE_CLRT_TM_BKWD_TAG; + } + if (msg->msg_ie_clrt->ie_clrt_tm_options == 0) { + msg->msg_ie_clrt->ie_clrt_tm_options = + T_ATM_ABSENT; + } + } + + /* + * Set broadband bearer attributes + */ + if (ap->bearer.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_bbcp) { + msg->msg_ie_bbcp = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_bbcp == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_bbcp_absent, + &msg->msg_ie_bbcp->ie_u.ie_bbcp, + sizeof(ie_bbcp_absent)); + msg->msg_ie_bbcp->ie_ident = UNI_IE_BBCP; + msg->msg_ie_bbcp->ie_bbcp_bearer_class = + ap->bearer.v.bearer_class; + msg->msg_ie_bbcp->ie_bbcp_traffic_type = + ap->bearer.v.traffic_type; + msg->msg_ie_bbcp->ie_bbcp_timing_req = + ap->bearer.v.timing_requirements; + msg->msg_ie_bbcp->ie_bbcp_clipping = + ap->bearer.v.clipping_susceptibility; + msg->msg_ie_bbcp->ie_bbcp_conn_config = + ap->bearer.v.connection_configuration; + } + + /* + * Set broadband high layer attributes + */ + if (ap->bhli.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_bhli) { + msg->msg_ie_bhli = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_bhli == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_bhli_absent, + &msg->msg_ie_bhli->ie_u.ie_bhli, + sizeof(ie_bhli_absent)); + msg->msg_ie_bhli->ie_ident = UNI_IE_BHLI; + msg->msg_ie_bhli->ie_bhli_type = ap->bhli.v.ID_type; + switch (ap->bhli.v.ID_type) { + case T_ATM_ISO_APP_ID: + KM_COPY(ap->bhli.v.ID.ISO_ID, + msg->msg_ie_bhli->ie_bhli_info, + sizeof(ap->bhli.v.ID.ISO_ID)); + break; + case T_ATM_USER_APP_ID: + KM_COPY(ap->bhli.v.ID.user_defined_ID, + msg->msg_ie_bhli->ie_bhli_info, + sizeof(ap->bhli.v.ID.user_defined_ID)); + break; + case T_ATM_VENDOR_APP_ID: + KM_COPY(ap->bhli.v.ID.vendor_ID.OUI, + msg->msg_ie_bhli->ie_bhli_info, + sizeof(ap->bhli.v.ID.vendor_ID.OUI)); + KM_COPY(ap->bhli.v.ID.vendor_ID.app_ID, + &msg->msg_ie_bhli->ie_bhli_info[sizeof(ap->bhli.v.ID.vendor_ID.OUI)-1], + sizeof(ap->bhli.v.ID.vendor_ID.app_ID)); + break; + } + } + + /* + * Set Broadband low layer, user layer 2 and 3 attributes + */ + if (ap->blli.tag_l2 == T_ATM_PRESENT || + ap->blli.tag_l3 == T_ATM_PRESENT) { + if (!msg->msg_ie_blli) { + msg->msg_ie_blli = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_blli == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_blli_absent, + &msg->msg_ie_blli->ie_u.ie_blli, + sizeof(ie_blli_absent)); + msg->msg_ie_blli->ie_ident = UNI_IE_BLLI; + + if (ap->blli.tag_l2 == T_ATM_PRESENT) { + switch(ap->blli.v.layer_2_protocol.ID_type) { + case T_ATM_SIMPLE_ID: + msg->msg_ie_blli->ie_blli_l2_id = + ap->blli.v.layer_2_protocol.ID.simple_ID; + break; + case T_ATM_USER_ID: + msg->msg_ie_blli->ie_blli_l2_user_proto = + ap->blli.v.layer_2_protocol.ID.user_defined_ID; + break; + } + if (ap->blli.v.layer_2_protocol.ID_type != + T_ATM_ABSENT) { + msg->msg_ie_blli->ie_blli_l2_mode = + ap->blli.v.layer_2_protocol.mode; + msg->msg_ie_blli->ie_blli_l2_window = + ap->blli.v.layer_2_protocol.window_size; + } + } + + if (ap->blli.tag_l3 == T_ATM_PRESENT) { + switch (ap->blli.v.layer_3_protocol.ID_type) { + case T_ATM_SIMPLE_ID: + msg->msg_ie_blli->ie_blli_l3_id = + ap->blli.v.layer_3_protocol.ID.simple_ID; + break; + + case T_ATM_IPI_ID: + msg->msg_ie_blli->ie_blli_l3_id = + UNI_IE_BLLI_L3P_ISO9577; + msg->msg_ie_blli->ie_blli_l3_ipi = + ap->blli.v.layer_3_protocol.ID.IPI_ID; + break; + + case T_ATM_SNAP_ID: + msg->msg_ie_blli->ie_blli_l3_id = + UNI_IE_BLLI_L3P_ISO9577; + msg->msg_ie_blli->ie_blli_l3_ipi = + UNI_IE_BLLI_L3IPI_SNAP; + KM_COPY(ap->blli.v.layer_3_protocol.ID.SNAP_ID.OUI, + msg->msg_ie_blli->ie_blli_l3_oui, + sizeof(msg->msg_ie_blli->ie_blli_l3_oui)); + KM_COPY(ap->blli.v.layer_3_protocol.ID.SNAP_ID.PID, + msg->msg_ie_blli->ie_blli_l3_pid, + sizeof(msg->msg_ie_blli->ie_blli_l3_pid)); + break; + + case T_ATM_USER_ID: + msg->msg_ie_blli->ie_blli_l3_id = + UNI_IE_BLLI_L3P_USER; + msg->msg_ie_blli->ie_blli_l3_user_proto = + ap->blli.v.layer_3_protocol.ID.user_defined_ID; + break; + } + if (ap->blli.v.layer_3_protocol.ID_type + != T_ATM_ABSENT) { + msg->msg_ie_blli->ie_blli_l3_mode = + ap->blli.v.layer_3_protocol.mode; + msg->msg_ie_blli->ie_blli_l3_packet_size = + ap->blli.v.layer_3_protocol.packet_size; + msg->msg_ie_blli->ie_blli_l3_window = + ap->blli.v.layer_3_protocol.window_size; + } + } + } + + /* + * Set the called party address and subaddress + */ + if (ap->called.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_cdad) { + msg->msg_ie_cdad = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_cdad == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_cdad_absent, + &msg->msg_ie_cdad->ie_u.ie_cdad, + sizeof(ie_cdad_absent)); + msg->msg_ie_cdad->ie_ident = UNI_IE_CDAD; + ATM_ADDR_COPY(&ap->called.addr, + &msg->msg_ie_cdad->ie_cdad_addr); + + if (ap->called.subaddr.address_format != T_ATM_ABSENT) { + if (!msg->msg_ie_cdsa) { + msg->msg_ie_cdsa = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_cdsa == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_cdsa_absent, + &msg->msg_ie_cdsa->ie_u.ie_cdsa, + sizeof(ie_cdsa_absent)); + msg->msg_ie_cdsa->ie_ident = UNI_IE_CDSA; + ATM_ADDR_COPY(&ap->called.subaddr, + &msg->msg_ie_cdsa->ie_cdsa_addr); + } + } + + /* + * Set the calling party address and subaddress + */ + + if (ap->calling.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_cgad) { + msg->msg_ie_cgad = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_cgad == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_cgad_absent, + &msg->msg_ie_cgad->ie_u.ie_cgad, + sizeof(ie_cgad_absent)); + msg->msg_ie_cgsa->ie_ident = UNI_IE_CGSA; + ATM_ADDR_COPY(&ap->calling.addr, + &msg->msg_ie_cgad->ie_cgad_addr); + + if (ap->calling.subaddr.address_format != + T_ATM_ABSENT) { + if (!msg->msg_ie_cgsa) { + msg->msg_ie_cgsa = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_cgsa == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_cgsa_absent, + &msg->msg_ie_cgsa->ie_u.ie_cgsa, + sizeof(ie_cgsa_absent)); + msg->msg_ie_cgsa->ie_ident = UNI_IE_CGSA; + ATM_ADDR_COPY(&ap->calling.subaddr, + &msg->msg_ie_cgsa->ie_cgsa_addr); + } + } + + /* + * Set quality of service attributes + */ + if (ap->qos.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_qosp) { + msg->msg_ie_qosp = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_qosp == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_qosp_absent, + &msg->msg_ie_qosp->ie_u.ie_qosp, + sizeof(ie_qosp_absent)); + msg->msg_ie_qosp->ie_ident = UNI_IE_QOSP; + if (usp->us_proto == ATM_SIG_UNI30) + msg->msg_ie_qosp->ie_coding = UNI_IE_CODE_STD; + else if ((ap->qos.v.forward.qos_class == + T_ATM_QOS_CLASS_0) || + (ap->qos.v.backward.qos_class == + T_ATM_QOS_CLASS_0)) + msg->msg_ie_qosp->ie_coding = UNI_IE_CODE_CCITT; + else + msg->msg_ie_qosp->ie_coding = ap->qos.v.coding_standard; + msg->msg_ie_qosp->ie_qosp_fwd_class = + ap->qos.v.forward.qos_class; + msg->msg_ie_qosp->ie_qosp_bkwd_class = + ap->qos.v.backward.qos_class; + } + + /* + * Set transit network attributes + */ + if (ap->transit.tag == T_ATM_PRESENT && + ap->transit.v.length != 0) { + if (!msg->msg_ie_trnt) { + msg->msg_ie_trnt = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_trnt == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_trnt_absent, + &msg->msg_ie_trnt->ie_u.ie_trnt, + sizeof(ie_trnt_absent)); + msg->msg_ie_trnt->ie_ident = UNI_IE_TRNT; + msg->msg_ie_trnt->ie_trnt_id_type = + UNI_IE_TRNT_IDT_NATL; + msg->msg_ie_trnt->ie_trnt_id_plan = + UNI_IE_TRNT_IDP_CIC; + KM_COPY(ap->transit.v.network_id, + msg->msg_ie_trnt->ie_trnt_id, + ap->transit.v.length); + } + + /* + * Set cause code + */ + if (ap->cause.tag == T_ATM_PRESENT) { + if (!msg->msg_ie_caus) { + msg->msg_ie_caus = (struct ie_generic *) + atm_allocate(&unisig_iepool); + if (msg->msg_ie_caus == NULL) { + err = ENOMEM; + goto done; + } + } + KM_COPY(&ie_caus_absent, + &msg->msg_ie_caus->ie_u.ie_caus, + sizeof(ie_caus_absent)); + msg->msg_ie_caus->ie_ident = UNI_IE_CAUS; + msg->msg_ie_caus->ie_coding = + ap->cause.v.coding_standard; + msg->msg_ie_caus->ie_caus_loc = + ap->cause.v.location; + msg->msg_ie_caus->ie_caus_cause = + ap->cause.v.cause_value; + + /* + * Don't copy the diagnostics from the attribute + * block, as there's no way to tell how much of + * the diagnostic field is relevant + */ + msg->msg_ie_caus->ie_caus_diag_len = 0; + } + +done: + return(err); +} diff --git a/sys/netatm/uni/unisig_util.c b/sys/netatm/uni/unisig_util.c new file mode 100644 index 0000000..ec4fbd0 --- /dev/null +++ b/sys/netatm/uni/unisig_util.c @@ -0,0 +1,389 @@ +/* + * + * =================================== + * 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: unisig_util.c,v 1.9 1998/08/26 23:29:24 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Protocol processing module + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_util.c,v 1.9 1998/08/26 23:29:24 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> + + +/* + * Free a UNISIG signalling message + * + * Free the passed message and any IEs that are attached to it + * + * Arguments: + * msg pointer to UNISIG protocol instance + * + * Returns: + * none + * + */ +void +unisig_free_msg(msg) + struct unisig_msg *msg; +{ + int i; + struct ie_generic *ie, *ienxt; + + ATM_DEBUG1("unisig_free_msg: msg=0x%x\n", msg); + + /* + * First free all the IEs + */ + for (i=0; i<UNI_MSG_IE_CNT; i++) { + ie = msg->msg_ie_vec[i]; + while (ie) { + ienxt = ie->ie_next; + atm_free(ie); + ie = ienxt; + } + } + + /* + * Finally, free the message structure itself + */ + atm_free(msg); +} + + +/* + * Verify a VCCB + * + * Search UNISIG's VCCB queue to verify that a VCCB belongs to UNISIG. + * + * Arguments: + * usp pointer to UNISIG protocol instance + * svp pointer to a VCCB + * + * Returns: + * TRUE the VCCB belongs to UNISIG + * FALSE the VCCB doesn't belong to UNISIG + * + */ +int +unisig_verify_vccb(usp, uvp) + struct unisig *usp; + struct unisig_vccb *uvp; + +{ + struct unisig_vccb *utp, *uvnext; + + for (utp = Q_HEAD(usp->us_vccq, struct unisig_vccb); + utp; utp = uvnext){ + uvnext = Q_NEXT(utp, struct unisig_vccb, uv_sigelem); + if (uvp == utp) { + return(TRUE); + } + } + return(FALSE); +} + + +/* + * Find a connection + * + * Find a VCCB given the call reference + * + * Arguments: + * usp pointer to UNISIG protocol instance + * cref the call reference to search for + * + * Returns: + * 0 there is no such VCCB + * uvp the address of the VCCB + * + */ +struct unisig_vccb * +unisig_find_conn(usp, cref) + struct unisig *usp; + u_int cref; + +{ + struct unisig_vccb *uvp, *uvnext; + + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; + uvp = uvnext){ + uvnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + if (uvp->uv_call_ref == cref) + break; + } + return(uvp); +} + + +/* + * Find a VCCB + * + * Find a VCCB given the VPI and VCI. + * + * Arguments: + * usp pointer to UNISIG protocol instance + * vpi the VPI to search for + * vci the VCI to search for + * dir the direction of the VCC (VCC_IN, VCC_OUT, or both). + * If dir is set to zero, return the address of any VCCB + * with the given VPI/VCI, regardless of direction. + * + * Returns: + * 0 there is no such VCCB + * uvp the address of the VCCB + * + */ +struct unisig_vccb * +unisig_find_vpvc(usp, vpi, vci, dir) + struct unisig *usp; + int vpi, vci; + u_char dir; + +{ + struct unisig_vccb *uvp, *uvnext; + + for (uvp = Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; + uvp = uvnext){ + uvnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); + if (uvp->uv_vpi == vpi && + uvp->uv_vci == vci && + (uvp->uv_type & dir) == dir) + break; + } + return(uvp); +} + + +/* + * Allocate a call reference value + * + * Arguments: + * usp pointer to UNISIG protocol instance + * + * Returns: + * 0 call reference not available + * cref the call reference value + * + */ +int +unisig_alloc_call_ref(usp) + struct unisig *usp; + +{ + int cref; + + /* + * Get the next call reference value + */ + cref = usp->us_cref; + + /* + * Make sure it hasn't got too large + */ + if (cref >= UNI_MSG_CALL_REF_DUMMY) { + /* XXX */ + log(LOG_ERR, "uni: call reference limit reached\n"); + return(0); + } + + /* + * Bump the call reference value + */ + usp->us_cref++; + + return(cref); +} + + +/* + * Print an ATM address + * + * Convert an ATM address into an ASCII string suitable for printing. + * + * Arguments: + * p pointer to an ATM address + * + * Returns: + * the address of a string with the ASCII representation of the + * address. This routine returns the address of a statically- + * allocated buffer, so if repeated calls to this routine are made, + * each call will destroy the result of the previous call. + * + */ +char * +unisig_addr_print(p) + Atm_addr *p; +{ + int i; + char *fp, *op, t_buff[16]; + u_char *cp; + static char strbuff[256]; + + static char nf_DCC[] = "0xX.XX.X.XXX.XX.XX.XX.XXXXXX.X"; + static char nf_ICD[] = "0xX.XX.X.XXX.XX.XX.XX.XXXXXX.X"; + static char nf_E164[] = "0xX.XXXXXXXX.XX.XX.XXXXXX.X"; + + union { + int w; + char c[4]; + } u1, u2; + + /* + * Clear the print buffer + */ + KM_ZERO(strbuff, sizeof(strbuff)); + + /* + * Select appropriate printing format + */ + switch(p->address_format) { + case T_ATM_ENDSYS_ADDR: + /* + * Select format by NSAP type + */ + switch(((Atm_addr_nsap *)p->address)->aan_afi) { + default: + case AFI_DCC: + fp = nf_DCC; + break; + case AFI_ICD: + fp = nf_ICD; + break; + case AFI_E164: + fp = nf_E164; + break; + } + + /* + * Loop through the format string, converting the NSAP + * to ASCII + */ + cp = (u_char *) p->address; + op = strbuff; + while (*fp) { + if (*fp == 'X') { + /* + * If format character is an 'X', put a + * two-digit hex representation of the + * NSAP byte in the output buffer + */ + sprintf(t_buff, "%x", *cp + 512); + strcpy(op, &t_buff[strlen(t_buff)-2]); + op++; op++; + cp++; + } else { + /* + * If format character isn't an 'X', + * just copy it to the output buffer + */ + *op = *fp; + op++; + } + fp++; + } + + break; + + case T_ATM_E164_ADDR: + /* + * Print the IA5 characters of the E.164 address + */ + for(i=0; i<p->address_length; i++) { + sprintf(&strbuff[strlen(strbuff)], "%c\0", + ((Atm_addr_e164 *)p->address)->aae_addr[i]); + } + break; + + case T_ATM_SPANS_ADDR: + /* + * Get address into integers + */ + u1.c[0] = ((Atm_addr_spans *)p->address)->aas_addr[0]; + u1.c[1] = ((Atm_addr_spans *)p->address)->aas_addr[1]; + u1.c[2] = ((Atm_addr_spans *)p->address)->aas_addr[2]; + u1.c[3] = ((Atm_addr_spans *)p->address)->aas_addr[3]; + u2.c[0] = ((Atm_addr_spans *)p->address)->aas_addr[4]; + u2.c[1] = ((Atm_addr_spans *)p->address)->aas_addr[5]; + u2.c[2] = ((Atm_addr_spans *)p->address)->aas_addr[6]; + u2.c[3] = ((Atm_addr_spans *)p->address)->aas_addr[7]; + + /* + * Print the address as two words xxxxx.yyyyyyyy + */ + sprintf(strbuff, "%x.%x", u1.w, u2.w); + break; + + case T_ATM_ABSENT: + default: + strcpy(strbuff, "-"); + } + + return(strbuff); +} + + +/* + * Print the contents of a message buffer chain + * + * Arguments: + * m pointer to a buffer + * + * Returns: + * none + * + */ +void +unisig_print_mbuf(m) + KBuffer *m; +{ + int i; + caddr_t cp; + + printf("unisig_print_mbuf:\n"); + while (m) { + KB_DATASTART(m, cp, caddr_t); + for (i = 0; i < KB_LEN(m); i++) { + if (i == 0) + printf(" bfr=0x%x: ", (int)m); + printf("%x ", (u_char)*cp++); + } + printf("<end_bfr>\n"); + m = KB_NEXT(m); + } +} diff --git a/sys/netatm/uni/unisig_var.h b/sys/netatm/uni/unisig_var.h new file mode 100644 index 0000000..1e3d01a7 --- /dev/null +++ b/sys/netatm/uni/unisig_var.h @@ -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: unisig_var.h,v 1.12 1998/07/24 20:24:34 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * Protocol control blocks + * + */ + +#ifndef _UNISIG_VAR_H +#define _UNISIG_VAR_H + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifdef ATM_KERNEL +/* + * Structure containing state information for each UNI protocol + * instance. There will be one instance for each ATM device interface + * using the UNI signalling manager. + */ +struct unisig { + struct siginst us_inst; /* Header */ + struct atm_time us_time; /* Timer controls */ + void (*us_lower) /* Lower command handler */ + __P((int, void *, int, int)); + Atm_connection *us_conn; /* Signalling connection */ + int us_cref; /* Call reference allocation */ + u_int us_retry; /* Protocol retry count */ + u_short us_headout; /* Headroom on sig ch output */ + u_char us_proto; /* Signalling version */ +}; + +#define us_next us_inst.si_next +#define us_pif us_inst.si_pif +#define us_addr us_inst.si_addr +#define us_subaddr us_inst.si_subaddr +#define us_vccq us_inst.si_vccq +#define us_state us_inst.si_state +#define us_ipserv us_inst.si_ipserv +#endif /* ATM_KERNEL */ + +/* + * Signalling manager states + */ +#define UNISIG_NULL 0 +#define UNISIG_ADDR_WAIT 1 +#define UNISIG_INIT 2 +#define UNISIG_ACTIVE 3 +#define UNISIG_DETACH 4 + +/* + * Signalling manager events + */ +#define UNISIG_SIGMGR_TIMEOUT 0 +#define UNISIG_SIGMGR_SSCF_EST_IND 1 +#define UNISIG_SIGMGR_SSCF_EST_CNF 2 +#define UNISIG_SIGMGR_SSCF_RLS_IND 3 +#define UNISIG_SIGMGR_SSCF_RLS_CNF 4 +#define UNISIG_SIGMGR_SSCF_DATA_IND 5 +#define UNISIG_SIGMGR_SSCF_UDATA_IND 6 +#define UNISIG_SIGMGR_CALL_CLEARED 7 +#define UNISIG_SIGMGR_DETACH 8 +#define UNISIG_SIGMGR_ADDR_SET 9 + +/* + * Signalling manager timer values + */ +#define UNISIG_SSCF_TIMEOUT (3 * ATM_HZ) + + +#ifdef ATM_KERNEL +/* + * UNI Virtual Channel Connection control block. All information + * regarding the state of a UNI-controlled VCC will be recorded here. + * There will be one UNI VCC control block for each UNI-controlled + * VCC. + */ +struct unisig_vccb { + struct vccb vcp_hdr; /* Generic VCCB */ + u_short uv_retry; /* Xmit retry count */ + u_int uv_call_ref; /* Q.2931 call reference */ +}; + +#define uv_type vcp_hdr.vc_type +#define uv_proto vcp_hdr.vc_proto +#define uv_sstate vcp_hdr.vc_sstate +#define uv_ustate vcp_hdr.vc_ustate +#define uv_pif vcp_hdr.vc_pif +#define uv_nif vcp_hdr.vc_nif +#define uv_sigelem vcp_hdr.vc_sigelem +#define uv_time vcp_hdr.vc_time +#define uv_vpi vcp_hdr.vc_vpi +#define uv_vci vcp_hdr.vc_vci +#define uv_connvc vcp_hdr.vc_connvc +#define uv_ipdus vcp_hdr.vc_ipdus +#define uv_opdus vcp_hdr.vc_opdus +#define uv_ibytes vcp_hdr.vc_ibytes +#define uv_obytes vcp_hdr.vc_obytes +#define uv_ierrors vcp_hdr.vc_ierrors +#define uv_oerrors vcp_hdr.vc_oerrors +#define uv_tstamp vcp_hdr.vc_tstamp +#endif /* ATM_KERNEL */ + +/* + * UNI VCC protocol states. Taken from The ATM Forum UNI 3.0 (section + * 5.2.1.1) + */ +#define UNI_NULL 0 /* No call exists */ +#define UNI_CALL_INITIATED 1 /* Initiating call */ +#define UNI_CALL_OUT_PROC 3 /* Outgoing call proceeding */ +#define UNI_CALL_DELIVERED 4 /* Not supported */ +#define UNI_CALL_PRESENT 6 /* Call coming in */ +#define UNI_CALL_RECEIVED 7 /* Not supported */ +#define UNI_CONNECT_REQUEST 8 /* Call coming in */ +#define UNI_CALL_IN_PROC 9 /* Incoming call proceeding */ +#define UNI_ACTIVE 10 /* Call is established */ +#define UNI_RELEASE_REQUEST 11 /* Clearing call */ +#define UNI_RELEASE_IND 12 /* Network disconnecting */ + +/* + * Additional states required for internal management of VCCs + */ +#define UNI_SSCF_RECOV 13 /* Signalling chan recovery */ +#define UNI_FREE 14 /* Waiting for user to free */ +#define UNI_PVC_ACTIVE 15 /* PVC Active */ +#define UNI_PVC_ACT_DOWN 16 /* PVC Active - i/f down */ + +/* + * UNI VCC events + */ +#define UNI_VC_TIMEOUT 0 /* Timer expired */ +#define UNI_VC_CALLP_MSG 1 /* CALL PROCEEDING message */ +#define UNI_VC_CONNECT_MSG 2 /* CONNECT message */ +#define UNI_VC_CNCTACK_MSG 3 /* CONNECT ACK message */ +#define UNI_VC_SETUP_MSG 4 /* SETUP message */ +#define UNI_VC_RELEASE_MSG 5 /* RELEASE message */ +#define UNI_VC_RLSCMP_MSG 6 /* RELEASE COMPLETE message */ +#define UNI_VC_STATUS_MSG 7 /* STATUS message */ +#define UNI_VC_STATUSENQ_MSG 8 /* STATUS ENQ message */ +#define UNI_VC_ADDP_MSG 9 /* ADD PARTY message */ +#define UNI_VC_ADDPACK_MSG 10 /* ADD PARTY ACK message */ +#define UNI_VC_ADDPREJ_MSG 11 /* ADD PARTY REJ message */ +#define UNI_VC_DROP_MSG 12 /* DROP PARTY message */ +#define UNI_VC_DROPACK_MSG 13 /* DROP PARTY ACK message */ +#define UNI_VC_SETUP_CALL 14 /* Setup routine called */ +#define UNI_VC_ACCEPT_CALL 15 /* Accept call routine called */ +#define UNI_VC_REJECT_CALL 16 /* Reject call routine called */ +#define UNI_VC_RELEASE_CALL 17 /* Release routine called */ +#define UNI_VC_ABORT_CALL 18 /* Abort routine called */ +#define UNI_VC_SAAL_FAIL 19 /* Signalling AAL failed */ +#define UNI_VC_SAAL_ESTAB 20 /* Signalling AAL back up */ + + +#ifdef ATM_KERNEL +/* + * UNI Timer Values. These values (except for T317) are taken from + * The ATM Forum UNI 3.0 (section 5.7.2). + */ +#define UNI_T303 (4 * ATM_HZ) +#define UNI_T308 (30 * ATM_HZ) +#define UNI_T309 (10 * ATM_HZ) +#define UNI_T310 (10 * ATM_HZ) +#define UNI_T313 (4 * ATM_HZ) +#define UNI_T316 (120 * ATM_HZ) +#define UNI_T317 (60 * ATM_HZ) +#define UNI_T322 (4 * ATM_HZ) +#define UNI_T398 (4 * ATM_HZ) +#define UNI_T399 (14 * ATM_HZ) + + +/* + * Timer macros + */ +#define UNISIG_TIMER(s, t) atm_timeout(&(s)->us_time, (t), unisig_timer) +#define UNISIG_CANCEL(s) atm_untimeout(&(s)->us_time) +#define UNISIG_VC_TIMER(v, t) atm_timeout(&(v)->vc_time, (t), unisig_vctimer) +#define UNISIG_VC_CANCEL(v) atm_untimeout(&(v)->vc_time) + + +/* + * Global function declarations + */ +struct usfmt; +struct unisig_msg; + + /* unisig_decode.c */ +int usf_dec_msg __P((struct usfmt *, struct unisig_msg *)); + + /* unisig_encode.c */ +int usf_enc_msg __P((struct usfmt *, struct unisig_msg *)); + + /* unisig_if.c */ +int unisig_start __P((void)); +int unisig_stop __P((void)); +int unisig_free __P((struct vccb *)); + + /* unisig_mbuf.c */ +int usf_init __P((struct usfmt *, struct unisig *, KBuffer *, int, int)); +int usf_byte __P((struct usfmt *, u_char *)); +int usf_short __P((struct usfmt *, u_short *)); +int usf_int3 __P((struct usfmt *, u_int *)); +int usf_int __P((struct usfmt *, u_int *)); +int usf_ext __P((struct usfmt *, u_int *)); +int usf_count __P((struct usfmt *)); +int usf_byte_mark __P((struct usfmt *, u_char *, u_char **)); + + /* unisig_msg.c */ +struct ie_generic; +void unisig_cause_from_attr __P((struct ie_generic *, + Atm_attributes *)); +void unisig_cause_from_msg __P((struct ie_generic *, + struct unisig_msg *, int)); +int unisig_send_msg __P((struct unisig *, + struct unisig_msg *)); +int unisig_send_setup __P((struct unisig *, + struct unisig_vccb *)); +int unisig_send_release __P((struct unisig *, + struct unisig_vccb *, + struct unisig_msg *, + int)); +int unisig_send_release_complete __P((struct unisig *, + struct unisig_vccb *, + struct unisig_msg *, + int)); +int unisig_send_status __P((struct unisig *, + struct unisig_vccb *, + struct unisig_msg *, + int)); +int unisig_rcv_msg __P((struct unisig *, KBuffer *)); + + /* unisig_print.c */ +void usp_print_msg __P((struct unisig_msg *, int)); + + /* unisig_proto.c */ +void unisig_timer __P((struct atm_time *)); +void unisig_vctimer __P((struct atm_time *)); +void unisig_saal_ctl __P((int, void *, void *)); +void unisig_saal_data __P((void *, KBuffer *)); +caddr_t unisig_getname __P((void *)); +void unisig_connected __P((void *)); +void unisig_cleared __P((void *, struct t_atm_cause *)); + + /* unisig_sigmgr_state.c */ +int unisig_sigmgr_state __P((struct unisig *, int, + KBuffer *)); + + /* unisig_subr.c */ +void unisig_set_cause_attr __P((Atm_attributes *, int)); +int unisig_open_vcc __P((struct unisig *, Atm_connvc *)); +int unisig_close_vcc __P((struct unisig *, + struct unisig_vccb *)); +int unisig_clear_vcc __P((struct unisig *, + struct unisig_vccb *, int)); +void unisig_switch_reset __P((struct unisig *, int)); +void unisig_save_attrs __P((struct unisig *, struct unisig_msg *, + Atm_attributes *)); +int unisig_set_attrs __P((struct unisig *, struct unisig_msg *, + Atm_attributes *)); + + /* unisig_util.c */ +void unisig_free_msg __P((struct unisig_msg *)); +int unisig_verify_vccb __P((struct unisig *, + struct unisig_vccb *)); +struct unisig_vccb * + unisig_find_conn __P((struct unisig *, u_int)); +struct unisig_vccb * + unisig_find_vpvc __P((struct unisig *, int, int, + u_char)); +int unisig_alloc_call_ref __P((struct unisig *)); +char * unisig_addr_print __P((Atm_addr *)); +void unisig_print_mbuf __P((KBuffer *)); +void unisig_print_buffer __P((KBuffer *)); + + /* unisig_vc_state.c */ +int unisig_vc_state __P((struct unisig *, + struct unisig_vccb *, + int, + struct unisig_msg *)); + + +/* + * External variables + */ +extern struct sp_info unisig_vcpool; +extern struct sp_info unisig_msgpool; +extern struct sp_info unisig_iepool; + +#endif /* ATM_KERNEL */ + +#endif /* _UNISIG_VAR_H */ diff --git a/sys/netatm/uni/unisig_vc_state.c b/sys/netatm/uni/unisig_vc_state.c new file mode 100644 index 0000000..cfd7635 --- /dev/null +++ b/sys/netatm/uni/unisig_vc_state.c @@ -0,0 +1,2223 @@ +/* + * + * =================================== + * 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: unisig_vc_state.c,v 1.11 1998/08/06 18:16:29 mks Exp $ + * + */ + +/* + * ATM Forum UNI 3.0/3.1 Signalling Manager + * ---------------------------------------- + * + * VC state machine + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: unisig_vc_state.c,v 1.11 1998/08/06 18:16:29 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/unisig.h> +#include <netatm/uni/unisig_var.h> +#include <netatm/uni/unisig_msg.h> +#include <netatm/uni/unisig_mbuf.h> +#include <netatm/uni/unisig_decode.h> + + +/* + * Local functions + */ +static int unisig_vc_invalid __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act01 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act02 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act03 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act04 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act05 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act06 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act07 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act08 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act09 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act10 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act11 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act12 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act13 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act14 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act15 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act16 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act17 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act18 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act19 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act20 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act21 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act22 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act23 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act24 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act25 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act26 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act27 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act28 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act29 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act30 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_act31 __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)); +static int unisig_vc_clear_call __P((struct unisig *, + struct unisig_vccb *, + struct unisig_msg *, + int)); + + +/* + * State table + */ +static int unisig_vc_states[21][17] = { +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 */ +{ 0, 2, 99, 5, 99, 99, 0, 99, 12, 99, 0, 14, 0, 3, 0, 0, 0 }, +{ 29, 4, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 29, 6, 99, 6, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 29, 17, 99, 17, 99, 99, 17, 99, 10, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 8, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 29, 7, 99, 15, 99, 99, 15, 99, 15, 99, 15, 16, 17, 0, 0, 0, 0 }, +{ 19, 3, 99, 3, 99, 99, 3, 99, 3, 99, 3, 13, 3, 0, 0, 0, 0 }, +{ 21, 21, 99, 21, 99, 99, 21, 99, 21, 99, 21, 21, 21, 0, 0, 0, 0 }, +{ 22, 22, 99, 22, 99, 99, 22, 99, 22, 99, 22, 22, 22, 0, 0, 0, 0 }, +{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 23, 17, 17, 0, 0, 0, 0 }, +{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 29, 17, 99, 17, 99, 99, 17, 99, 17, 99, 17, 17, 17, 0, 0, 0, 0 }, +{ 1, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }, +{ 99, 25, 99, 25, 99, 99, 9, 99, 25, 99, 25, 25, 25, 25, 31, 25, 25 }, +{ 99, 25, 99, 25, 99, 99, 11, 99, 25, 99, 25, 25, 25, 25, 19, 25, 25 }, +{ 99, 12, 99, 12, 99, 99, 25, 99, 12, 99, 12, 19, 19, 30, 19, 99, 99 }, +{ 99, 12, 99, 12, 99, 99, 12, 99, 12, 99, 12, 3, 3, 3, 24, 26, 26 }, +{ 99, 3, 99, 3, 99, 99, 30, 99, 3, 99, 18, 3, 3, 0, 19, 27, 19 }, +{ 99, 7, 99, 7, 99, 99, 30, 99, 7, 99, 19, 19, 19, 20, 19, 19, 28 } +}; + + +/* + * Action vector + * + * A given state, action pair selects an action number from the + * state table. This vector holds the address of the action routine + * for each action number. + */ +#define MAX_ACTION 32 +static int (*unisig_vc_act_vec[MAX_ACTION]) + __P((struct unisig *, struct unisig_vccb *, + struct unisig_msg *)) = { + unisig_vc_invalid, + unisig_vc_act01, + unisig_vc_act02, + unisig_vc_act03, + unisig_vc_act04, + unisig_vc_act05, + unisig_vc_act06, + unisig_vc_act07, + unisig_vc_act08, + unisig_vc_act09, + unisig_vc_act10, + unisig_vc_act11, + unisig_vc_act12, + unisig_vc_act13, + unisig_vc_act14, + unisig_vc_act15, + unisig_vc_act16, + unisig_vc_act17, + unisig_vc_act18, + unisig_vc_act19, + unisig_vc_act20, + unisig_vc_act21, + unisig_vc_act22, + unisig_vc_act23, + unisig_vc_act24, + unisig_vc_act25, + unisig_vc_act26, + unisig_vc_act27, + unisig_vc_act28, + unisig_vc_act29, + unisig_vc_act30, + unisig_vc_act31 +}; + + +/* + * Process an event on a VC + * + * Arguments: + * usp pointer to the UNISIG instance + * uvp pointer to the VCCB for the affected VCC + * event a numeric indication of which event has occured + * msg pointer to a signalling message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +int +unisig_vc_state(usp, uvp, event, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + int event; + struct unisig_msg *msg; +{ + int action, rc, state; + + /* + * Select an action from the state table + */ + if (uvp) + state = uvp->uv_sstate; + else + state = UNI_NULL; + action = unisig_vc_states[event][state]; + if (action >= MAX_ACTION || action < 0) + panic("unisig_vc_state: invalid action\n"); + + /* + * Perform the requested action + */ + ATM_DEBUG4("unisig_vc_state: uvp=0x%x, state=%d, event=%d, action=%d\n", + (int) uvp, state, event, action); + rc = unisig_vc_act_vec[action](usp, uvp, msg); + + return(rc); +} + + +/* + * VC state machine action 0 + * Unexpected action - log an error message + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection (may + be null) + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_invalid(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + log(LOG_ERR, "unisig_vc_state: unexpected action\n"); + return(EINVAL); +} + + +/* + * VC state machine action 1 + * Setup handler called + * + * Send SETUP, start timer T303, go to UNI_CALL_INITIATED state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act01(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Send the setup message + */ + rc = unisig_send_setup(usp, uvp); + if (rc) + return(rc); + + /* + * Set timer T303 + */ + uvp->uv_retry = 0; + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T303); + + /* + * Set the new state + */ + uvp->uv_sstate = UNI_CALL_INITIATED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); +} + + +/* + * VC state machine action 2 + * Timeout while waiting for CALL PROCEEDING or CONNECT + * + * If this is the second expiration, clear the call. Otherwise, + * retransmit the SETUP message and restart T303. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act02(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc = 0; + + if (uvp->uv_retry) { + /* + * Clear the call + */ + rc = unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NO_ROUTE_TO_DESTINATION); + } else { + uvp->uv_retry++; + (void) unisig_send_setup(usp, uvp); + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T303); + } + + return(rc); +} + + +/* + * VC state machine action 3 + * + * Clear the call internally + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act03(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Clear the VCCB + */ + rc = unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_DESTINATION_OUT_OF_ORDER); + + return(rc); +} + + +/* + * VC state machine action 4 + * Received CALL PROCEEDING + * + * Start timer T310, go to UNI_CALL_OUT_PROC + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act04(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int cause, rc, vpi, vci; + struct atm_pif *pip = usp->us_pif; + struct ie_generic *iep; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Make sure a Connection ID is part of the message + */ + if (msg->msg_ie_cnid) { + vpi = msg->msg_ie_cnid->ie_cnid_vpci; + vci = msg->msg_ie_cnid->ie_cnid_vci; + } else { + iep = (struct ie_generic *)atm_allocate(&unisig_iepool); + if (!iep) + return(ENOMEM); + iep->ie_ident = UNI_IE_CNID; + iep->ie_err_cause = UNI_IE_CAUS_MISSING; + MSG_IE_ADD(msg, iep, UNI_MSG_IE_ERR); + cause = UNI_IE_CAUS_MISSING; + ATM_DEBUG0("unisig_vc_act04: no CNID in Call Proc\n"); + goto response04; + } + + /* + * Make sure we can handle the specified VPI and VCI + */ + if (vpi > pip->pif_maxvpi || vci > pip->pif_maxvci || + vci < UNI_IE_CNID_MIN_VCI) { + cause = UNI_IE_CAUS_BAD_VCC; + ATM_DEBUG0("unisig_vc_act04: VPI/VCI invalid\n"); + goto response04; + } + + /* + * Make sure the specified VPI and VCI are not in use + */ + if (unisig_find_vpvc(usp, vpi, vci, VCC_OUT)) { + cause = UNI_IE_CAUS_NA_VCC; + ATM_DEBUG0("unisig_vc_act04: VPI/VCI in use\n"); + goto response04; + } + + /* + * Start timer T310 + */ + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T310); + + /* + * Save the specified VPI and VCI + */ + uvp->uv_vpi = vpi; + uvp->uv_vci = vci; + + /* + * Set the state + */ + uvp->uv_sstate = UNI_CALL_OUT_PROC; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); + +response04: + /* + * Initiate call clearing + */ + rc = unisig_vc_clear_call(usp, uvp, msg, cause); + + return(rc); +} + + +/* + * VC state machine action 5 + * Timeout in UNI_CALL_OUT_PROC + * + * Clear call towards network + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act05(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + struct unisig_msg *rls_msg; + struct ie_generic *cause_ie; + + /* + * Send a RELEASE message + */ + rls_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (rls_msg == NULL) + return(ENOMEM); + cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (cause_ie == NULL) { + atm_free(rls_msg); + return(ENOMEM); + } + + /* + * Fill out the RELEASE message + */ + rls_msg->msg_call_ref = uvp->uv_call_ref; + rls_msg->msg_type = UNI_MSG_RLSE; + rls_msg->msg_type_flag = 0; + rls_msg->msg_type_action = 0; + rls_msg->msg_ie_caus = cause_ie; + + /* + * Fill out the cause IE + */ + cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; + cause_ie->ie_caus_cause = UNI_IE_CAUS_TIMER; + KM_COPY("310", cause_ie->ie_caus_diagnostic, 3); + + /* + * Send the RELEASE message. + */ + rc = unisig_send_msg(usp, rls_msg); + unisig_free_msg(rls_msg); + + /* + * Start timer T308 + */ + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T308); + + /* + * Set the new state + */ + uvp->uv_sstate = UNI_RELEASE_REQUEST; + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(rc); +} + + +/* + * VC state machine action 6 + * Received CONNECT + * + * Send CONNECT ACK, go to UNI_ACTIVE state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act06(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int cause, rc, vci, vpi; + struct atm_pif *pip = usp->us_pif; + struct unisig_msg *cack_msg; + struct ie_generic *iep; + Atm_attributes *ap; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + ap = &uvp->uv_connvc->cvc_attr; + + /* + * See if a VPI/VCI is specified + */ + if (msg->msg_ie_cnid) { + /* + * Yes--VPI/VCI must be the first specification or must + * match what was specified before + */ + vpi = msg->msg_ie_cnid->ie_cnid_vpci; + vci = msg->msg_ie_cnid->ie_cnid_vci; + if ((uvp->uv_vpi || uvp->uv_vci) && + (vpi != uvp->uv_vpi || + vci != uvp->uv_vci)) { + cause = UNI_IE_CAUS_BAD_VCC; + ATM_DEBUG0("unisig_vc_act06: VPI/VCI invalid\n"); + goto response06; + } + + /* + * Specified VPI/VCI must be within range + */ + if (vpi > pip->pif_maxvpi || vci > pip->pif_maxvci || + vci < UNI_IE_CNID_MIN_VCI) { + cause = UNI_IE_CAUS_BAD_VCC; + ATM_DEBUG0("unisig_vc_act06: VPI/VCI invalid\n"); + goto response06; + } + uvp->uv_vpi = vpi; + uvp->uv_vci = vci; + } else { + /* + * No--VPI/VCI must have been specified earlier + */ + if (!uvp->uv_vpi || !uvp->uv_vci) { + iep = (struct ie_generic *)atm_allocate( + &unisig_iepool); + if (!iep) + return(ENOMEM); + iep->ie_ident = UNI_IE_CNID; + iep->ie_err_cause = UNI_IE_CAUS_MISSING; + MSG_IE_ADD(msg, iep, UNI_MSG_IE_ERR); + cause = UNI_IE_CAUS_MISSING; + ATM_DEBUG0("unisig_vc_act06: CNID missing\n"); + goto response06; + } + } + + /* + * Handle AAL parameters negotiation + */ + if (msg->msg_ie_aalp) { + struct ie_generic *aalp = msg->msg_ie_aalp; + + /* + * AAL parameters must have been sent in SETUP + */ + if ((ap->aal.tag != T_ATM_PRESENT) || + (ap->aal.type != aalp->ie_aalp_aal_type)) { + cause = UNI_IE_CAUS_IECONTENT; + goto response06; + } + + switch (aalp->ie_aalp_aal_type) { + + case UNI_IE_AALP_AT_AAL3: + /* + * Maximum SDU size negotiation + */ + if (aalp->ie_aalp_4_fwd_max_sdu == T_ATM_ABSENT) + break; + if ((ap->aal.v.aal4.forward_max_SDU_size < + aalp->ie_aalp_4_fwd_max_sdu) || + (ap->aal.v.aal4.backward_max_SDU_size < + aalp->ie_aalp_4_bkwd_max_sdu)) { + cause = UNI_IE_CAUS_IECONTENT; + goto response06; + } else { + ap->aal.v.aal4.forward_max_SDU_size = + aalp->ie_aalp_4_fwd_max_sdu; + ap->aal.v.aal4.backward_max_SDU_size = + aalp->ie_aalp_4_bkwd_max_sdu; + } + break; + + case UNI_IE_AALP_AT_AAL5: + /* + * Maximum SDU size negotiation + */ + if (aalp->ie_aalp_5_fwd_max_sdu == T_ATM_ABSENT) + break; + if ((ap->aal.v.aal5.forward_max_SDU_size < + aalp->ie_aalp_5_fwd_max_sdu) || + (ap->aal.v.aal5.backward_max_SDU_size < + aalp->ie_aalp_5_bkwd_max_sdu)) { + cause = UNI_IE_CAUS_IECONTENT; + goto response06; + } else { + ap->aal.v.aal5.forward_max_SDU_size = + aalp->ie_aalp_5_fwd_max_sdu; + ap->aal.v.aal5.backward_max_SDU_size = + aalp->ie_aalp_5_bkwd_max_sdu; + } + break; + } + } + + /* + * Get memory for a CONNECT ACK message + */ + cack_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (cack_msg == NULL) + return(ENOMEM); + + /* + * Fill out the CONNECT ACK message + */ + cack_msg->msg_call_ref = uvp->uv_call_ref; + cack_msg->msg_type = UNI_MSG_CACK; + cack_msg->msg_type_flag = 0; + cack_msg->msg_type_action = 0; + + /* + * Send the CONNECT ACK message + */ + rc = unisig_send_msg(usp, cack_msg); + unisig_free_msg(cack_msg); + + /* + * Set the new state + */ + uvp->uv_sstate = UNI_ACTIVE; + uvp->uv_ustate = VCCU_OPEN; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + /* + * Notify the user that the connection is now active + */ + atm_cm_connected(uvp->uv_connvc); + + return(0); + +response06: + /* + * Initiate call clearing + */ + rc = unisig_vc_clear_call(usp, uvp, msg, cause); + + return(rc); +} + + +/* + * VC state machine action 7 + * Abort routine called or signalling SAAL session reset while in + * one of the call setup states + * + * Clear the call, send RELEASE COMPLETE, notify the user. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act07(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Send a RELEASE COMPLETE message rejecting the connection + */ + rc = unisig_send_release_complete(usp, uvp, msg, + UNI_IE_CAUS_TEMP); + + /* + * Clear the call VCCB + */ + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + /* + * Notify the user + */ + unisig_set_cause_attr(&uvp->uv_connvc->cvc_attr, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + atm_cm_cleared(uvp->uv_connvc); + + return(rc); +} + + +/* + * VC state machine action 8 + * Received SETUP + * + * Check call paramaters, notify user that a call has been received, + * set UNI_CALL_PRESENT state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act08(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int cause = 0, rc, vpi, vci; + struct atm_pif *pip = usp->us_pif; + struct atm_nif *nip; + Atm_addr_nsap *nap; + Atm_attributes attr; + + ATM_DEBUG3("unisig_vc_act08: usp=0x%x, uvp=0x%x, msg=0x%x\n", + (int) usp, (int) uvp, (int) msg); + + /* + * Make sure that the called address is the right format + */ + if (msg->msg_ie_cdad->ie_cdad_plan != UNI_IE_CDAD_PLAN_NSAP) { + cause = UNI_IE_CAUS_IECONTENT; + ATM_DEBUG0("unisig_vc_act08: bad address format\n"); + goto response08; + } + + /* + * Make sure that the called address is ours + */ + nap = (Atm_addr_nsap *) msg->msg_ie_cdad->ie_cdad_addr.address; + if (bcmp(usp->us_addr.address, nap, /* XXX */ + sizeof(Atm_addr_nsap)-1)) { + cause = UNI_IE_CAUS_IECONTENT; + ATM_DEBUG0("unisig_vc_act08: address not mine\n"); + goto response08; + } + + /* + * Find the right NIF for the given selector byte + */ + nip = pip->pif_nif; + while (nip && nip->nif_sel != nap->aan_sel) { + nip = nip->nif_pnext; + } + if (!nip) { + cause = UNI_IE_CAUS_IECONTENT; + ATM_DEBUG0("unisig_vc_act08: bad selector byte\n"); + goto response08; + } + + /* + * See if we can handle the specified encapsulation + */ + if (msg->msg_ie_blli->ie_blli_l2_id != UNI_IE_BLLI_L2P_LLC && + (msg->msg_ie_blli->ie_blli_l2_id != 0 || + msg->msg_ie_blli->ie_blli_l3_id != + UNI_IE_BLLI_L3P_ISO9577)) { + cause = UNI_IE_CAUS_UNAVAIL; + ATM_DEBUG0("unisig_vc_act08: bad encapsulation\n"); + goto response08; + } + + /* + * See if we recognize the specified AAL + */ + if (msg->msg_ie_aalp->ie_aalp_aal_type != UNI_IE_AALP_AT_AAL3 && + msg->msg_ie_aalp->ie_aalp_aal_type != + UNI_IE_AALP_AT_AAL5) { + cause = UNI_IE_CAUS_UAAL; + ATM_DEBUG0("unisig_vc_act08: bad AAL\n"); + goto response08; + } + + /* + * Should verify that we can handle requested + * connection QOS + */ + + /* + * Make sure the specified VPI/VCI is valid + */ + vpi = msg->msg_ie_cnid->ie_cnid_vpci; + vci = msg->msg_ie_cnid->ie_cnid_vci; + if (vpi > pip->pif_maxvpi || + vci > pip->pif_maxvci || + vci < UNI_IE_CNID_MIN_VCI) { + cause = UNI_IE_CAUS_BAD_VCC; + ATM_DEBUG0("unisig_vc_act08: VPI/VCI invalid\n"); + goto response08; + } + + /* + * Make sure the specified VPI/VCI isn't in use already + */ + if (unisig_find_vpvc(usp, vpi, vci, VCC_IN)) { + cause = UNI_IE_CAUS_NA_VCC; + ATM_DEBUG0("unisig_vc_act08: VPI/VCI in use\n"); + goto response08; + } + + /* + * Make sure it's a point-to-point connection + */ + if (msg->msg_ie_bbcp->ie_bbcp_conn_config != + UNI_IE_BBCP_CC_PP) { + cause = UNI_IE_CAUS_NI_BC; + ATM_DEBUG0("unisig_vc_act08: conn not pt-pt\n"); + goto response08; + } + + /* + * Fill in the VCCB fields that we can at this point + */ + uvp->uv_type = VCC_SVC | VCC_IN | VCC_OUT; + uvp->uv_proto = pip->pif_sigmgr->sm_proto; + uvp->uv_sstate = UNI_CALL_PRESENT; + uvp->uv_ustate = VCCU_POPEN; + uvp->uv_pif = pip; + uvp->uv_nif = nip; + uvp->uv_vpi = msg->msg_ie_cnid->ie_cnid_vpci; + uvp->uv_vci = msg->msg_ie_cnid->ie_cnid_vci; + uvp->uv_tstamp = time_second; + + /* + * Copy the connection attributes from the SETUP message + * to an attribute block + */ + KM_ZERO(&attr, sizeof(attr)); + attr.nif = nip; + attr.aal.tag = T_ATM_ABSENT; + attr.traffic.tag = T_ATM_ABSENT; + attr.bearer.tag = T_ATM_ABSENT; + attr.bhli.tag = T_ATM_ABSENT; + attr.blli.tag_l2 = T_ATM_ABSENT; + attr.blli.tag_l3 = T_ATM_ABSENT; + attr.llc.tag = T_ATM_ABSENT; + attr.called.tag = T_ATM_ABSENT; + attr.calling.tag = T_ATM_ABSENT; + attr.qos.tag = T_ATM_ABSENT; + attr.transit.tag = T_ATM_ABSENT; + attr.cause.tag = T_ATM_ABSENT; + unisig_save_attrs(usp, msg, &attr); + + /* + * Notify the connection manager of the new VCC + */ + ATM_DEBUG0("unisig_vc_act08: notifying user of connection\n"); + rc = atm_cm_incoming((struct vccb *)uvp, &attr); + if (rc) + goto response08; + + /* + * Wait for the connection recipient to issue an accept + * or reject + */ + return(0); + +response08: + ATM_DEBUG1("unisig_vc_act08: reject with cause=%d\n", cause); + + /* + * Clear the VCCB state + */ + uvp->uv_sstate = UNI_NULL; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + /* + * Some problem was detected with the request. Send a Q.2931 + * message rejecting the connection. + */ + rc = unisig_send_release_complete(usp, uvp, msg, cause); + + return(rc); +} + + +/* + * VC state machine action 9 + * Accept routine called by user + * + * Send CONNECT, start timer T313, go to UNI_CONNECT_REQUEST state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act09(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + struct unisig_msg *conn_msg; + + conn_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (conn_msg == NULL) + return(ENOMEM); + + /* + * Fill out the response + */ + conn_msg->msg_call_ref = uvp->uv_call_ref; + conn_msg->msg_type = UNI_MSG_CONN; + conn_msg->msg_type_flag = 0; + conn_msg->msg_type_action = 0; + + /* + * Send the CONNECT message. If the send fails, the other + * side will eventually time out and close the connection. + */ + rc = unisig_send_msg(usp, conn_msg); + unisig_free_msg(conn_msg); + if (rc) { + return(rc); + } + + /* + * Start timer T313 + */ + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T313); + + /* + * Set the new state + */ + uvp->uv_sstate = UNI_CONNECT_REQUEST; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); +} + + +/* + * VC state machine action 10 + * Received CONNECT ACK + * + * Go to UNI_ACTIVE state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act10(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Set the state + */ + uvp->uv_sstate = UNI_ACTIVE; + uvp->uv_ustate = VCCU_OPEN; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + /* + * Notify the user that the call is up + */ + atm_cm_connected(uvp->uv_connvc); + + return (0); +} + + +/* + * VC state machine action 11 + * Reject handler called + * + * Send RELEASE COMPLETE, clear the call + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act11(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Send a RELEASE COMPLETE message + */ + rc = unisig_send_release_complete(usp, uvp, msg, + UNI_IE_CAUS_REJECT); + + /* + * Clear the call VCCB + */ + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(rc); +} + + +/* + * VC state machine action 12 + * Release or abort routine called + * + * Send RELEASE, start timer T308, go to UNI_RELEASE_REQUEST state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act12(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Send the RELEASE message + */ + rc = unisig_vc_clear_call(usp, uvp, (struct unisig_msg *)0, + T_ATM_ABSENT); + + return(rc); +} + + +/* + * VC state machine action 13 + * RELEASE COMPLETE received + * + * Clear the call + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act13(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Set the state + */ + uvp->uv_sstate = UNI_FREE; + if (uvp->uv_ustate != VCCU_ABORT) + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + /* + * Notify the user that the call is now closed + */ + unisig_set_cause_attr(&uvp->uv_connvc->cvc_attr, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + atm_cm_cleared(uvp->uv_connvc); + + return(0); +} + + +/* + * VC state machine action 14 + * Timer expired while waiting for RELEASE COMPLETE + * + * If this is the second expiration, just clear the call. Otherwise, + * retransmit the RELEASE message and restart timer T308. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act14(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Check the retry count + */ + if (uvp->uv_retry) { + /* + * Clear the connection + */ + rc = unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + } else { + /* + * Increment the retry count + */ + uvp->uv_retry++; + + /* + * Resend the RELEASE message + */ + rc = unisig_send_release(usp, uvp, + (struct unisig_msg *)0, T_ATM_ABSENT); + if (rc) + return(rc); + + /* + * Restart timer T308 + */ + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T308); + } + + return(0); +} + + +/* + * VC state machine action 15 + * RELEASE received in UNI_ACTIVE state + * + * Send RELEASE COMPLETE, go to UNI_FREE, notify the user + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act15(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int cause, rc; + struct ie_generic *iep; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * If there was no Cause IE, flag an error + */ + if (!msg->msg_ie_caus) { + cause = UNI_IE_CAUS_MISSING; + for (iep=msg->msg_ie_err; iep; iep=iep->ie_next) { + if (iep->ie_ident == UNI_IE_CAUS && + iep->ie_err_cause == + UNI_IE_CAUS_IECONTENT) { + cause = UNI_IE_CAUS_IECONTENT; + } + } + if (cause == UNI_IE_CAUS_MISSING) { + iep = (struct ie_generic *)atm_allocate( + &unisig_iepool); + if (!iep) + return(ENOMEM); + iep->ie_ident = UNI_IE_CNID; + iep->ie_err_cause = UNI_IE_CAUS_MISSING; + MSG_IE_ADD(msg, iep, UNI_MSG_IE_ERR); + } + } else { + cause = UNI_IE_CAUS_NORM_UNSP; + } + + /* + * Send a RELEASE COMPLETE message + */ + rc = unisig_send_release_complete(usp, uvp, msg, cause); + + /* + * Set the state + */ + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + /* + * Notify the user that the call is cleared + */ + unisig_set_cause_attr(&uvp->uv_connvc->cvc_attr, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + atm_cm_cleared(uvp->uv_connvc); + + return(rc); +} + + +/* + * VC state machine action 16 + * RELEASE received in UNI_RELEASE_REQUEST state + * + * Clear the call + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act16(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Clear the VCCB + */ + rc = unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + + return(rc); +} + + +/* + * VC state machine action 17 + * Protocol error + * + * Send a STATUS message with cause 101, "message not compatible with + * call state" + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act17(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + ATM_DEBUG3("unisig_vc_perror: usp=0x%x, uvp=0x%x, msg=0x%x\n", + (int) usp, (int) uvp, (int) msg); + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Send a STATUS message + */ + rc = unisig_send_status(usp, uvp, msg, UNI_IE_CAUS_STATE); + + return(rc ? rc : EINVAL); +} + + +/* + * VC state machine action 18 + * Signalling AAL connection has been lost + * + * Start timer T309. If the timer expires before the SAAL connection + * comes back, the VCC will be cleared. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act18(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Start timer T309 + */ + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T309); + + /* + * Set new state + */ + uvp->uv_sstate = UNI_SSCF_RECOV; + + return(0); +} + + +/* + * VC state machine action 19 + * Ignore the event + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act19(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + return(0); +} + + +/* + * VC state machine action 20 + * SSCF establish indication in UNI_SSCF_RECOV state -- signalling + * AAL has come up after an outage + * + * Send STATUS ENQ to make sure we're in compatible state with other end + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act20(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + struct unisig_msg *stat_msg; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Get memory for a STATUS ENQUIRY message + */ + stat_msg = (struct unisig_msg *)atm_allocate(&unisig_msgpool); + if (stat_msg == NULL) + return(ENOMEM); + + /* + * Fill out the message + */ + stat_msg->msg_call_ref = uvp->uv_call_ref; + stat_msg->msg_type = UNI_MSG_SENQ; + stat_msg->msg_type_flag = 0; + stat_msg->msg_type_action = 0; + + /* + * Send the STATUS ENQUIRY message + */ + rc = unisig_send_msg(usp, stat_msg); + unisig_free_msg(stat_msg); + + /* + * Return to active state + */ + uvp->uv_sstate = UNI_ACTIVE; + + return(rc); +} + + +/* + * VC state machine action 21 + * STATUS received + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection (may + * be NULL) + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act21(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int cause, rc; + + /* + * Ignore a STATUS message with the global call reference + */ + if (GLOBAL_CREF(msg->msg_call_ref)) { + return(0); + } + + /* + * If the network thinks we're in NULL state, clear the VCC + */ + if (msg->msg_ie_clst->ie_clst_state == UNI_NULL) { + if (uvp) { + (void)unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_DESTINATION_OUT_OF_ORDER); + } + return(0); + } + + /* + * If we are in NULL state, send a RELEASE COMPLETE + */ + if (!uvp || (uvp->uv_sstate == UNI_FREE) || + (uvp->uv_sstate == UNI_NULL)) { + rc = unisig_send_release_complete(usp, + uvp, msg, UNI_IE_CAUS_STATE); + return(rc); + } + + /* + * If the reported state doesn't match our state, close the VCC + * unless we're in UNI_RELEASE_REQUEST or UNI_RELEASE_IND + */ + if (msg->msg_ie_clst->ie_clst_state != uvp->uv_sstate) { + if (uvp->uv_sstate == UNI_RELEASE_REQUEST || + uvp->uv_sstate == UNI_RELEASE_IND) { + return(0); + } + rc = unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + } + + /* + * States match, check for an error on one of our messages + */ + cause = msg->msg_ie_caus->ie_caus_cause; + if (cause == UNI_IE_CAUS_MISSING || + cause == UNI_IE_CAUS_MTEXIST || + cause == UNI_IE_CAUS_IEEXIST || + cause == UNI_IE_CAUS_IECONTENT || + cause == UNI_IE_CAUS_STATE) { + ATM_DEBUG2("unisig_vc_act21: error %d on message 0x%x\n", + cause, + msg->msg_ie_caus->ie_caus_diagnostic[0]); + if (uvp) { + (void)unisig_clear_vcc(usp, uvp, + T_ATM_CAUSE_INVALID_INFO_ELEMENT_CONTENTS); + } + } + + return(0); +} + + +/* + * VC state machine action 22 + * Received STATUS ENQ + * + * Send STATUS with cause 30 "response to STATUS ENQUIRY" and + * current state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection (may + * be NULL) + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act22(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + struct unisig_msg *status; + struct ie_generic *callst_ie, *cause_ie; + + ATM_DEBUG3("unisig_vc_perror: usp=0x%x, uvp=0x%x, msg=0x%x\n", + (int) usp, (int) uvp, (int) msg); + + /* + * Get memory for a STATUS message + */ + status = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (status == NULL) + return(ENOMEM); + callst_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (callst_ie == NULL) { + atm_free(status); + return(ENOMEM); + } + cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); + if (cause_ie == NULL) { + atm_free(status); + atm_free(callst_ie); + return(ENOMEM); + } + + /* + * Fill out the response + */ + if (uvp) { + status->msg_call_ref = uvp->uv_call_ref; + } else if (msg) { + if (msg->msg_call_ref & UNI_MSG_CALL_REF_RMT) + status->msg_call_ref = msg->msg_call_ref & + UNI_MSG_CALL_REF_MASK; + else + status->msg_call_ref = msg->msg_call_ref | + UNI_MSG_CALL_REF_RMT; + } else { + status->msg_call_ref = UNI_MSG_CALL_REF_GLOBAL; + } + status->msg_type = UNI_MSG_STAT; + status->msg_type_flag = 0; + status->msg_type_action = 0; + status->msg_ie_clst = callst_ie; + status->msg_ie_caus = cause_ie; + + /* + * Fill out the call state IE + */ + callst_ie->ie_ident = UNI_IE_CLST; + callst_ie->ie_coding = 0; + callst_ie->ie_flag = 0; + callst_ie->ie_action = 0; + if (uvp) { + switch(uvp->uv_sstate) { + case UNI_FREE: + callst_ie->ie_clst_state = UNI_NULL; + break; + default: + callst_ie->ie_clst_state = uvp->uv_sstate; + } + } else { + callst_ie->ie_clst_state = UNI_NULL; + } + + /* + * Fill out the cause IE + */ + cause_ie->ie_ident = UNI_IE_CAUS; + cause_ie->ie_coding = 0; + cause_ie->ie_flag = 0; + cause_ie->ie_action = 0; + cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; + cause_ie->ie_caus_cause = UNI_IE_CAUS_SENQ; + + /* + * Send the STATUS message + */ + rc = unisig_send_msg(usp, status); + unisig_free_msg(status); + return(rc); +} + + +/* + * VC state machine action 23 + * Received ADD PARTY + * + * We don't support multipoint connections, so send an ADD PARTY REJECT + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act23(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + struct unisig_msg *apr_msg; + + /* + * Get memory for the ADD PARTY REJECT message + */ + apr_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); + if (apr_msg == NULL) + return(ENOMEM); + + /* + * Fill out the message + */ + if (msg->msg_call_ref & UNI_MSG_CALL_REF_RMT) + apr_msg->msg_call_ref = msg->msg_call_ref & + UNI_MSG_CALL_REF_MASK; + else + apr_msg->msg_call_ref = msg->msg_call_ref | + UNI_MSG_CALL_REF_RMT; + apr_msg->msg_type = UNI_MSG_ADPR; + apr_msg->msg_type_flag = 0; + apr_msg->msg_type_action = 0; + + /* + * Use the endpoint reference IE from the received message + */ + apr_msg->msg_ie_eprf = msg->msg_ie_eprf; + + /* + * Send the ADD PARTY REJECT message + */ + rc = unisig_send_msg(usp, apr_msg); + apr_msg->msg_ie_eprf = NULL; + unisig_free_msg(apr_msg); + + return(rc); +} + + +/* + * VC state machine action 24 + * User error + * + * Return EALREADY + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act24(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + return(EALREADY); +} + + +/* + * VC state machine action 25 + * User error + * + * Return EINVAL + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act25(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + return(EINVAL); +} + + +/* + * VC state machine action 26 + * PVC abort + * + * The abort handler was called to abort a PVC. Clear the VCCB and + * notify the user. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act26(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Close the VCCB + */ + rc = unisig_close_vcc(usp, uvp); + if (rc) + return(rc); + + /* + * Notify the user + */ + unisig_set_cause_attr(&uvp->uv_connvc->cvc_attr, + T_ATM_CAUSE_NORMAL_CALL_CLEARING); + atm_cm_cleared(uvp->uv_connvc); + + return(0); +} + + +/* + * VC state machine action 27 + * Signalling AAL failure + * + * Change PVC state to UNI_PVC_ACT_DOWN. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act27(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + /* + * Set the state + */ + uvp->uv_sstate = UNI_PVC_ACT_DOWN; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); +} + + +/* + * VC state machine action 28 + * Signalling AAL established + * + * Set PVC state to UNI_PVC_ACTIVE. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act28(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + /* + * Set the state + */ + uvp->uv_sstate = UNI_PVC_ACTIVE; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); +} + + +/* + * VC state machine action 29 + * Protocol error + * + * Send a RELEASE COMPLETE message with cause 81, "invalid call + * reference value" + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection (may + * be NULL) + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act29(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + int rc; + + /* + * Send a RELEASE COMPLETE message + */ + rc = unisig_send_release_complete(usp, uvp, msg, + UNI_IE_CAUS_CREF); + + return(rc); +} + + +/* + * VC state machine action 30 + * Release routine called while SSCF session down, or SSCF session + * reset or lost while in UNI_CALL_PRESENT + * + * Go to UNI_FREE state + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act30(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + /* + * Clear any running timer + */ + UNISIG_VC_CANCEL((struct vccb *) uvp); + + /* + * Clear the call state + */ + uvp->uv_sstate = UNI_FREE; + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); +} + + +/* + * VC state machine action 31 + * Accept handler called in UNI_FREE state. + * + * The call was in UNI_CALL_PRESENT state when it was closed because + * of an SSCF failure. Return an error indication. The accept + * handler will free the VCCB and return the proper code to the + * caller. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to a UNISIG message structure + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_act31(usp, uvp, msg) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; +{ + return(ENETDOWN); +} + + +/* + * Initiate clearing a call by sending a RELEASE message. + * + * Arguments: + * usp pointer to protocol instance block + * uvp pointer to the VCCB for the affected connection + * msg pointer to UNI signalling message that the RELEASE + * responds to (may be NULL) + * cause the reason for clearing the call; a value of + * T_ATM_ABSENT indicates that the cause code is + * in the VCC's ATM attributes block + * + * Returns: + * 0 success + * errno error encountered + * + */ +static int +unisig_vc_clear_call(usp, uvp, msg, cause) + struct unisig *usp; + struct unisig_vccb *uvp; + struct unisig_msg *msg; + int cause; +{ + int rc; + + /* + * Clear the retry count + */ + uvp->uv_retry = 0; + + /* + * Make sure the ATM attributes block has a valid cause code, + * if needed + */ + if (cause == T_ATM_ABSENT && + uvp->uv_connvc->cvc_attr.cause.tag != + T_ATM_PRESENT) { + uvp->uv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT; + uvp->uv_connvc->cvc_attr.cause.v.coding_standard = + T_ATM_ITU_CODING; + uvp->uv_connvc->cvc_attr.cause.v.location = + T_ATM_LOC_USER; + uvp->uv_connvc->cvc_attr.cause.v.cause_value = + usp->us_proto == ATM_SIG_UNI30 ? + T_ATM_CAUSE_UNSPECIFIED_NORMAL : + T_ATM_CAUSE_NORMAL_CALL_CLEARING; + } + + /* + * Send a RELEASE message + */ + rc = unisig_send_release(usp, uvp, msg, cause); + if (rc) + return(rc); + + /* + * Start timer T308 + */ + UNISIG_VC_TIMER((struct vccb *) uvp, UNI_T308); + + /* + * Set the VCCB state + */ + uvp->uv_sstate = UNI_RELEASE_REQUEST; + if (uvp->uv_ustate != VCCU_ABORT) + uvp->uv_ustate = VCCU_CLOSED; + + /* + * Mark the time + */ + uvp->uv_tstamp = time_second; + + return(0); +} |