diff -ur --new-file old/atm/.kernel new/atm/.kernel --- old/atm/.kernel Wed Aug 19 19:03:49 1998 +++ new/atm/.kernel Thu Aug 20 16:27:21 1998 @@ -1,2 +1,2 @@ # this file is used to control automated generation of differences -2.1.105 +2.1.117 diff -ur --new-file old/atm/BUGS new/atm/BUGS --- old/atm/BUGS Tue Aug 18 13:24:29 1998 +++ new/atm/BUGS Fri Aug 21 00:15:16 1998 @@ -1,6 +1,7 @@ -Known bugs and restrictions in version 0.41 +Known bugs and restrictions in version 0.43 =========================================== + - 2.1.117 kernel may not compile properly with sound enabled - libresolve conflicts with libc on some systems - ENI driver loses synchronization on some systems, leading to panics or hung VCs (these may be two distinct problems) @@ -8,3 +9,4 @@ - MPOA may fail on RedHat 5.x with "mpcd: changed_fds = ...", due to what seems to be a bug in RedHat's glibc - CLIP interfaces must not be reconfigured while "up" + - the "new" atmsigd leaks memory when tracing diff -ur --new-file old/atm/CHANGES new/atm/CHANGES --- old/atm/CHANGES Wed Aug 19 19:03:02 1998 +++ new/atm/CHANGES Fri Aug 21 00:54:10 1998 @@ -1,3 +1,27 @@ +Version 0.42 to 0.43 (21-AUG-1998) +==================== + +Bug fixes +--------- + + - trying to use atmtcp when compiled as a module with the module not loaded + crashed the kernel + +New features +------------ + + - upgraded to the 2.1.117 kernel + - included NICStAR driver by Rui Prior at INESC (this driver also includes + parts of an earlier driver written by Matt Welsh, then enhanced by R. D. + Rechenmacher and Jawaid Bazyar) + - new atmsigd with support for multiple signaling entities (experimental) + +Other changes +------------- + + - removed register dumping code from suni.c (leaked out into the distribution) + + Version 0.41 to 0.42 (19-AUG-1998) ==================== diff -ur --new-file old/atm/Makefile new/atm/Makefile --- old/atm/Makefile Tue Aug 11 17:42:11 1998 +++ new/atm/Makefile Thu Aug 20 23:19:56 1998 @@ -3,7 +3,7 @@ # "maint" must appear after "qgen" DIRS=lib test debug qgen saal sigd maint arpd ilmid aqd man led lane mpoad \ - switch # tcd extra + switch sigd.new # tcd extra all: for n in $(DIRS); do $(MAKE) -C $$n || exit; done diff -ur --new-file old/atm/README new/atm/README --- old/atm/README Wed Aug 19 19:03:09 1998 +++ new/atm/README Thu Aug 20 16:26:58 1998 @@ -1,4 +1,4 @@ -ATM on Linux, release 0.42 (alpha) by Werner Almesberger, EPFL ICA +ATM on Linux, release 0.43 (alpha) by Werner Almesberger, EPFL ICA ============================================== Werner.Almesberger@epfl.ch This is experimental software. There are known major bugs and certainly @@ -9,7 +9,7 @@ device drivers, source for demons, management and test tools, and some documentation. -The kernel patch is relative to the "standard" 2.1.105 kernel. +The kernel patch is relative to the "standard" 2.1.117 kernel. Please see http://lrcwww.epfl.ch/linux-atm/info.html for a list of features supported by ATM on Linux. diff -ur --new-file old/atm/USAGE new/atm/USAGE --- old/atm/USAGE Wed Aug 19 19:04:05 1998 +++ new/atm/USAGE Fri Aug 21 01:02:14 1998 @@ -1,4 +1,4 @@ -Usage instructions - ATM on Linux, release 0.42 +Usage instructions - ATM on Linux, release 0.43 ------------------------------------------------- For updates of ATM on Linux, please check the Web page at @@ -17,9 +17,9 @@ In order to install this package, you need - the package itself - ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.42.tar.gz - - the Linux kernel, version 2.1.105, e.g. from - ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.105.tar.gz + ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.43.tar.gz + - the Linux kernel, version 2.1.117, e.g. from + ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.117.tar.gz - Perl, version 4 or 5 - if you want memory debugging: MPR version 1.6, e.g. from ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/mpr-1.6.tar.gz @@ -33,11 +33,11 @@ all the files listed above there. Then extract the ATM on Linux distribution: -tar xfz atm-0.42.tar.gz +tar xfz atm-0.43.tar.gz and the kernel source: -tar xfz linux-2.1.105.tar.gz +tar xfz linux-2.1.117.tar.gz Finally, you can extract the ATM-related patches: @@ -49,6 +49,7 @@ atm/ Documentation in ASCII format, kernel patch, top-level Makefile, and distribution scripts atm/sigd/ UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: atmsigd + atm/sigd.new/ Experimental version: atmsigd.new atm/saal/ Signaling AAL library (SSCOP, SSCF, and SAAL) atm/qgen/ Q.2931-style message handling atm/ilmid/ ILMI address registration demon: ilmid @@ -93,6 +94,9 @@ Enable usec resolution timestamps (CONFIG_ATM_ZATM_EXACT_TS) IDT 77201 (NICStAR) (CONFIG_ATM_NICSTAR) + +Note that the file drivers/atm/nicstar.h contains a few configurable +settings for the IDT 77201 driver. Then build your kernel and reboot. diff -ur --new-file old/atm/VERSION new/atm/VERSION --- old/atm/VERSION Wed Aug 19 19:03:54 1998 +++ new/atm/VERSION Fri Aug 21 00:53:48 1998 @@ -1 +1 @@ -0.42 +0.43 diff -ur --new-file old/atm/atm.patch new/atm/atm.patch --- old/atm/atm.patch Wed Aug 19 19:02:34 1998 +++ new/atm/atm.patch Fri Aug 21 01:08:48 1998 @@ -1,6 +1,6 @@ ---- ref/Makefile Sun Jun 7 19:41:28 1998 -+++ work/Makefile Tue Jun 9 21:33:13 1998 -@@ -117,6 +117,10 @@ +--- ref/Makefile Wed Aug 19 07:02:21 1998 ++++ work/Makefile Fri Aug 21 00:52:27 1998 +@@ -121,6 +121,10 @@ DRIVERS := $(DRIVERS) drivers/net/net.a @@ -11,7 +11,7 @@ ifeq ($(CONFIG_SCSI),y) DRIVERS := $(DRIVERS) drivers/scsi/scsi.a endif -@@ -313,6 +317,7 @@ +@@ -301,6 +305,7 @@ if [ -f NET_MODULES ]; then inst_mod NET_MODULES net; fi; \ if [ -f IPV4_MODULES ]; then inst_mod IPV4_MODULES ipv4; fi; \ if [ -f IPV6_MODULES ]; then inst_mod IPV6_MODULES ipv6; fi; \ @@ -19,9 +19,9 @@ if [ -f SCSI_MODULES ]; then inst_mod SCSI_MODULES scsi; fi; \ if [ -f FS_MODULES ]; then inst_mod FS_MODULES fs; fi; \ if [ -f NLS_MODULES ]; then inst_mod NLS_MODULES fs; fi; \ ---- ref/Documentation/Configure.help Sun Jun 7 19:37:41 1998 -+++ work/Documentation/Configure.help Tue Jun 9 21:32:11 1998 -@@ -2657,6 +2657,79 @@ +--- ref/Documentation/Configure.help Wed Aug 19 23:37:58 1998 ++++ work/Documentation/Configure.help Fri Aug 21 00:52:27 1998 +@@ -3166,6 +3166,79 @@ This is a backward compatibility option, choose Y for now. This option will be removed soon. @@ -102,15 +102,15 @@ CONFIG_SCSI If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/Documentation/atm.txt Tue Jun 9 21:32:11 1998 ++++ work/Documentation/atm.txt Fri Aug 21 00:52:27 1998 @@ -0,0 +1,4 @@ +In order to use anything but the most primitive functions of ATM, +several user-mode programs are required to assist the kernel. These +programs and related material can be found via the ATM on Linux Web +page at http://lrcwww.epfl.ch/linux-atm/ ---- ref/arch/i386/config.in Wed Apr 29 07:41:33 1998 -+++ work/arch/i386/config.in Tue Jun 9 21:32:11 1998 -@@ -93,6 +93,9 @@ +--- ref/arch/i386/config.in Thu Aug 6 11:29:45 1998 ++++ work/arch/i386/config.in Fri Aug 21 00:52:27 1998 +@@ -91,6 +91,9 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in @@ -120,9 +120,9 @@ fi endmenu fi ---- ref/arch/alpha/config.in Sat May 9 02:54:39 1998 -+++ work/arch/alpha/config.in Thu Jun 18 10:58:45 1998 -@@ -227,6 +227,9 @@ +--- ref/arch/alpha/config.in Sun Aug 9 21:09:05 1998 ++++ work/arch/alpha/config.in Fri Aug 21 00:52:27 1998 +@@ -229,6 +229,9 @@ bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in @@ -132,18 +132,18 @@ fi endmenu fi ---- ref/drivers/Makefile Sun Jun 7 19:37:41 1998 -+++ work/drivers/Makefile Tue Jun 9 21:35:26 1998 -@@ -9,7 +9,7 @@ - +--- ref/drivers/Makefile Wed Aug 5 01:49:18 1998 ++++ work/drivers/Makefile Fri Aug 21 00:52:27 1998 +@@ -10,7 +10,7 @@ SUB_DIRS := block char net misc sound MOD_SUB_DIRS := $(SUB_DIRS) sbus --ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus cdrom isdn pnp macintosh -+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus cdrom isdn pnp macintosh atm + ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus cdrom isdn pnp \ +- macintosh video dio zorro fc4 ++ macintosh video dio zorro fc4 atm - ifdef CONFIG_PCI - SUB_DIRS += pci -@@ -56,6 +56,11 @@ + ifdef CONFIG_DIO + SUB_DIRS += dio +@@ -75,6 +75,11 @@ ifeq ($(CONFIG_ISDN),m) MOD_SUB_DIRS += isdn endif @@ -156,7 +156,7 @@ ifeq ($(CONFIG_AP1000),y) --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/Config.in Wed Aug 19 18:21:40 1998 ++++ work/drivers/atm/Config.in Fri Aug 21 02:32:42 1998 @@ -0,0 +1,23 @@ +# +# ATM device configuration @@ -179,10 +179,10 @@ +# if [ "$CONFIG_ATM_TNETA1570" = "y" ]; then +# bool ' Enable extended debugging' CONFIG_ATM_TNETA1570_DEBUG n +# fi -+# tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR y ++ tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR y +fi --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/Makefile Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/Makefile Fri Aug 21 00:52:28 1998 @@ -0,0 +1,57 @@ +# File: drivers/atm/Makefile +# @@ -242,7 +242,7 @@ + +include $(TOPDIR)/Rules.make --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/atmdev_init.c Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/atmdev_init.c Fri Aug 21 00:52:28 1998 @@ -0,0 +1,48 @@ +/* drivers/atm/atmdev_init.c - ATM device driver initialization */ + @@ -293,7 +293,7 @@ + return devs; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/atmtcp.c Tue Aug 18 13:27:32 1998 ++++ work/drivers/atm/atmtcp.c Fri Aug 21 00:52:28 1998 @@ -0,0 +1,339 @@ +/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ + @@ -635,7 +635,7 @@ +#endif + --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/eni.c Fri Jul 17 16:38:52 1998 ++++ work/drivers/atm/eni.c Fri Aug 21 00:52:28 1998 @@ -0,0 +1,2143 @@ +/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ + @@ -2781,7 +2781,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/eni.h Wed Aug 19 14:31:02 1998 ++++ work/drivers/atm/eni.h Fri Aug 21 01:09:03 1998 @@ -0,0 +1,114 @@ +/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */ + @@ -2898,7 +2898,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/midway.h Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/midway.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,265 @@ +/* drivers/atm/midway.h - Efficient Networks Midway (SAR) description */ + @@ -3166,8 +3166,3910 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/suni.c Tue Aug 18 13:42:40 1998 -@@ -0,0 +1,311 @@ ++++ work/drivers/atm/nicstar.h Fri Aug 21 02:32:42 1998 +@@ -0,0 +1,748 @@ ++/****************************************************************************** ++ * ++ * nicstar.h ++ * ++ * Header file for the nicstar device driver. ++ * ++ * Author: Rui Prior ++ * ++ * (C) INESC 1998 ++ * ++ ******************************************************************************/ ++ ++ ++#ifndef _LINUX_NICSTAR_H_ ++#define _LINUX_NICSTAR_H_ ++ ++ ++/* Includes *******************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* Options ********************************************************************/ ++ ++#define ESI_FROM_EPROM 1 /* Undef to use hardcoded ESI */ ++#define NS_MAX_CARDS 1 /* Maximum number of NICStAR based cards ++ controlled by the device driver*/ ++ ++#undef RCQ_SUPPORT /* Do not define this for now */ ++ ++#define NS_TST_NUM_ENTRIES 2340 /* + 1 for return */ ++#define NS_TST_RESERVED 340 /* N. entries reserved for UBR/ABR/VBR */ ++ ++#define NS_SMBUFSIZE 48 /* 48, 96, 240 or 2048 */ ++#define NS_LGBUFSIZE 16384 /* 2048, 4096, 8192 or 16384 */ ++#define NS_RSQSIZE 8192 /* 2048, 4096 or 8192 */ ++#define NS_VPIBITS 2 /* 0, 1, 2, or 8 */ ++ ++#define NS_MAX_RCTSIZE 4096 /* Number of entries. 4096 or 16384. ++ Define 4096 only if (all) your card(s) ++ have 32K x 32bit SRAM, in which case ++ setting this to 16384 will just waste a ++ lot of memory. ++ Setting this to 4096 for a card with ++ 128K x 32bit SRAM will limit the maximum ++ VCI. */ ++ ++#define NS_PCI_LATENCY 64 /* Must be a multiple of 32 */ ++ ++ /* Number of buffers initially allocated */ ++#define NUM_SB 32 /* Must be even */ ++#define NUM_LB 24 /* Must be even */ ++#define NUM_HB 8 /* Pre-allocated huge buffers */ ++#define NUM_IOVB 48 /* Iovec buffers */ ++ ++ /* Lower level for count of buffers */ ++#define MIN_SB 8 /* Must be even */ ++#define MIN_LB 8 /* Must be even */ ++#define MIN_HB 6 ++#define MIN_IOVB 8 ++ ++ /* Upper level for count of buffers */ ++#define MAX_SB 64 /* Must be even, <= 508 */ ++#define MAX_LB 48 /* Must be even, <= 508 */ ++#define MAX_HB 10 ++#define MAX_IOVB 80 ++ ++ /* These are the absolute maximum allowed for the ioctl() */ ++#define TOP_SB 256 /* Must be even, <= 508 */ ++#define TOP_LB 128 /* Must be even, <= 508 */ ++#define TOP_HB 64 ++#define TOP_IOVB 256 ++ ++ ++#define MAX_TBD_PER_VC 1 /* Number of TBDs before a TSR */ ++#define MAX_TBD_PER_SCQ 10 /* Only meaningful for variable rate SCQs */ ++ ++#undef ENABLE_TSQFIE ++ ++#define SCQFULL_TIMEOUT (5 * HZ) ++ ++#define PCR_TOLERANCE 1.0001 ++ ++ ++ ++/* ESI stuff ******************************************************************/ ++ ++#define NS_ESI0 0x00 /* Hardcoded ESI, in case your card doesn't have */ ++#define NS_ESI1 0x00 /* it on the EPROM */ ++#define NS_ESI2 0x00 ++#define NS_ESI3 0x00 ++#define NS_ESI4 0x00 ++#define NS_ESI5 0x00 ++ ++#define NICSTAR_EPROM_MAC_ADDR_OFFSET 0x6C ++ ++ ++/* #defines *******************************************************************/ ++ ++#define NS_IOREMAP_SIZE 4096 ++ ++#define IDT_25_PCR (25600000/270*260/8/53) ++ ++#define BUF_SM 0x00000000 /* These two are used for push_rxbufs() */ ++#define BUF_LG 0x00000001 /* CMD, Write_FreeBufQ, LBUF bit */ ++ ++#define NS_HBUFSIZE 65568 /* Size of max. AAL5 PDU */ ++#define NS_MAX_IOVECS (2 + (65568 - NS_SMBUFSIZE) / \ ++ (NS_LGBUFSIZE - (NS_LGBUFSIZE % 48))) ++#define NS_IOVBUFSIZE (NS_MAX_IOVECS * (sizeof(struct iovec))) ++ ++#define NS_SMBUFSIZE_USABLE (NS_SMBUFSIZE - NS_SMBUFSIZE % 48) ++#define NS_LGBUFSIZE_USABLE (NS_LGBUFSIZE - NS_LGBUFSIZE % 48) ++ ++#define NS_AAL0_HEADER (ATM_AAL0_SDU - ATM_CELL_PAYLOAD) /* 4 bytes */ ++ ++#define NS_SMSKBSIZE (NS_SMBUFSIZE + NS_AAL0_HEADER) ++#define NS_LGSKBSIZE (NS_SMBUFSIZE + NS_LGBUFSIZE) ++ ++ ++/* NICStAR structures located in host memory **********************************/ ++ ++ ++ ++/* RSQ - Receive Status Queue ++ * ++ * Written by the NICStAR, read by the device driver. ++ */ ++ ++typedef struct ns_rsqe ++{ ++ u32 word_1; ++ u32 buffer_handle; ++ u32 final_aal5_crc32; ++ u32 word_4; ++} ns_rsqe; ++ ++#define ns_rsqe_vpi(ns_rsqep) (((ns_rsqep)->word_1 & 0x00FF0000) >> 16) ++#define ns_rsqe_vci(ns_rsqep) ((ns_rsqep)->word_1 & 0x0000FFFF) ++ ++#define NS_RSQE_VALID 0x80000000 ++#define NS_RSQE_NZGFC 0x00004000 ++#define NS_RSQE_EOPDU 0x00002000 ++#define NS_RSQE_BUFSIZE 0x00001000 ++#define NS_RSQE_CONGESTION 0x00000800 ++#define NS_RSQE_CLP 0x00000400 ++#define NS_RSQE_CRCERR 0x00000200 ++ ++#define NS_RSQE_BUFSIZE_SM 0x00000000 ++#define NS_RSQE_BUFSIZE_LG 0x00001000 ++ ++#define ns_rsqe_valid(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_VALID) ++#define ns_rsqe_nzgfc(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_NZGFC) ++#define ns_rsqe_eopdu(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_EOPDU) ++#define ns_rsqe_bufsize(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_BUFSIZE) ++#define ns_rsqe_congestion(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_CONGESTION) ++#define ns_rsqe_clp(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_CLP) ++#define ns_rsqe_crcerr(ns_rsqep) ((ns_rsqep)->word_4 & NS_RSQE_CRCERR) ++ ++#define ns_rsqe_cellcount(ns_rsqep) ((ns_rsqep)->word_4 & 0x000001FF) ++#define ns_rsqe_init(ns_rsqep) ((ns_rsqep)->word_4 = 0x00000000) ++ ++#define NS_RSQ_NUM_ENTRIES (NS_RSQSIZE / 16) ++#define NS_RSQ_ALIGNMENT NS_RSQSIZE ++ ++ ++ ++/* RCQ - Raw Cell Queue ++ * ++ * Written by the NICStAR, read by the device driver. ++ */ ++ ++typedef struct cell_payload ++{ ++ u32 word[12]; ++} cell_payload; ++ ++typedef struct ns_rcqe ++{ ++ u32 word_1; ++ u32 word_2; ++ u32 word_3; ++ u32 word_4; ++ cell_payload payload; ++} ns_rcqe; ++ ++#define NS_RCQE_SIZE 64 /* bytes */ ++ ++#define ns_rcqe_islast(ns_rcqep) ((ns_rcqep)->word_2 != 0x00000000) ++#define ns_rcqe_cellheader(ns_rcqep) ((ns_rcqep)->word_1) ++#define ns_rcqe_nextbufhandle(ns_rcqep) ((ns_rcqep)->word_2) ++ ++ ++ ++/* SCQ - Segmentation Channel Queue ++ * ++ * Written by the device driver, read by the NICStAR. ++ */ ++ ++typedef struct ns_scqe ++{ ++ u32 word_1; ++ u32 word_2; ++ u32 word_3; ++ u32 word_4; ++} ns_scqe; ++ ++ /* NOTE: SCQ entries can be either a TBD (Transmit Buffer Descriptors) ++ or TSR (Transmit Status Requests) */ ++ ++#define NS_SCQE_TYPE_TBD 0x00000000 ++#define NS_SCQE_TYPE_TSR 0x80000000 ++ ++ ++#define NS_TBD_EOPDU 0x40000000 ++#define NS_TBD_AAL0 0x00000000 ++#define NS_TBD_AAL34 0x04000000 ++#define NS_TBD_AAL5 0x08000000 ++ ++#define NS_TBD_VPI_MASK 0x0FF00000 ++#define NS_TBD_VCI_MASK 0x000FFFF0 ++#define NS_TBD_VC_MASK (NS_TBD_VPI_MASK | NS_TBD_VCI_MASK) ++ ++#define NS_TBD_VPI_SHIFT 20 ++#define NS_TBD_VCI_SHIFT 4 ++ ++#define ns_tbd_mkword_1(flags, m, n, buflen) (flags | m << 23 | n << 16 | buflen) ++#define ns_tbd_mkword_1_novbr(flags, buflen) (flags | buflen | 0x00810000) ++#define ns_tbd_mkword_3(control, pdulen) (control << 16 | pdulen) ++#define ns_tbd_mkword_4(gfc, vpi, vci, pt, clp) (gfc << 28 | vpi << 20 | vci << 4 | pt << 1 | clp)) ++ ++ ++#define NS_TSR_INTENABLE 0x20000000 ++ ++#define NS_TSR_SCDISVBR 0xFFFF /* Use as scdi for VBR SCD */ ++ ++#define ns_tsr_mkword_1(flags) (NS_SCQE_TYPE_TSR | (flags)) ++#define ns_tsr_mkword_2(scdi, scqi) ((scdi) << 16 | 0x00008000 | (scqi)) ++ ++#define ns_scqe_is_tsr(ns_scqep) ((ns_scqep)->word_1 & NS_SCQE_TYPE_TSR) ++ ++#define VBR_SCQ_NUM_ENTRIES 512 ++#define VBR_SCQSIZE 8192 ++#define CBR_SCQ_NUM_ENTRIES 64 ++#define CBR_SCQSIZE 1024 ++ ++#define NS_SCQE_SIZE 16 ++ ++ ++ ++/* TSQ - Transmit Status Queue ++ * ++ * Written by the NICStAR, read by the device driver. ++ */ ++ ++typedef struct ns_tsi ++{ ++ u32 word_1; ++ u32 word_2; ++} ns_tsi; ++ ++ /* NOTE: The first word can be a status word copied from the TSR which ++ originated the TSI, or a timer overflow indicator. In this last ++ case, the value of the first word is all zeroes. */ ++ ++#define NS_TSI_EMPTY 0x80000000 ++#define NS_TSI_TIMESTAMP_MASK 0x00FFFFFF ++ ++#define ns_tsi_isempty(ns_tsip) ((ns_tsip)->word_2 & NS_TSI_EMPTY) ++#define ns_tsi_gettimestamp(ns_tsip) ((ns_tsip)->word_2 & NS_TSI_TIMESTAMP_MASK) ++ ++#define ns_tsi_init(ns_tsip) ((ns_tsip)->word_2 = NS_TSI_EMPTY) ++ ++ ++#define NS_TSQSIZE 8192 ++#define NS_TSQ_NUM_ENTRIES 1024 ++#define NS_TSQ_ALIGNMENT 8192 ++ ++ ++#define NS_TSI_SCDISVBR NS_TSR_SCDISVBR ++ ++#define ns_tsi_tmrof(ns_tsip) ((ns_tsip)->word_1 == 0x00000000) ++#define ns_tsi_getscdindex(ns_tsip) (((ns_tsip)->word_1 & 0xFFFF0000) >> 16) ++#define ns_tsi_getscqpos(ns_tsip) ((ns_tsip)->word_1 & 0x00007FFF) ++ ++ ++ ++/* NICStAR structures located in local SRAM ***********************************/ ++ ++ ++ ++/* RCT - Receive Connection Table ++ * ++ * Written by both the NICStAR and the device driver. ++ */ ++ ++typedef struct ns_rcte ++{ ++ u32 word_1; ++ u32 buffer_handle; ++ u32 dma_address; ++ u32 aal5_crc32; ++} ns_rcte; ++ ++#define NS_RCTE_BSFB 0x00200000 /* Rev. D only */ ++#define NS_RCTE_NZGFC 0x00100000 ++#define NS_RCTE_CONNECTOPEN 0x00080000 ++#define NS_RCTE_AALMASK 0x00070000 ++#define NS_RCTE_AAL0 0x00000000 ++#define NS_RCTE_AAL34 0x00010000 ++#define NS_RCTE_AAL5 0x00020000 ++#define NS_RCTE_RCQ 0x00030000 ++#define NS_RCTE_RAWCELLINTEN 0x00008000 ++#define NS_RCTE_RXCONSTCELLADDR 0x00004000 ++#define NS_RCTE_BUFFVALID 0x00002000 ++#define NS_RCTE_FBDSIZE 0x00001000 ++#define NS_RCTE_EFCI 0x00000800 ++#define NS_RCTE_CLP 0x00000400 ++#define NS_RCTE_CRCERROR 0x00000200 ++#define NS_RCTE_CELLCOUNT_MASK 0x000001FF ++ ++#define NS_RCTE_FBDSIZE_SM 0x00000000 ++#define NS_RCTE_FBDSIZE_LG 0x00001000 ++ ++#define NS_RCT_ENTRY_SIZE 4 /* Number of dwords */ ++ ++ /* NOTE: We could make macros to contruct the first word of the RCTE, ++ but that doesn't seem to make much sense... */ ++ ++ ++ ++/* FBD - Free Buffer Descriptor ++ * ++ * Written by the device driver using via the command register. ++ */ ++ ++typedef struct ns_fbd ++{ ++ u32 buffer_handle; ++ u32 dma_address; ++} ns_fbd; ++ ++ ++ ++ ++/* TST - Transmit Schedule Table ++ * ++ * Written by the device driver. ++ */ ++ ++typedef u32 ns_tste; ++ ++#define NS_TST_OPCODE_MASK 0x60000000 ++ ++#define NS_TST_OPCODE_NULL 0x00000000 /* Insert null cell */ ++#define NS_TST_OPCODE_FIXED 0x20000000 /* Cell from a fixed rate channel */ ++#define NS_TST_OPCODE_VARIABLE 0x40000000 ++#define NS_TST_OPCODE_END 0x60000000 /* Jump */ ++ ++#define ns_tste_make(opcode, sramad) (opcode | sramad) ++ ++ /* NOTE: ++ ++ - When the opcode is FIXED, sramad specifies the SRAM address of the ++ SCD for that fixed rate channel. ++ - When the opcode is END, sramad specifies the SRAM address of the ++ location of the next TST entry to read. ++ */ ++ ++ ++ ++/* SCD - Segmentation Channel Descriptor ++ * ++ * Written by both the device driver and the NICStAR ++ */ ++ ++typedef struct ns_scd ++{ ++ u32 word_1; ++ u32 word_2; ++ u32 partial_aal5_crc; ++ u32 reserved; ++ ns_scqe cache_a; ++ ns_scqe cache_b; ++} ns_scd; ++ ++#define NS_SCD_BASE_MASK_VAR 0xFFFFE000 /* Variable rate */ ++#define NS_SCD_BASE_MASK_FIX 0xFFFFFC00 /* Fixed rate */ ++#define NS_SCD_TAIL_MASK_VAR 0x00001FF0 ++#define NS_SCD_TAIL_MASK_FIX 0x000003F0 ++#define NS_SCD_HEAD_MASK_VAR 0x00001FF0 ++#define NS_SCD_HEAD_MASK_FIX 0x000003F0 ++#define NS_SCD_XMITFOREVER 0x02000000 ++ ++ /* NOTE: There are other fields in word 2 of the SCD, but as they should ++ not be needed in the device driver they are not defined here. */ ++ ++ ++ ++ ++/* NICStAR local SRAM memory map **********************************************/ ++ ++ ++#define NS_RCT 0x00000 ++#define NS_RCT_32_END 0x03FFF ++#define NS_RCT_128_END 0x0FFFF ++#define NS_UNUSED_32 0x04000 ++#define NS_UNUSED_128 0x10000 ++#define NS_UNUSED_END 0x1BFFF ++#define NS_TST_FRSCD 0x1C000 ++#define NS_TST_FRSCD_END 0x1E7DB ++#define NS_VRSCD2 0x1E7DC ++#define NS_VRSCD2_END 0x1E7E7 ++#define NS_VRSCD1 0x1E7E8 ++#define NS_VRSCD1_END 0x1E7F3 ++#define NS_VRSCD0 0x1E7F4 ++#define NS_VRSCD0_END 0x1E7FF ++#define NS_RXFIFO 0x1E800 ++#define NS_RXFIFO_END 0x1F7FF ++#define NS_SMFBQ 0x1F800 ++#define NS_SMFBQ_END 0x1FBFF ++#define NS_LGFBQ 0x1FC00 ++#define NS_LGFBQ_END 0x1FFFF ++ ++ ++ ++/* NISCtAR operation registers ************************************************/ ++ ++ ++enum ns_regs ++{ ++ DR0 = 0x00, ++ DR1 = 0x04, ++ DR2 = 0x08, ++ DR3 = 0x0C, ++ CMD = 0x10, ++ CFG = 0x14, ++ STAT = 0x18, ++ RSQB = 0x1C, ++ RSQT = 0x20, ++ RSQH = 0x24, ++ CDC = 0x28, ++ VPEC = 0x2C, ++ ICC = 0x30, ++ RAWCT = 0x34, ++ TMR = 0x38, ++ TSTB = 0x3C, ++ TSQB = 0x40, ++ TSQT = 0x44, ++ TSQH = 0x48, ++ GP = 0x4C, ++ VPM = 0x50 ++}; ++ ++ ++/* NICStAR commands issued to the CMD register ********************************/ ++ ++#define NS_CMD_NO_OPERATION 0x00000000 ++#define NS_CMD_OPENCLOSE_CONNECTION 0x20000000 ++#define NS_CMD_WRITE_SRAM 0x40000000 ++#define NS_CMD_READ_SRAM 0x50000000 ++#define NS_CMD_WRITE_FREEBUFQ 0x60000000 ++#define NS_CMD_READ_UTILITY 0x80000000 ++#define NS_CMD_WRITE_UTILITY 0x90000000 ++ ++#define NS_CMD_OPEN_CONNECTION (NS_CMD_OPENCLOSE_CONNECTION | 0x00080000) ++#define NS_CMD_CLOSE_CONNECTION NS_CMD_OPENCLOSE_CONNECTION ++ ++ ++/* NICStAR configuration bits *************************************************/ ++ ++#define NS_CFG_SWRST 0x80000000 ++#define NS_CFG_RXPATH 0x20000000 ++#define NS_CFG_SMBUFSIZE_MASK 0x18000000 ++#define NS_CFG_LGBUFSIZE_MASK 0x06000000 ++#define NS_CFG_EFBIE 0x01000000 ++#define NS_CFG_RSQSIZE_MASK 0x00C00000 ++#define NS_CFG_ICACCEPT 0x00200000 ++#define NS_CFG_IGNOREGFC 0x00100000 ++#define NS_CFG_VPIBITS_MASK 0x000C0000 ++#define NS_CFG_RCTSIZE_MASK 0x00030000 ++#define NS_CFG_VCERRACCEPT 0x00008000 ++#define NS_CFG_RXINT_MASK 0x00007000 ++#define NS_CFG_RAWIE 0x00000800 ++#define NS_CFG_RSQAFIE 0x00000400 ++#define NS_CFG_RXRM 0x00000200 ++#define NS_CFG_TMRROIE 0x00000080 ++#define NS_CFG_TXEN 0x00000020 ++#define NS_CFG_TXIE 0x00000010 ++#define NS_CFG_TXURIE 0x00000008 ++#define NS_CFG_UMODE 0x00000004 ++#define NS_CFG_TSQFIE 0x00000002 ++#define NS_CFG_PHYIE 0x00000001 ++ ++#define NS_CFG_SMBUFSIZE_48 0x00000000 ++#define NS_CFG_SMBUFSIZE_96 0x08000000 ++#define NS_CFG_SMBUFSIZE_240 0x10000000 ++#define NS_CFG_SMBUFSIZE_2048 0x18000000 ++ ++#define NS_CFG_LGBUFSIZE_2048 0x00000000 ++#define NS_CFG_LGBUFSIZE_4096 0x02000000 ++#define NS_CFG_LGBUFSIZE_8192 0x04000000 ++#define NS_CFG_LGBUFSIZE_16384 0x06000000 ++ ++#define NS_CFG_RSQSIZE_2048 0x00000000 ++#define NS_CFG_RSQSIZE_4096 0x00400000 ++#define NS_CFG_RSQSIZE_8192 0x00800000 ++ ++#define NS_CFG_VPIBITS_0 0x00000000 ++#define NS_CFG_VPIBITS_1 0x00040000 ++#define NS_CFG_VPIBITS_2 0x00080000 ++#define NS_CFG_VPIBITS_8 0x000C0000 ++ ++#define NS_CFG_RCTSIZE_4096_ENTRIES 0x00000000 ++#define NS_CFG_RCTSIZE_8192_ENTRIES 0x00010000 ++#define NS_CFG_RCTSIZE_16384_ENTRIES 0x00020000 ++ ++#define NS_CFG_RXINT_NOINT 0x00000000 ++#define NS_CFG_RXINT_NODELAY 0x00001000 ++#define NS_CFG_RXINT_314US 0x00002000 ++#define NS_CFG_RXINT_624US 0x00003000 ++#define NS_CFG_RXINT_899US 0x00004000 ++ ++ ++/* NICStAR STATus bits ********************************************************/ ++ ++#define NS_STAT_SFBQC_MASK 0xFF000000 ++#define NS_STAT_LFBQC_MASK 0x00FF0000 ++#define NS_STAT_TSIF 0x00008000 ++#define NS_STAT_TXICP 0x00004000 ++#define NS_STAT_TSQF 0x00001000 ++#define NS_STAT_TMROF 0x00000800 ++#define NS_STAT_PHYI 0x00000400 ++#define NS_STAT_CMDBZ 0x00000200 ++#define NS_STAT_SFBQF 0x00000100 ++#define NS_STAT_LFBQF 0x00000080 ++#define NS_STAT_RSQF 0x00000040 ++#define NS_STAT_EOPDU 0x00000020 ++#define NS_STAT_RAWCF 0x00000010 ++#define NS_STAT_SFBQE 0x00000008 ++#define NS_STAT_LFBQE 0x00000004 ++#define NS_STAT_RSQAF 0x00000002 ++ ++#define ns_stat_sfbqc_get(stat) (((stat) & NS_STAT_SFBQC_MASK) >> 23) ++#define ns_stat_lfbqc_get(stat) (((stat) & NS_STAT_LFBQC_MASK) >> 15) ++ ++ ++ ++/* #defines which depend on other #defines ************************************/ ++ ++ ++#define NS_TST0 NS_TST_FRSCD ++#define NS_TST1 (NS_TST_FRSCD + NS_TST_NUM_ENTRIES + 1) ++ ++#define NS_FRSCD (NS_TST1 + NS_TST_NUM_ENTRIES + 1) ++#define NS_FRSCD_SIZE 12 /* 12 dwords */ ++#define NS_FRSCD_NUM ((NS_TST_FRSCD_END + 1 - NS_FRSCD) / NS_FRSCD_SIZE) ++ ++#if (NS_SMBUFSIZE == 48) ++#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_48 ++#elif (NS_SMBUFSIZE == 96) ++#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_96 ++#eliif (NS_SMBUFSIZE == 240) ++#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_240 ++#elif (NS_SMBUFSIZE == 2048) ++#define NS_CFG_SMBUFSIZE NS_CFG_SMBUFSIZE_2048 ++#else ++#error NS_SMBUFSIZE is incorrect in nicstar.h ++#endif /* NS_SMBUFSIZE */ ++ ++#if (NS_LGBUFSIZE == 2048) ++#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_2048 ++#elif (NS_LGBUFSIZE == 4096) ++#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_4096 ++#eliif (NS_LGBUFSIZE == 8192) ++#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_8192 ++#elif (NS_LGBUFSIZE == 16384) ++#define NS_CFG_LGBUFSIZE NS_CFG_LGBUFSIZE_16384 ++#else ++#error NS_LGBUFSIZE is incorrect in nicstar.h ++#endif /* NS_LGBUFSIZE */ ++ ++#if (NS_RSQSIZE == 2048) ++#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_2048 ++#elif (NS_RSQSIZE == 4096) ++#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_4096 ++#elif (NS_RSQSIZE == 8192) ++#define NS_CFG_RSQSIZE NS_CFG_RSQSIZE_8192 ++#else ++#error NS_RSQSIZE is incorrect in nicstar.h ++#endif /* NS_RSQSIZE */ ++ ++#if (NS_VPIBITS == 0) ++#define NS_CFG_VPIBITS NS_CFG_VPIBITS_0 ++#elif (NS_VPIBITS == 1) ++#define NS_CFG_VPIBITS NS_CFG_VPIBITS_1 ++#elif (NS_VPIBITS == 2) ++#define NS_CFG_VPIBITS NS_CFG_VPIBITS_2 ++#elif (NS_VPIBITS == 8) ++#define NS_CFG_VPIBITS NS_CFG_VPIBITS_8 ++#else ++#error NS_VPIBITS is incorrect in nicstar.h ++#endif /* NS_VPIBITS */ ++ ++#ifdef RCQ_SUPPORT ++#define NS_CFG_RAWIE_OPT NS_CFG_RAWIE ++#else ++#define NS_CFG_RAWIE_OPT 0x00000000 ++#endif /* RCQ_SUPPORT */ ++ ++#ifdef ENABLE_TSQFIE ++#define NS_CFG_TSQFIE_OPT NS_CFG_TSQFIE ++#else ++#define NS_CFG_TSQFIE_OPT 0x00000000 ++#endif /* ENABLE_TSQFIE */ ++ ++ ++/* PCI stuff ******************************************************************/ ++ ++#ifndef PCI_VENDOR_ID_IDT ++#define PCI_VENDOR_ID_IDT 0x111D ++#endif /* PCI_VENDOR_ID_IDT */ ++ ++#ifndef PCI_DEVICE_ID_IDT_IDT77201 ++#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 ++#endif /* PCI_DEVICE_ID_IDT_IDT77201 */ ++ ++ ++ ++/* Device driver structures ***************************************************/ ++ ++ ++typedef struct tsq_info ++{ ++ void *org; ++ ns_tsi *base; ++ ns_tsi *next; ++ ns_tsi *last; ++} tsq_info; ++ ++ ++typedef struct scq_info ++{ ++ void *org; ++ ns_scqe *base; ++ ns_scqe *last; ++ ns_scqe *next; ++ volatile ns_scqe *tail; /* Not related to the nicstar register */ ++ unsigned num_entries; ++ struct sk_buff **skb; /* Pointer to an array of pointers ++ to the sk_buffs used for tx */ ++ u32 scd; /* SRAM address of the corresponding ++ SCD */ ++ int tbd_count; /* Only meaningful on variable rate */ ++ struct wait_queue *scqfull_waitq; ++ volatile char full; /* SCQ full indicator */ ++} scq_info; ++ ++ ++ ++typedef struct rsq_info ++{ ++ void *org; ++ ns_rsqe *base; ++ ns_rsqe *next; ++ ns_rsqe *last; ++} rsq_info; ++ ++ ++typedef struct skb_pool ++{ ++ volatile int count; /* number of buffers in the queue */ ++ struct sk_buff_head queue; ++} skb_pool; ++ ++/* NOTE: for small and large buffer pools, the count is not used, as the ++ actual value used for buffer management is the one read from the ++ card. */ ++ ++ ++typedef struct vc_map ++{ ++ volatile int tx:1; /* TX vc? */ ++ volatile int rx:1; /* RX vc? */ ++ struct atm_vcc *tx_vcc, *rx_vcc; ++ struct sk_buff *rx_iov; /* RX iovector skb */ ++ scq_info *scq; /* To keep track of the SCQ */ ++ u32 cbr_scd; /* SRAM address of the corresponding ++ SCD. 0x00000000 for UBR/VBR/ABR */ ++ int tbd_count; ++} vc_map; ++ ++ ++typedef struct ns_dev ++{ ++ int index; /* Card ID to the device driver */ ++ int sram_size; /* In k x 32bit words. 32 or 128 */ ++ u32 membase; /* Card's memory base address */ ++ unsigned long max_pcr; ++ int rct_size; /* Number of entries */ ++ int vpibits; ++ int vcibits; ++ struct pci_dev *pcidev; ++ struct atm_dev *atmdev; ++ tsq_info tsq; ++ rsq_info rsq; ++ scq_info *scq0, *scq1, *scq2; /* VBR SCQs */ ++ skb_pool sbpool; /* Small buffers */ ++ skb_pool lbpool; /* Large buffers */ ++ skb_pool hbpool; /* Pre-allocated huge buffers */ ++ skb_pool iovpool; /* iovector buffers */ ++ volatile int efbie; /* Empty free buf. queue int. enabled */ ++ volatile u32 tst_addr; /* SRAM address of the TST in use */ ++ volatile int tst_free_entries; ++ vc_map vcmap[NS_MAX_RCTSIZE]; ++ vc_map *tste2vc[NS_TST_NUM_ENTRIES]; ++ vc_map *scd2vc[NS_FRSCD_NUM]; ++ buf_nr sbnr; ++ buf_nr lbnr; ++ buf_nr hbnr; ++ buf_nr iovnr; ++ int sbfqc; ++ int lbfqc; ++ u32 sm_handle; ++ u32 sm_addr; ++ u32 lg_handle; ++ u32 lg_addr; ++ struct sk_buff *rcbuf; /* Current raw cell buffer */ ++ u32 rawch; /* Raw cell queue head */ ++} ns_dev; ++ ++ ++ /* NOTE: Each tste2vc entry relates a given TST entry to the corresponding ++ CBR vc. If the entry is not allocated, it must be NULL. ++ ++ There are two TSTs so the driver can modify them on the fly ++ without stopping the transmission. ++ ++ scd2vc allows us to find out unused fixed rate SCDs, because ++ they must have a NULL pointer here. */ ++ ++ ++#endif /* _LINUX_NICSTAR_H_ */ +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/nicstar.c Fri Aug 21 02:32:42 1998 +@@ -0,0 +1,2795 @@ ++/****************************************************************************** ++ * ++ * nicstar.c ++ * ++ * Device driver supporting CBR for NICStAR based cards. ++ * ++ * IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME. ++ * It was taken from the frle-0.22 device driver. ++ * As the file doesn't have a copyright notice, in the file ++ * nicstarmac.copyright I put the copyright notice from the ++ * frle-0.22 device driver. ++ * Some code is based on the nicstar driver by M. Welsh. ++ * ++ * Author: Rui Prior ++ * ++ * (C) INESC 1998 ++ * ++ ******************************************************************************/ ++ ++ ++/* Header files ***************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "nicstar.h" ++#include "nicstarmac.h" ++ ++ ++/* Additional code ************************************************************/ ++ ++#include "nicstarmac.c" ++ ++ ++/* Configurable parameters ****************************************************/ ++ ++#undef PHY_LOOPBACK ++#undef TX_DEBUG ++#undef RX_DEBUG ++#undef GENERAL_DEBUG ++#undef EXTRA_DEBUG ++ ++#undef NS_USE_DESTRUCTORS /* For now keep this undefined unless you know ++ you're going to use only raw ATM */ ++ ++ ++/* Do not touch these *********************************************************/ ++ ++#ifdef TX_DEBUG ++#define TXPRINTK(args...) printk(args) ++#else ++#define TXPRINTK(args...) ++#endif /* TX_DEBUG */ ++ ++#ifdef RX_DEBUG ++#define RXPRINTK(args...) printk(args) ++#else ++#define RXPRINTK(args...) ++#endif /* RX_DEBUG */ ++ ++#ifdef GENERAL_DEBUG ++#define PRINTK(args...) printk(args) ++#else ++#define PRINTK(args...) ++#endif /* GENERAL_DEBUG */ ++ ++#ifdef EXTRA_DEBUG ++#define XPRINTK(args...) printk(args) ++#else ++#define XPRINTK(args...) ++#endif /* EXTRA_DEBUG */ ++ ++ ++/* Macros *********************************************************************/ ++ ++#define MAX(a,b) ((a) > (b) ? (a) : (b)) ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++ ++#define CMD_BUSY(card) (readl((card)->membase + STAT) & NS_STAT_CMDBZ) ++ ++#define NS_DELAY mdelay(1) ++ ++#define ALIGN_ADDRESS(addr, alignment) \ ++ ((((u32) (addr)) + (((u32) (alignment)) - 1)) & ~(((u32) (alignment)) - 1)) ++ ++#undef CEIL(d) ++ ++ ++/* Version definition *********************************************************/ ++/* ++#include ++char kernel_version[] = UTS_RELEASE; ++*/ ++ ++/* Function declarations ******************************************************/ ++ ++static u32 ns_read_sram(ns_dev *card, u32 sram_address); ++static void ns_write_sram(ns_dev *card, u32 sram_address, u32 *value, int count); ++static int ns_init_card(int i, struct pci_dev *pcidev); ++static void ns_init_card_error(ns_dev *card, int error); ++static scq_info *get_scq(int size, u32 scd); ++static void free_scq(scq_info *scq, struct atm_vcc *vcc); ++static void push_rxbufs(ns_dev *card, u32 type, u32 handle1, u32 addr1, ++ u32 handle2, u32 addr2); ++static void ns_irq_handler(int irq, void *dev_id, struct pt_regs *regs); ++static int ns_open(struct atm_vcc *vcc, short vpi, int vci); ++static void ns_close(struct atm_vcc *vcc); ++static void fill_tst(ns_dev *card, int n, vc_map *vc); ++static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb); ++static int push_scqe(ns_dev *card, vc_map *vc, scq_info *scq, ns_scqe *tbd, ++ struct sk_buff *skb); ++static void process_tsq(ns_dev *card); ++static void drain_scq(ns_dev *card, scq_info *scq, int pos); ++static void process_rsq(ns_dev *card); ++static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe); ++#ifdef NS_USE_DESTRUCTORS ++static void ns_sb_destructor(struct sk_buff *sb); ++static void ns_lb_destructor(struct sk_buff *lb); ++static void ns_hb_destructor(struct sk_buff *hb); ++#endif /* NS_USE_DESTRUCTORS */ ++static void recycle_rx_buf(ns_dev *card, struct sk_buff *skb); ++static void recycle_iovec_rx_bufs(ns_dev *card, struct iovec *iov, int count); ++static void recycle_iov_buf(ns_dev *card, struct sk_buff *iovb); ++static void dequeue_sm_buf(ns_dev *card, struct sk_buff *sb); ++static void dequeue_lg_buf(ns_dev *card, struct sk_buff *lb); ++static int ns_proc_read(struct atm_dev *dev, loff_t *pos, char *page); ++static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg); ++static void which_list(ns_dev *card, struct sk_buff *skb); ++ ++ ++/* Global variables ***********************************************************/ ++ ++static struct ns_dev *cards[NS_MAX_CARDS]; ++static unsigned num_cards = 0; ++static struct atmdev_ops atm_ops = ++{ ++ NULL, /* dev_close */ ++ ns_open, /* open */ ++ ns_close, /* close */ ++ ns_ioctl, /* ioctl */ ++ NULL, /* getsockopt */ ++ NULL, /* setsockopt */ ++ ns_send, /* send */ ++ NULL, /* sg_send */ ++ NULL, /* send_oam */ ++ NULL, /* phy_put */ ++ NULL, /* phy_get */ ++ NULL, /* feedback */ ++ NULL, /* change_qos */ ++ NULL, /* free_rx_skb */ ++ ns_proc_read /* proc_read */ ++}; ++ ++ ++/* Functions*******************************************************************/ ++ ++#ifdef MODULE ++ ++int init_module(void) ++{ ++ int i; ++ unsigned error = 0; /* Initialized to remove compile warning */ ++ struct pci_dev *pcidev; ++ ++ XPRINTK("nicstar: init_module() called.\n"); ++ if(!pci_present()) ++ { ++ printk("nicstar: no PCI subsystem found.\n"); ++ return -EIO; ++ } ++ ++ for(i = 0; i < NS_MAX_CARDS; i++) ++ cards[i] = NULL; ++ ++ pcidev = NULL; ++ for(i = 0; i < NS_MAX_CARDS; i++) ++ { ++ if ((pcidev = pci_find_device(PCI_VENDOR_ID_IDT, ++ PCI_DEVICE_ID_IDT_IDT77201, ++ pcidev)) == NULL) ++ break; ++ ++ error = ns_init_card(i, pcidev); ++ if (error) ++ i--; /* Try to find another card but don't increment index */ ++ } ++ ++ if (i == 0) ++ { ++ if (!error) ++ { ++ printk("nicstar: no cards found.\n"); ++ return -ENXIO; ++ } ++ else ++ return -EIO; ++ } ++ TXPRINTK("nicstar: TX debug enabled.\n"); ++ RXPRINTK("nicstar: RX debug enabled.\n"); ++ PRINTK("nicstar: General debug enabled.\n"); ++#ifdef PHY_LOOPBACK ++ printk("nicstar: using PHY loopback.\n"); ++#endif /* PHY_LOOPBACK */ ++ XPRINTK("nicstar: init_module() returned.\n"); ++ ++ return 0; ++} ++ ++ ++ ++void cleanup_module(void) ++{ ++ int i, j; ++ unsigned short pci_command; ++ ns_dev *card; ++ struct sk_buff *hb; ++ struct sk_buff *iovb; ++ struct sk_buff *lb; ++ struct sk_buff *sb; ++ ++ XPRINTK("nicstar: cleanup_module() called.\n"); ++ ++ if (MOD_IN_USE) ++ printk("nicstar: module in use, remove delayed.\n"); ++ ++ for (i = 0; i < NS_MAX_CARDS; i++) ++ { ++ if (cards[i] == NULL) ++ continue; ++ ++ card = cards[i]; ++ ++ /* Stop everything */ ++ writel(0x00000000, card->membase + CFG); ++ ++ /* De-register device */ ++ atm_dev_deregister(card->atmdev); ++ ++ /* Disable memory mapping and busmastering */ ++ if (pci_read_config_word(card->pcidev, PCI_COMMAND, &pci_command) != 0) ++ { ++ printk("nicstar%d: can't read PCI_COMMAND.\n", i); ++ } ++ pci_command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); ++ if (pci_write_config_word(card->pcidev, PCI_COMMAND, pci_command) != 0) ++ { ++ printk("nicstar%d: can't write PCI_COMMAND.\n", i); ++ } ++ ++ /* Free up resources */ ++ j = 0; ++ PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count); ++ while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) ++ { ++ kfree_skb(hb); ++ j++; ++ } ++ PRINTK("nicstar%d: %d huge buffers freed.\n", i, j); ++ j = 0; ++ PRINTK("nicstar%d: freeing %d iovec buffers.\n", i, card->iovpool.count); ++ while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) ++ { ++ kfree_skb(iovb); ++ j++; ++ } ++ PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j); ++ while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL) ++ kfree_skb(lb); ++ while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL) ++ kfree_skb(sb); ++ free_scq(card->scq0, NULL); ++ for (j = 0; j < NS_FRSCD_NUM; j++) ++ { ++ if (card->scd2vc[j] != NULL) ++ free_scq(card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc); ++ } ++ kfree(card->rsq.org); ++ kfree(card->tsq.org); ++ free_irq(card->pcidev->irq, card); ++ iounmap((void *) card->membase); ++ kfree(card); ++ ++ } ++ XPRINTK("nicstar: cleanup_module() returned.\n"); ++} ++ ++ ++#else ++ ++__initfunc(int nicstar_detect(void)) ++{ ++ int i; ++ unsigned error = 0; /* Initialized to remove compile warning */ ++ struct pci_dev *pcidev; ++ ++ if(!pci_present()) ++ { ++ printk("nicstar: no PCI subsystem found.\n"); ++ return -EIO; ++ } ++ ++ for(i = 0; i < NS_MAX_CARDS; i++) ++ cards[i] = NULL; ++ ++ pcidev = NULL; ++ for(i = 0; i < NS_MAX_CARDS; i++) ++ { ++ if ((pcidev = pci_find_device(PCI_VENDOR_ID_IDT, ++ PCI_DEVICE_ID_IDT_IDT77201, ++ pcidev)) == NULL) ++ break; ++ ++ error = ns_init_card(i, pcidev); ++ if (error) ++ i--; /* Try to find another card but don't increment index */ ++ } ++ ++ if (i == 0 && error) ++ return -EIO; ++ ++ TXPRINTK("nicstar: TX debug enabled.\n"); ++ RXPRINTK("nicstar: RX debug enabled.\n"); ++ PRINTK("nicstar: General debug enabled.\n"); ++#ifdef PHY_LOOPBACK ++ printk("nicstar: using PHY loopback.\n"); ++#endif /* PHY_LOOPBACK */ ++ XPRINTK("nicstar: init_module() returned.\n"); ++ ++ return i; ++} ++ ++ ++#endif /* MODULE */ ++ ++ ++static u32 ns_read_sram(ns_dev *card, u32 sram_address) ++{ ++ unsigned long flags; ++ u32 data; ++ sram_address <<= 2; ++ sram_address &= 0x0007FFFC; /* address must be dword aligned */ ++ sram_address |= 0x50000000; /* SRAM read command */ ++ save_flags(flags); cli(); ++ while (CMD_BUSY(card)); ++ writel(sram_address, card->membase + CMD); ++ while (CMD_BUSY(card)); ++ data = readl(card->membase + DR0); ++ restore_flags(flags); ++ return data; ++} ++ ++ ++ ++static void ns_write_sram(ns_dev *card, u32 sram_address, u32 *value, int count) ++{ ++ unsigned long flags; ++ int i, c; ++ count--; /* count range now is 0..3 instead of 1..4 */ ++ c = count; ++ c <<= 2; /* to use increments of 4 */ ++ save_flags(flags); cli(); ++ while (CMD_BUSY(card)); ++ for (i = 0; i <= c; i += 4) ++ writel(*(value++), card->membase + i); ++ /* Note: DR# registers are the first 4 dwords in nicstar's memspace, ++ so card->membase + DR0 == card->membase */ ++ sram_address <<= 2; ++ sram_address &= 0x0007FFFC; ++ sram_address |= (0x40000000 | count); ++ writel(sram_address, card->membase + CMD); ++ restore_flags(flags); ++} ++ ++ ++static int ns_init_card(int i, struct pci_dev *pcidev) ++{ ++ int j; ++ struct ns_dev *card; ++ unsigned short pci_command; ++ unsigned char pci_latency; ++ unsigned error; ++ u32 data; ++ u32 u32d[4]; ++ u32 ns_cfg_rctsize; ++ int bcount; ++ ++ error = 0; ++ ++ if ((card = kmalloc(sizeof(ns_dev), GFP_KERNEL)) == NULL) ++ { ++ printk("nicstar%d: can't allocate memory for device structure.\n", i); ++ error = 2; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ cards[i] = card; ++ ++ card->index = i; ++ card->pcidev = pcidev; ++ card->membase = (u32) (pcidev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK); ++ card->membase = (u32) ioremap(card->membase, NS_IOREMAP_SIZE); ++ if (card->membase == (u32) (NULL)) ++ { ++ printk("nicstar%d: can't ioremap() membase.\n",i); ++ error = 3; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ PRINTK("nicstar%d: membase at 0x%x.\n", i, card->membase); ++ ++ if (pci_read_config_word(pcidev, PCI_COMMAND, &pci_command) != 0) ++ { ++ printk("nicstar%d: can't read PCI_COMMAND.\n", i); ++ error = 4; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); ++ if (pci_write_config_word(pcidev, PCI_COMMAND, pci_command) != 0) ++ { ++ printk("nicstar%d: can't write PCI_COMMAND.\n", i); ++ error = 5; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ ++ if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0) ++ { ++ printk("nicstar%d: can't read PCI latency timer.\n", i); ++ error = 6; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ if (pci_latency < NS_PCI_LATENCY) ++ { ++ PRINTK("nicstar%d: setting PCI latency timer to %d.\n", i, NS_PCI_LATENCY); ++ for (j = 1; j < 4; j++) ++ { ++ if (pci_write_config_byte(pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0); ++ break; ++ } ++ if (j == 10) ++ { ++ printk("nicstar%d: can't set PCI latency timer to %d.\n", i, NS_PCI_LATENCY); ++ error = 7; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ } ++ ++ /* Clear timer overflow */ ++ data = readl(card->membase + STAT); ++ if (data & NS_STAT_TMROF) ++ writel(NS_STAT_TMROF, card->membase + STAT); ++ ++ /* Software reset */ ++ writel(NS_CFG_SWRST, card->membase + CFG); ++ NS_DELAY; ++ writel(0x00000000, card->membase + CFG); ++ ++ /* PHY reset */ ++ writel(0x00000008, card->membase + GP); ++ NS_DELAY; ++ writel(0x00000001, card->membase + GP); ++ NS_DELAY; ++ while (CMD_BUSY(card)); ++ writel(NS_CMD_WRITE_UTILITY | 0x00000100, card->membase + CMD); /* Sync UTOPIA with SAR clock */ ++ NS_DELAY; ++ ++ /* Detect PHY type */ ++ while (CMD_BUSY(card)); ++ writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD); ++ while (CMD_BUSY(card)); ++ data = readl(card->membase + DR0); ++ if (data == 0x00000009) ++ { ++ printk("nicstar%d: PHY seems to be 25 Mbps.\n", i); ++ card->max_pcr = IDT_25_PCR; ++ while(CMD_BUSY(card)); ++ writel(0x00000008, card->membase + DR0); ++ writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD); ++ /* Clear an eventual pending interrupt */ ++ writel(NS_STAT_SFBQF, card->membase + STAT); ++#ifdef PHY_LOOPBACK ++ while(CMD_BUSY(card)); ++ writel(0x00000022, card->membase + DR0); ++ writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD); ++#endif /* PHY_LOOPBACK */ ++ } ++ else if (data == 0x00000030) ++ { ++ printk("nicstar%d: PHY seems to be 155 Mbps.\n", i); ++ card->max_pcr = ATM_OC3_PCR; ++#ifdef PHY_LOOPBACK ++ while(CMD_BUSY(card)); ++ writel(0x00000002, card->membase + DR0); ++ writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD); ++#endif /* PHY_LOOPBACK */ ++ } ++ else ++ { ++ printk("nicstar%d: can't determine PHY type.\n", i); ++ error = 8; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ writel(0x00000000, card->membase + GP); ++ ++ /* Determine SRAM size */ ++ data = 0x76543210; ++ ns_write_sram(card, 0x1C003, &data, 1); ++ data = 0x89ABCDEF; ++ ns_write_sram(card, 0x14003, &data, 1); ++ if (ns_read_sram(card, 0x14003) == 0x89ABCDEF && ++ ns_read_sram(card, 0x1C003) == 0x76543210) ++ card->sram_size = 128; ++ else ++ card->sram_size = 32; ++ PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size); ++ ++ card->rct_size = NS_MAX_RCTSIZE; ++ ++#if (NS_MAX_RCTSIZE == 4096) ++ if (card->sram_size == 128) ++ printk("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n", i); ++#elif (NS_MAX_RCTSIZE == 16384) ++ if (card->sram_size == 32) ++ { ++ printk("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n", i); ++ card->rct_size = 4096; ++ } ++#else ++#error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c ++#endif ++ ++ card->vpibits = NS_VPIBITS; ++ if (card->rct_size == 4096) ++ card->vcibits = 12 - NS_VPIBITS; ++ else /* card->rct_size == 16384 */ ++ card->vcibits = 14 - NS_VPIBITS; ++ ++#ifdef ESI_FROM_EPROM ++ /* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */ ++ nicstar_init_eprom(card->membase); ++#endif /* ESI_FROM_EPROM */ ++ ++ if (request_irq(pcidev->irq, &ns_irq_handler, SA_INTERRUPT, "nicstar", card) != 0) ++ { ++ printk("nicstar%d: can't allocate IRQ.\n", i); ++ error = 9; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ ++ /* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */ ++ writel(0x00000000, card->membase + VPM); ++ ++ /* Initialize TSQ */ ++ card->tsq.org = kmalloc(NS_TSQSIZE + NS_TSQ_ALIGNMENT, GFP_KERNEL); ++ if (card->tsq.org == NULL) ++ { ++ printk("nicstar%d: can't allocate TSQ.\n", i); ++ error = 10; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ card->tsq.base = (ns_tsi *) ALIGN_ADDRESS(card->tsq.org, NS_TSQ_ALIGNMENT); ++ card->tsq.next = card->tsq.base; ++ card->tsq.last = card->tsq.base + (NS_TSQ_NUM_ENTRIES - 1); ++ for (j = 0; j < NS_TSQ_NUM_ENTRIES; j++) ++ ns_tsi_init(card->tsq.base + j); ++ writel(0x00000000, card->membase + TSQH); ++ writel((u32) virt_to_bus(card->tsq.base), card->membase + TSQB); ++ PRINTK("nicstar%d: TSQ base at 0x%x 0x%x 0x%x.\n", i, (u32) card->tsq.base, ++ (u32) virt_to_bus(card->tsq.base), readl(card->membase + TSQB)); ++ ++ /* Initialize RSQ */ ++ card->rsq.org = kmalloc(NS_RSQSIZE + NS_RSQ_ALIGNMENT, GFP_KERNEL); ++ if (card->rsq.org == NULL) ++ { ++ printk("nicstar%d: can't allocate RSQ.\n", i); ++ error = 11; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ card->rsq.base = (ns_rsqe *) ALIGN_ADDRESS(card->rsq.org, NS_RSQ_ALIGNMENT); ++ card->rsq.next = card->rsq.base; ++ card->rsq.last = card->rsq.base + (NS_RSQ_NUM_ENTRIES - 1); ++ for (j = 0; j < NS_RSQ_NUM_ENTRIES; j++) ++ ns_rsqe_init(card->rsq.base + j); ++ writel(0x00000000, card->membase + RSQH); ++ writel((u32) virt_to_bus(card->rsq.base), card->membase + RSQB); ++ PRINTK("nicstar%d: RSQ base at 0x%x.\n", i, (u32) card->rsq.base); ++ ++ /* Initialize SCQ0, the only VBR SCQ used */ ++ card->scq1 = (scq_info *) NULL; ++ card->scq2 = (scq_info *) NULL; ++ card->scq0 = get_scq(VBR_SCQSIZE, NS_VRSCD0); ++ if (card->scq0 == (scq_info *) NULL) ++ { ++ printk("nicstar%d: can't get SCQ0.\n", i); ++ error = 12; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ u32d[0] = (u32) virt_to_bus(card->scq0->base); ++ u32d[1] = (u32) 0x00000000; ++ u32d[2] = (u32) 0xffffffff; ++ u32d[3] = (u32) 0x00000000; ++ ns_write_sram(card, NS_VRSCD0, u32d, 4); ++ ns_write_sram(card, NS_VRSCD1, u32d, 4); /* These last two won't be used */ ++ ns_write_sram(card, NS_VRSCD2, u32d, 4); /* but are initialized, just in case... */ ++ card->scq0->scd = NS_VRSCD0; ++ PRINTK("nicstar%d: VBR-SCQ0 base at 0x%x.\n", i, (u32) card->scq0->base); ++ ++ /* Initialize TSTs */ ++ card->tst_addr = NS_TST0; ++ card->tst_free_entries = NS_TST_NUM_ENTRIES; ++ data = NS_TST_OPCODE_VARIABLE; ++ for (j = 0; j < NS_TST_NUM_ENTRIES; j++) ++ ns_write_sram(card, NS_TST0 + j, &data, 1); ++ data = ns_tste_make(NS_TST_OPCODE_END, NS_TST0); ++ ns_write_sram(card, NS_TST0 + NS_TST_NUM_ENTRIES, &data, 1); ++ for (j = 0; j < NS_TST_NUM_ENTRIES; j++) ++ ns_write_sram(card, NS_TST1 + j, &data, 1); ++ data = ns_tste_make(NS_TST_OPCODE_END, NS_TST1); ++ ns_write_sram(card, NS_TST1 + NS_TST_NUM_ENTRIES, &data, 1); ++ for (j = 0; j < NS_TST_NUM_ENTRIES; j++) ++ card->tste2vc[j] = NULL; ++ writel(NS_TST0 << 2, card->membase + TSTB); ++ ++ ++ /* Initialize RCT. AAL type is set on opening the VC. */ ++#ifdef RCQ_SUPPORT ++ u32d[0] = NS_RCTE_RAWCELLINTEN; ++#else ++ u32d[0] = 0x00000000; ++#endif RCQ_SUPPORT ++ u32d[1] = 0x00000000; ++ u32d[2] = 0x00000000; ++ u32d[3] = 0xFFFFFFFF; ++ for (j = 0; j < card->rct_size; j++) ++ ns_write_sram(card, j * 4, u32d, 4); ++ ++ memset(card->vcmap, 0, NS_MAX_RCTSIZE * sizeof(vc_map)); ++ ++ for (j = 0; j < NS_FRSCD_NUM; j++) ++ card->scd2vc[j] = NULL; ++ ++ /* Initialize buffer levels */ ++ card->sbnr.min = MIN_SB; ++ card->sbnr.init = NUM_SB; ++ card->sbnr.max = MAX_SB; ++ card->lbnr.min = MIN_LB; ++ card->lbnr.init = NUM_LB; ++ card->lbnr.max = MAX_LB; ++ card->iovnr.min = MIN_IOVB; ++ card->iovnr.init = NUM_IOVB; ++ card->iovnr.max = MAX_IOVB; ++ card->hbnr.min = MIN_HB; ++ card->hbnr.init = NUM_HB; ++ card->hbnr.max = MAX_HB; ++ ++ card->sm_handle = 0x00000000; ++ card->sm_addr = 0x00000000; ++ card->lg_handle = 0x00000000; ++ card->lg_addr = 0x00000000; ++ ++ card->efbie = 1; /* To prevent push_rxbufs from enabling the interrupt */ ++ ++ /* Allocate small buffers */ ++ skb_queue_head_init(&card->sbpool.queue); ++ card->sbpool.count = 0; /* Not used */ ++ for (j = 0; j < NUM_SB; j++) ++ { ++ struct sk_buff *sb; ++ sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); ++ if (sb == NULL) ++ { ++ printk("nicstar%d: can't allocate %dth of %d small buffers.\n", ++ i, j, NUM_SB); ++ error = 13; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ skb_queue_tail(&card->sbpool.queue, sb); ++ skb_reserve(sb, NS_AAL0_HEADER); ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0); ++ } ++ /* Test for strange behaviour which leads to crashes */ ++ if ((bcount = ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min) ++ { ++ printk("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n", ++ i, j, bcount); ++ error = 13; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ ++ ++ /* Allocate large buffers */ ++ skb_queue_head_init(&card->lbpool.queue); ++ card->lbpool.count = 0; /* Not used */ ++ for (j = 0; j < NUM_LB; j++) ++ { ++ struct sk_buff *lb; ++ lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); ++ if (lb == NULL) ++ { ++ printk("nicstar%d: can't allocate %dth of %d large buffers.\n", ++ i, j, NUM_LB); ++ error = 14; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ skb_queue_tail(&card->lbpool.queue, lb); ++ skb_reserve(lb, NS_SMBUFSIZE); ++ push_rxbufs(card, BUF_LG, (u32) lb, (u32) virt_to_bus(lb->data), 0, 0); ++ /* Due to the implementation of push_rxbufs() this is 1, not 0 */ ++ if (j == 1) ++ { ++ card->rcbuf = lb; ++ card->rawch = (u32) virt_to_bus(lb->data); ++ } ++ } ++ /* Test for strange behaviour which leads to crashes */ ++ if ((bcount = ns_stat_lfbqc_get(readl(card->membase + STAT))) < card->lbnr.min) ++ { ++ printk("nicstar%d: Strange... Just allocated %d large buffers and lfbqc = %d.\n", ++ i, j, bcount); ++ error = 14; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ ++ ++ /* Allocate iovec buffers */ ++ skb_queue_head_init(&card->iovpool.queue); ++ card->iovpool.count = 0; ++ for (j = 0; j < NUM_IOVB; j++) ++ { ++ struct sk_buff *iovb; ++ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL); ++ if (iovb == NULL) ++ { ++ printk("nicstar%d: can't allocate %dth of %d iovec buffers.\n", ++ i, j, NUM_IOVB); ++ error = 15; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ skb_queue_tail(&card->iovpool.queue, iovb); ++ card->iovpool.count++; ++ } ++ ++ ++ /* Pre-allocate some huge buffers */ ++ skb_queue_head_init(&card->hbpool.queue); ++ card->hbpool.count = 0; ++ for (j = 0; j < NUM_HB; j++) ++ { ++ struct sk_buff *hb; ++ hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); ++ if (hb == NULL) ++ { ++ printk("nicstar%d: can't allocate %dth of %d huge buffers.\n", ++ i, j, NUM_HB); ++ error = 16; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ skb_queue_tail(&card->hbpool.queue, hb); ++ card->hbpool.count++; ++ } ++ ++ ++ /* Configure NICStAR */ ++ if (card->rct_size == 4096) ++ ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES; ++ else /* (card->rct_size == 16384) */ ++ ns_cfg_rctsize = NS_CFG_RCTSIZE_16384_ENTRIES; ++ ++ card->efbie = 1; ++ writel(NS_CFG_RXPATH | ++ NS_CFG_SMBUFSIZE | ++ NS_CFG_LGBUFSIZE | ++ NS_CFG_EFBIE | ++ NS_CFG_RSQSIZE | ++ NS_CFG_VPIBITS | ++ ns_cfg_rctsize | ++ NS_CFG_RXINT_NODELAY | ++ NS_CFG_RAWIE | /* Only enabled if RCQ_SUPPORT */ ++ NS_CFG_RSQAFIE | ++ NS_CFG_TXEN | ++ NS_CFG_TXIE | ++ NS_CFG_TSQFIE_OPT, /* Only enabled if ENABLE_TSQFIE */ ++ card->membase + CFG); ++ ++ /* Register device */ ++ card->atmdev = atm_dev_register("nicstar", &atm_ops, -1, 0UL); ++ if (card->atmdev == NULL) ++ { ++ printk("nicstar%d: can't register device.\n", i); ++ error = 17; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ ++#ifdef ESI_FROM_EPROM ++ nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET, ++ card->atmdev->esi, 6); ++ printk("nicstar%d: MAC address %02X:%02X:%02X:%02X:%02X:%02X\n", i, ++ card->atmdev->esi[0], card->atmdev->esi[1], card->atmdev->esi[2], ++ card->atmdev->esi[3], card->atmdev->esi[4], card->atmdev->esi[5]); ++#else ++ card->atmdev->esi[0] = NS_ESI0; ++ card->atmdev->esi[1] = NS_ESI1; ++ card->atmdev->esi[2] = NS_ESI2; ++ card->atmdev->esi[3] = NS_ESI3; ++ card->atmdev->esi[4] = NS_ESI4; ++ card->atmdev->esi[5] = NS_ESI5; ++#endif /* ESI_FROM_EPROM */ ++ ++ card->atmdev->dev_data = card; ++ card->atmdev->ci_range.vpi_bits = card->vpibits; ++ card->atmdev->ci_range.vci_bits = card->vcibits; ++ ++ num_cards++; ++ ++ return error; ++} ++ ++ ++ ++static void ns_init_card_error(ns_dev *card, int error) ++{ ++ if (error >= 17) ++ { ++ writel(0x00000000, card->membase + CFG); ++ } ++ if (error >= 16) ++ { ++ struct sk_buff *hb; ++ while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL) ++ kfree_skb(hb); ++ } ++ if (error >= 15) ++ { ++ struct sk_buff *iovb; ++ while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL) ++ kfree_skb(iovb); ++ } ++ if (error >= 14) ++ { ++ struct sk_buff *lb; ++ while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL) ++ kfree_skb(lb); ++ } ++ if (error >= 13) ++ { ++ struct sk_buff *sb; ++ while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL) ++ kfree_skb(sb); ++ free_scq(card->scq0, NULL); ++ } ++ if (error >= 12) ++ { ++ kfree(card->rsq.org); ++ } ++ if (error >= 11) ++ { ++ kfree(card->tsq.org); ++ } ++ if (error >= 10) ++ { ++ free_irq(card->pcidev->irq, card); ++ } ++ if (error >= 4) ++ { ++ iounmap((void *) card->membase); ++ } ++ if (error >= 3) ++ { ++ kfree(card); ++ } ++} ++ ++ ++ ++static scq_info *get_scq(int size, u32 scd) ++{ ++ scq_info *scq; ++ int i; ++ ++ if (size != VBR_SCQSIZE && size != CBR_SCQSIZE) ++ return (scq_info *) NULL; ++ ++ scq = (scq_info *) kmalloc(sizeof(scq_info), GFP_KERNEL); ++ if (scq == (scq_info *) NULL) ++ return (scq_info *) NULL; ++ scq->org = kmalloc(2 * size, GFP_KERNEL); ++ if (scq->org == NULL) ++ { ++ kfree(scq); ++ return (scq_info *) NULL; ++ } ++ scq->skb = (struct sk_buff **) kmalloc(sizeof(struct sk_buff *) * ++ (size / NS_SCQE_SIZE), GFP_KERNEL); ++ if (scq->skb == (struct sk_buff **) NULL) ++ { ++ kfree(scq->org); ++ kfree(scq); ++ return (scq_info *) NULL; ++ } ++ scq->num_entries = size / NS_SCQE_SIZE; ++ scq->base = (ns_scqe *) ALIGN_ADDRESS(scq->org, size); ++ scq->next = scq->base; ++ scq->last = scq->base + (scq->num_entries - 1); ++ scq->tail = scq->last; ++ scq->scd = scd; ++ scq->num_entries = size / NS_SCQE_SIZE; ++ scq->tbd_count = 0; ++ scq->scqfull_waitq = NULL; ++ scq->full = 0; ++ ++ for (i = 0; i < scq->num_entries; i++) ++ scq->skb[i] = NULL; ++ ++ return scq; ++} ++ ++ ++ ++/* For variable rate SCQ vcc must be NULL */ ++static void free_scq(scq_info *scq, struct atm_vcc *vcc) ++{ ++ int i; ++ ++ if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) ++ for (i = 0; i < scq->num_entries; i++) ++ { ++ if (scq->skb[i] != NULL) ++ { ++ vcc = scq->skb[i]->atm.vcc; ++ if (vcc->pop != NULL) ++ vcc->pop(vcc, scq->skb[i]); ++ else ++ dev_kfree_skb(scq->skb[i]); ++ } ++ } ++ else /* vcc must be != NULL */ ++ { ++ if (vcc == NULL) ++ { ++ printk("nicstar: free_scq() called with vcc == NULL for fixed rate scq."); ++ for (i = 0; i < scq->num_entries; i++) ++ dev_kfree_skb(scq->skb[i]); ++ } ++ else ++ for (i = 0; i < scq->num_entries; i++) ++ { ++ if (scq->skb[i] != NULL) ++ { ++ if (vcc->pop != NULL) ++ vcc->pop(vcc, scq->skb[i]); ++ else ++ dev_kfree_skb(scq->skb[i]); ++ } ++ } ++ } ++ kfree(scq->skb); ++ kfree(scq->org); ++ kfree(scq); ++} ++ ++ ++ ++/* The handles passed must be pointers to the sk_buff containing the small ++ or large buffer(s) cast to u32. */ ++static void push_rxbufs(ns_dev *card, u32 type, u32 handle1, u32 addr1, ++ u32 handle2, u32 addr2) ++{ ++ u32 stat; ++ unsigned long flags; ++ ++ ++#ifdef GENERAL_DEBUG ++ if (!addr1) ++ printk("nicstar%d: push_rxbufs called with addr1 = 0.\n", card->index); ++#endif /* GENERAL_DEBUG */ ++ ++ stat = readl(card->membase + STAT); ++ card->sbfqc = ns_stat_sfbqc_get(stat); ++ card->lbfqc = ns_stat_lfbqc_get(stat); ++ if (type == BUF_SM) ++ { ++ if (!addr2) ++ { ++ if (card->sm_addr) ++ { ++ addr2 = card->sm_addr; ++ handle2 = card->sm_handle; ++ card->sm_addr = 0x00000000; ++ card->sm_handle = 0x00000000; ++ } ++ else /* (!sm_addr) */ ++ { ++ card->sm_addr = addr1; ++ card->sm_handle = handle1; ++ } ++ } ++ } ++ else /* type == BUF_LG */ ++ { ++ if (!addr2) ++ { ++ if (card->lg_addr) ++ { ++ addr2 = card->lg_addr; ++ handle2 = card->lg_handle; ++ card->lg_addr = 0x00000000; ++ card->lg_handle = 0x00000000; ++ } ++ else /* (!lg_addr) */ ++ { ++ card->lg_addr = addr1; ++ card->lg_handle = handle1; ++ } ++ } ++ } ++ ++ if (addr2) ++ { ++ if (type == BUF_SM) ++ { ++ if (card->sbfqc >= card->sbnr.max) ++ { ++ skb_unlink((struct sk_buff *) handle1); ++ kfree_skb((struct sk_buff *) handle1); ++ skb_unlink((struct sk_buff *) handle2); ++ kfree_skb((struct sk_buff *) handle2); ++ return; ++ } ++ else ++ card->sbfqc += 2; ++ } ++ else /* (type == BUF_LG) */ ++ { ++ if (card->lbfqc >= card->lbnr.max) ++ { ++ skb_unlink((struct sk_buff *) handle1); ++ kfree_skb((struct sk_buff *) handle1); ++ skb_unlink((struct sk_buff *) handle2); ++ kfree_skb((struct sk_buff *) handle2); ++ return; ++ } ++ else ++ card->lbfqc += 2; ++ } ++ ++ save_flags(flags); cli(); ++ ++ while (CMD_BUSY(card)); ++ writel(handle1, card->membase + DR0); ++ writel(addr1, card->membase + DR1); ++ writel(handle2, card->membase + DR2); ++ writel(addr2, card->membase + DR3); ++ writel(NS_CMD_WRITE_FREEBUFQ | (u32) type, card->membase + CMD); ++ ++ restore_flags(flags); ++ ++ XPRINTK("nicstar%d: Pushing %s buffers at 0x%x and 0x%x.\n", card->index, ++ (type == BUF_SM ? "small" : "large"), addr1, addr2); ++ } ++ ++ if (!card->efbie && card->sbfqc >= card->sbnr.min && ++ card->lbfqc >= card->lbnr.min) ++ { ++ card->efbie = 1; ++ writel((readl(card->membase + CFG) | NS_CFG_EFBIE), card->membase + CFG); ++ } ++ ++ return; ++} ++ ++ ++ ++static void ns_irq_handler(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ u32 stat_r; ++ ns_dev *card; ++ ++ card = (ns_dev *) dev_id; ++ ++ PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index); ++ ++ stat_r = readl(card->membase + STAT); ++ ++ /* Transmit Status Indicator has been written to T. S. Queue */ ++ if (stat_r & NS_STAT_TSIF) ++ { ++ TXPRINTK("nicstar%d: TSI interrupt\n", card->index); ++ process_tsq(card); ++ /* If there are no entries to process, clear the interrupt */ ++ if (readl(card->membase + TSQT) == (u32) virt_to_bus(card->tsq.next)) ++ writel(NS_STAT_TSIF, card->membase + STAT); ++ } ++ ++ /* Incomplete CS-PDU has been transmitted */ ++ if (stat_r & NS_STAT_TXICP) ++ { ++ writel(NS_STAT_TXICP, card->membase + STAT); ++ TXPRINTK("nicstar%d: Incomplete CS-PDU transmitted.\n", ++ card->index); ++ } ++ ++ /* Transmit Status Queue 7/8 full */ ++ if (stat_r & NS_STAT_TSQF) ++ { ++ writel(NS_STAT_TSQF, card->membase + STAT); ++ PRINTK("nicstar%d: TSQ full.\n", card->index); ++ process_tsq(card); ++ } ++ ++ /* Timer overflow */ ++ if (stat_r & NS_STAT_TMROF) ++ { ++ writel(NS_STAT_TMROF, card->membase + STAT); ++ PRINTK("nicstar%d: Timer overflow.\n", card->index); ++ } ++ ++ /* PHY device interrupt signal active */ ++ if (stat_r & NS_STAT_PHYI) ++ { ++ writel(NS_STAT_PHYI, card->membase + STAT); ++ printk("nicstar%d: PHY interrupt.\n", card->index); ++ } ++ ++ /* Small Buffer Queue is full */ ++ if (stat_r & NS_STAT_SFBQF) ++ { ++ writel(NS_STAT_SFBQF, card->membase + STAT); ++ printk("nicstar%d: Small free buffer queue is full.\n", card->index); ++ } ++ ++ /* Large Buffer Queue is full */ ++ if (stat_r & NS_STAT_LFBQF) ++ { ++ writel(NS_STAT_LFBQF, card->membase + STAT); ++ printk("nicstar%d: Large free buffer queue is full.\n", card->index); ++ } ++ ++ /* Receive Status Queue is full */ ++ if (stat_r & NS_STAT_RSQF) ++ { ++ writel(NS_STAT_RSQF, card->membase + STAT); ++ printk("nicstar%d: RSQ full.\n", card->index); ++ process_rsq(card); ++ } ++ ++ /* Complete CS-PDU received */ ++ if (stat_r & NS_STAT_EOPDU) ++ { ++ RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index); ++ process_rsq(card); ++ /* If there are no entries to process, clear the interrupt */ ++ if (readl(card->membase + RSQT) == (u32) virt_to_bus(card->rsq.next)) ++ writel(NS_STAT_EOPDU, card->membase + STAT); ++ } ++ ++ /* Raw cell received */ ++ if (stat_r & NS_STAT_RAWCF) ++ { ++ writel(NS_STAT_RAWCF, card->membase + STAT); ++#ifndef RCQ_SUPPORT ++ printk("nicstar%d: Raw cell received and no support yet...\n", ++ card->index); ++#endif /* RCQ_SUPPORT */ ++ /* NOTE: the following procedure may keep a raw cell pending untill the ++ next interrupt. As this preliminary support is only meant to ++ avoid buffer leakage, this is not an issue. */ ++ while (readl(card->membase + RAWCT) != card->rawch) ++ { ++ ns_rcqe *rawcell; ++ ++ rawcell = (ns_rcqe *) bus_to_virt(card->rawch); ++ if (ns_rcqe_islast(rawcell)) ++ { ++ struct sk_buff *oldbuf; ++ ++ oldbuf = card->rcbuf; ++ card->rcbuf = (struct sk_buff *) ns_rcqe_nextbufhandle(rawcell); ++ card->rawch = (u32) virt_to_bus(card->rcbuf->data); ++ recycle_rx_buf(card, oldbuf); ++ } ++ else ++ card->rawch += NS_RCQE_SIZE; ++ } ++ } ++ ++ /* Small buffer queue is empty */ ++ if (stat_r & NS_STAT_SFBQE) ++ { ++ int i; ++ struct sk_buff *sb; ++ ++ writel(NS_STAT_SFBQE, card->membase + STAT); ++ printk("nicstar%d: Small free buffer queue empty.\n", ++ card->index); ++ for (i = 0; i < card->sbnr.min; i++) ++ { ++ sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC); ++ if (sb == NULL) ++ { ++ writel(readl(card->membase + CFG) & ~NS_CFG_EFBIE, card->membase + CFG); ++ card->efbie = 0; ++ break; ++ } ++ skb_queue_tail(&card->sbpool.queue, sb); ++ skb_reserve(sb, NS_AAL0_HEADER); ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0); ++ } ++ card->sbfqc = i; ++ process_rsq(card); ++ } ++ ++ /* Large buffer queue empty */ ++ if (stat_r & NS_STAT_LFBQE) ++ { ++ int i; ++ struct sk_buff *lb; ++ ++ writel(NS_STAT_LFBQE, card->membase + STAT); ++ printk("nicstar%d: Large free buffer queue empty.\n", ++ card->index); ++ for (i = 0; i < card->lbnr.min; i++) ++ { ++ lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC); ++ if (lb == NULL) ++ { ++ writel(readl(card->membase + CFG) & ~NS_CFG_EFBIE, card->membase + CFG); ++ card->efbie = 0; ++ break; ++ } ++ skb_queue_tail(&card->lbpool.queue, lb); ++ skb_reserve(lb, NS_SMBUFSIZE); ++ push_rxbufs(card, BUF_LG, (u32) lb, (u32) virt_to_bus(lb->data), 0, 0); ++ } ++ card->lbfqc = i; ++ process_rsq(card); ++ } ++ ++ /* Receive Status Queue is 7/8 full */ ++ if (stat_r & NS_STAT_RSQAF) ++ { ++ writel(NS_STAT_RSQAF, card->membase + STAT); ++ RXPRINTK("nicstar%d: RSQ almost full.\n", card->index); ++ process_rsq(card); ++ } ++ ++ XPRINTK("nicstar%d: end of interrupt service\n", card->index); ++} ++ ++ ++ ++static int ns_open(struct atm_vcc *vcc, short vpi, int vci) ++{ ++ ns_dev *card; ++ vc_map *vc; ++ int error; ++ double tmpd; ++ int tcr, tcra; /* target cell rate, and absolute value */ ++ int n = 0; /* Number of entries in the TST. Initialized to remove ++ the compiler warning. */ ++ u32 u32d[4]; ++ int frscdi = 0; /* Index of the SCD. Initialized to remove the compiler ++ warning. How I wish compilers were clever enough to ++ tell which variables can truly be used ++ uninitialized... */ ++ ++ card = (ns_dev *) vcc->dev->dev_data; ++ PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int) vpi, vci); ++ if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) ++ { ++ PRINTK("nicstar%d: unsupported AAL.\n", card->index); ++ return -EINVAL; ++ } ++ ++ if ((error = atm_find_ci(vcc, &vpi, &vci))) ++ { ++ PRINTK("nicstar%d: error in atm_find_ci().\n", card->index); ++ return error; ++ } ++ vc = &(card->vcmap[vpi << card->vcibits | vci]); ++ vcc->vpi = vpi; ++ vcc->vci = vci; ++ vcc->dev_data = vc; ++ vcc->flags |= ATM_VF_ADDR; ++ ++ /* Check requested cell rate and availability of SCD if CBR */ ++ if (vcc->qos.txtp.traffic_class == ATM_CBR) ++ { ++ if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0 && ++ vcc->qos.txtp.min_pcr == 0) ++ { ++ PRINTK("nicstar%d: trying to open a CBR vc with cell rate = 0 \n", ++ card->index); ++ vcc->flags &= ~(ATM_VF_ADDR); ++ return -EINVAL; ++ } ++ ++ tcr = atm_pcr_goal(&(vcc->qos.txtp)); ++ tcra = tcr >= 0 ? tcr : -tcr; ++ ++ PRINTK("nicstar%d: target cell rate = %d.\n", card->index, ++ vcc->qos.txtp.max_pcr); ++ ++ tmpd = ((double) tcra) * ((double) NS_TST_NUM_ENTRIES) / ++ ((double) card->max_pcr); ++ ++ n = (int) tmpd; ++ if (tcr > 0) ++ { ++ if (tmpd > (double) n) n++; ++ } ++ else if (tcr < 0) ++ { ++ if (tmpd < (double) n) n--; ++ } ++ else /* tcr == 0 */ ++ { ++ if ((n = (card->tst_free_entries - NS_TST_RESERVED)) <= 0) ++ { ++ PRINTK("nicstar%d: no CBR bandwidth free.\n", card->index); ++ vcc->flags &= ~(ATM_VF_ADDR); ++ return -EINVAL; ++ } ++ } ++ ++ if (n == 0) ++ { ++ printk("nicstar%d: selected bandwidth < granularity.\n", card->index); ++ vcc->flags &= ~(ATM_VF_ADDR); ++ return -EINVAL; ++ } ++ ++ if (vcc->qos.txtp.max_pcr > 0) ++ { ++ tmpd = (double) n * (double) card->max_pcr / ++ (double) NS_TST_NUM_ENTRIES; ++ if (tmpd > PCR_TOLERANCE * (double) vcc->qos.txtp.max_pcr) ++ { ++ PRINTK("nicstar%d: target cell rate exceeded requested max_pcr.\n", ++ card->index); ++ } ++ } ++ ++ if (n > (card->tst_free_entries - NS_TST_RESERVED)) ++ { ++ PRINTK("nicstar%d: not enough free CBR bandwidth.\n", card->index); ++ vcc->flags &= ~(ATM_VF_ADDR); ++ return -EINVAL; ++ } ++ else ++ card->tst_free_entries -= n; ++ ++ XPRINTK("nicstar%d: writing %d tst entries.\n", card->index, n); ++ for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) ++ { ++ if (card->scd2vc[frscdi] == NULL) ++ { ++ card->scd2vc[frscdi] = vc; ++ break; ++ } ++ } ++ if (frscdi == NS_FRSCD_NUM) ++ { ++ PRINTK("nicstar%d: no SCD available for CBR channel.\n", card->index); ++ card->tst_free_entries += n; ++ vcc->flags &= ~(ATM_VF_ADDR); ++ return -EBUSY; ++ } ++ } ++ ++ /* NOTE: You are not allowed to modify an open connection's QOS. To change ++ that, remove the ATM_VF_PARTIAL flag checking. There may be other changes ++ needed to do that. */ ++ if (!(vcc->flags & ATM_VF_PARTIAL)) ++ { ++ scq_info *scq; ++ ++ vcc->flags |= ATM_VF_PARTIAL; ++ if (vcc->qos.txtp.traffic_class == ATM_CBR) ++ { ++ vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE; ++ ++ scq = get_scq(CBR_SCQSIZE, vc->cbr_scd); ++ if (scq == (scq_info *) NULL) ++ { ++ PRINTK("nicstar%d: can't get fixed rate SCQ.\n", card->index); ++ card->scd2vc[frscdi] = NULL; ++ card->tst_free_entries += n; ++ vcc->flags &= ~(ATM_VF_ADDR | ATM_VF_PARTIAL); ++ return -ENOMEM; ++ } ++ vc->scq = scq; ++ u32d[0] = (u32) virt_to_bus(scq->base); ++ u32d[1] = (u32) 0x00000000; ++ u32d[2] = (u32) 0xffffffff; ++ u32d[3] = (u32) 0x00000000; ++ ns_write_sram(card, vc->cbr_scd, u32d, 4); ++ ++ fill_tst(card, n, vc); ++ } ++ else /* not CBR */ ++ { ++ vc->cbr_scd = 0x00000000; ++ vc->scq = card->scq0; ++ } ++ ++ if (vcc->qos.txtp.traffic_class != ATM_NONE && !vc->tx) ++ { ++ vc->tx = 1; ++ vc->tx_vcc = vcc; ++ vc->tbd_count = 0; ++ } ++ if (vcc->qos.rxtp.traffic_class != ATM_NONE && !vc->rx) ++ { ++ u32 status; ++ ++ vc->rx = 1; ++ vc->rx_vcc = vcc; ++ vc->rx_iov = NULL; ++ ++ /* Open the connection in hardware */ ++ if (vcc->qos.aal == ATM_AAL5) ++ status = NS_RCTE_AAL5 | NS_RCTE_CONNECTOPEN; ++ else /* vcc->qos.aal == ATM_AAL0 */ ++ status = NS_RCTE_AAL0 | NS_RCTE_CONNECTOPEN; ++#ifdef RCQ_SUPPORT ++ status |= NS_RCTE_RAWCELLINTEN; ++#endif /* RCQ_SUPPORT */ ++ ns_write_sram(card, NS_RCT + (vpi << card->vcibits | vci) * ++ NS_RCT_ENTRY_SIZE, &status, 1); ++ } ++ ++ } ++ ++ vcc->flags |= ATM_VF_READY; ++ return 0; ++} ++ ++ ++ ++static void ns_close(struct atm_vcc *vcc) ++{ ++ vc_map *vc; ++ ns_dev *card; ++ u32 u32d[4]; ++ u32 data; ++ int i; ++ ++ vc = vcc->dev_data; ++ card = vcc->dev->dev_data; ++ PRINTK("nicstar%d: closing vpi.vci %d.%d \n", card->index, ++ (int) vcc->vpi, vcc->vci); ++ ++ vcc->flags &= ~(ATM_VF_READY); ++ ++ if (vcc->qos.rxtp.traffic_class != ATM_NONE) ++ { ++ u32 addr; ++ unsigned long flags; ++ ++ addr = NS_RCT + (vcc->vpi << card->vcibits | vcc->vci) * NS_RCT_ENTRY_SIZE; ++ save_flags(flags); cli(); ++ while(CMD_BUSY(card)); ++ writel(NS_CMD_CLOSE_CONNECTION | addr << 2, card->membase + CMD); ++ restore_flags(flags); ++ ++ vc->rx = 0; ++ if (vc->rx_iov != NULL) ++ { ++ struct sk_buff *iovb; ++ u32 stat; ++ ++ stat = readl(card->membase + STAT); ++ card->sbfqc = ns_stat_sfbqc_get(stat); ++ card->lbfqc = ns_stat_lfbqc_get(stat); ++ ++ PRINTK("nicstar%d: closing a VC with pending rx buffers.\n", ++ card->index); ++ iovb = vc->rx_iov; ++ recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, ++ iovb->atm.iovcnt); ++ iovb->atm.iovcnt = 0; ++ iovb->atm.vcc = NULL; ++ save_flags(flags); cli(); ++ recycle_iov_buf(card, iovb); ++ restore_flags(flags); ++ vc->rx_iov = NULL; ++ } ++ } ++ ++ if (vcc->qos.txtp.traffic_class != ATM_NONE) ++ { ++ vc->tx = 0; ++ } ++ ++ if (vcc->qos.txtp.traffic_class == ATM_CBR) ++ { ++ unsigned long flags; ++ ns_scqe *scqep; ++ scq_info *scq; ++ ++ scq = vc->scq; ++ ++ for (;;) ++ { ++ save_flags(flags); cli(); ++ scqep = scq->next; ++ if (scqep == scq->base) ++ scqep = scq->last; ++ else ++ scqep--; ++ if (scqep == scq->tail) ++ { ++ restore_flags(flags); ++ break; ++ } ++ /* If the last entry is not a TSR, place one in the SCQ in order to ++ be able to completely drain it and then close. */ ++ if (!ns_scqe_is_tsr(scqep) && scq->tail != scq->next) ++ { ++ ns_scqe tsr; ++ u32 scdi, scqi; ++ u32 data; ++ ++ tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); ++ scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; ++ scqi = scq->next - scq->base; ++ tsr.word_2 = ns_tsr_mkword_2(scdi, scqi); ++ tsr.word_3 = 0x00000000; ++ tsr.word_4 = 0x00000000; ++ *scq->next = tsr; ++ if (scq->next == scq->last) ++ scq->next = scq->base; ++ else ++ scq->next++; ++ data = (u32) virt_to_bus(scq->next); ++ ns_write_sram(card, scq->scd, &data, 1); ++ } ++ schedule(); ++ restore_flags(flags); ++ } ++ ++ /* Re-initialize SCD to stop transmission */ ++ u32d[1] = ns_read_sram(card, vc->cbr_scd) & NS_SCD_BASE_MASK_FIX; ++ u32d[2] = 0x00000000; ++ ns_write_sram(card, vc->cbr_scd, u32d, 2); ++ ++ /* Free all TST entries */ ++ data = NS_TST_OPCODE_VARIABLE; ++ for (i = 0; i < NS_TST_NUM_ENTRIES; i++) ++ { ++ if (card->tste2vc[i] == vc) ++ { ++ ns_write_sram(card, card->tst_addr + i, &data, 1); ++ card->tste2vc[i] = NULL; ++ card->tst_free_entries++; ++ } ++ } ++ ++ card->scd2vc[(vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE] = NULL; ++ free_scq(vc->scq, vcc); ++ } ++ ++ vcc->dev_data = NULL; ++ vcc->flags &= ~(ATM_VF_PARTIAL | ATM_VF_ADDR); ++ ++#ifdef RX_DEBUG ++ { ++ u32 stat, cfg; ++ stat = readl(card->membase + STAT); ++ cfg = readl(card->membase + CFG); ++ printk("STAT = 0x%08X CFG = 0x%08X \n", stat, cfg); ++ printk("TSQ: base = 0x%08X next = 0x%08X last = 0x%08X TSQT = 0x%08X \n", ++ (u32) card->tsq.base, (u32) card->tsq.next,(u32) card->tsq.last, ++ readl(card->membase + TSQT)); ++ printk("RSQ: base = 0x%08X next = 0x%08X last = 0x%08X RSQT = 0x%08X \n", ++ (u32) card->rsq.base, (u32) card->rsq.next,(u32) card->rsq.last, ++ readl(card->membase + RSQT)); ++ printk("Empty free buffer queue interrupt %s \n", ++ card->efbie ? "enabled" : "disabled"); ++ printk("SBCNT = %d count = %d LBCNT = %d count = %d \n", ++ ns_stat_sfbqc_get(stat), card->sbpool.count, ++ ns_stat_lfbqc_get(stat), card->lbpool.count); ++ printk("hbpool.count = %d iovpool.count = %d \n", ++ card->hbpool.count, card->iovpool.count); ++ } ++#endif /* RX_DEBUG */ ++} ++ ++ ++ ++static void fill_tst(ns_dev *card, int n, vc_map *vc) ++{ ++ u32 new_tst; ++ double c, q; ++ int e, r; ++ u32 data; ++ ++ /* It would be very complicated to keep the two TSTs synchronized while ++ assuring that writes are only made to the inactive TST. So, for now I ++ will use only one TST. If problems occur, I will change this again */ ++ ++ new_tst = card->tst_addr; ++ ++ /* Fill procedure */ ++ ++ for (e = 0; e < NS_TST_NUM_ENTRIES; e++) ++ { ++ if (card->tste2vc[e] == NULL) ++ break; ++ } ++ if (e == NS_TST_NUM_ENTRIES) ++ printk("nicstar%d: No free TST entries found. \n", card->index); ++ ++ r = n; ++ c = 1.0; ++ q = (double) n / (double) NS_TST_NUM_ENTRIES; ++ ++ data = ns_tste_make(NS_TST_OPCODE_FIXED, vc->cbr_scd); ++ ++ while (e < NS_TST_NUM_ENTRIES) ++ { ++ if (c >= 1.0 && card->tste2vc[e] == NULL) ++ { ++ card->tste2vc[e] = vc; ++ ns_write_sram(card, new_tst + e, &data, 1); ++ c -= 1.0; ++ if (--r == 0) ++ break; ++ } ++ ++ e++; ++ c += q; ++ } ++ if (r != 0) ++ printk("nicstar%d: Not enough free TST entries. CBR lower than requested.\n", ++ card->index); ++ ++ /* End of fill procedure */ ++ ++ data = ns_tste_make(NS_TST_OPCODE_END, new_tst); ++ ns_write_sram(card, new_tst + NS_TST_NUM_ENTRIES, &data, 1); ++ ns_write_sram(card, card->tst_addr + NS_TST_NUM_ENTRIES, &data, 1); ++ card->tst_addr = new_tst; ++} ++ ++ ++ ++static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb) ++{ ++ ns_dev *card; ++ vc_map *vc; ++ scq_info *scq; ++ unsigned long buflen; ++ ns_scqe scqe; ++ u32 flags; /* TBD flags, not CPU flags */ ++ ++ card = vcc->dev->dev_data; ++ TXPRINTK("nicstar%d: ns_send() called.\n", card->index); ++ if ((vc = (vc_map *) vcc->dev_data) == NULL) ++ { ++ printk("nicstar%d: vcc->dev_data == NULL on ns_send().\n", card->index); ++ vcc->stats->tx_err++; ++ dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ if (!vc->tx) ++ { ++ printk("nicstar%d: Trying to transmit on a non-tx VC.\n", card->index); ++ vcc->stats->tx_err++; ++ dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) ++ { ++ printk("nicstar%d: Only AAL0 and AAL5 are supported.\n", card->index); ++ vcc->stats->tx_err++; ++ dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ if (skb->atm.iovcnt != 0) ++ { ++ printk("nicstar%d: No scatter-gather yet.\n", card->index); ++ vcc->stats->tx_err++; ++ dev_kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ skb->atm.vcc = vcc; ++ ++ if (vcc->qos.aal == ATM_AAL5) ++ { ++ buflen = (skb->len + 47 + 8) / 48 * 48; /* Multiple of 48 */ ++ flags = NS_TBD_AAL5; ++ scqe.word_2 = (u32) virt_to_bus(skb->data); ++ scqe.word_3 = (u32) skb->len; ++ scqe.word_4 = ((u32) vcc->vpi) << NS_TBD_VPI_SHIFT | ++ ((u32) vcc->vci) << NS_TBD_VCI_SHIFT; ++ flags |= NS_TBD_EOPDU; ++ } ++ else /* (vcc->qos.aal == ATM_AAL0) */ ++ { ++ buflen = ATM_CELL_PAYLOAD; /* i.e., 48 bytes */ ++ flags = NS_TBD_AAL0; ++ scqe.word_2 = (u32) virt_to_bus(skb->data) + NS_AAL0_HEADER; ++ scqe.word_3 = 0x00000000; ++ if (*skb->data & 0x02) /* Payload type 1 - end of pdu */ ++ flags |= NS_TBD_EOPDU; ++ scqe.word_4 = *((u32 *) skb->data) & ~NS_TBD_VC_MASK; ++ /* Force the VPI/VCI to be the same as in VCC struct */ ++ scqe.word_4 |= (((u32) vcc->vpi) << NS_TBD_VPI_SHIFT & ++ ((u32) vcc->vci) << NS_TBD_VCI_SHIFT) & NS_TBD_VC_MASK; ++ } ++ ++ if (vcc->qos.txtp.traffic_class == ATM_CBR) ++ { ++ scqe.word_1 = ns_tbd_mkword_1_novbr(flags, (u32) buflen); ++ scq = ((vc_map *) vcc->dev_data)->scq; ++ } ++ else ++ { ++ scqe.word_1 = ns_tbd_mkword_1(flags, (u32) 1, (u32) 1, (u32) buflen); ++ scq = card->scq0; ++ } ++ ++ if (push_scqe(card, vc, scq, &scqe, skb) != 0) /* Timeout pushing the TBD */ ++ { ++ printk("nicstar%d: Timeout pushing TBD.\n", card->index); ++ vcc->stats->tx_err++; ++ dev_kfree_skb(skb); ++ return -EIO; ++ } ++ vcc->stats->tx++; ++ ++ return 0; ++} ++ ++ ++ ++static int push_scqe(ns_dev *card, vc_map *vc, scq_info *scq, ns_scqe *tbd, ++ struct sk_buff *skb) ++{ ++ unsigned long flags; ++ ns_scqe tsr; ++ u32 scdi, scqi; ++ int scq_is_vbr; ++ u32 data; ++ int index; ++ ++ if (scq->tail == scq->next) ++ { ++ save_flags(flags); cli(); ++ scq->full = 1; ++ current->timeout = jiffies + SCQFULL_TIMEOUT; ++ interruptible_sleep_on(&scq->scqfull_waitq); ++ restore_flags(flags); ++ ++ if (scq->full) ++ return 1; ++ } ++ *scq->next = *tbd; ++ index = (int) (scq->next - scq->base); ++ scq->skb[index] = skb; ++ XPRINTK("nicstar%d: sending skb at 0x%x (pos %d).\n", ++ card->index, (u32) skb, index); ++ XPRINTK("nicstar%d: TBD written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%x.\n", ++ card->index, tbd->word_1, tbd->word_2, tbd->word_3, tbd->word_4, ++ (u32) scq->next); ++ if (scq->next == scq->last) ++ scq->next = scq->base; ++ else ++ scq->next++; ++ ++ vc->tbd_count++; ++ if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) ++ { ++ scq->tbd_count++; ++ scq_is_vbr = 1; ++ } ++ else ++ scq_is_vbr = 0; ++ ++ if (vc->tbd_count >= MAX_TBD_PER_VC || scq->tbd_count >= MAX_TBD_PER_SCQ) ++ { ++ tsr.word_1 = ns_tsr_mkword_1(NS_TSR_INTENABLE); ++ if (scq_is_vbr) ++ scdi = NS_TSR_SCDISVBR; ++ else ++ scdi = (vc->cbr_scd - NS_FRSCD) / NS_FRSCD_SIZE; ++ scqi = scq->next - scq->base; ++ tsr.word_2 = ns_tsr_mkword_2(scdi, scqi); ++ tsr.word_3 = 0x00000000; ++ tsr.word_4 = 0x00000000; ++ ++ if (scq->tail == scq->next) ++ { ++ save_flags(flags); cli(); ++ scq->full = 1; ++ current->timeout = jiffies + SCQFULL_TIMEOUT; ++ interruptible_sleep_on(&scq->scqfull_waitq); ++ restore_flags(flags); ++ } ++ ++ if (!scq->full) ++ { ++ *scq->next = tsr; ++ index = (int) (scq->next - scq->base); ++ scq->skb[index] = NULL; ++ XPRINTK("nicstar%d: TSR written:\n0x%x\n0x%x\n0x%x\n0x%x\n at 0x%x.\n", ++ card->index, tsr.word_1, tsr.word_2, tsr.word_3, tsr.word_4, ++ (u32) scq->next); ++ if (scq->next == scq->last) ++ scq->next = scq->base; ++ else ++ scq->next++; ++ vc->tbd_count = 0; ++ scq->tbd_count = 0; ++ } ++ else ++ PRINTK("nicstar%d: Could not write TSI.\n", card->index); ++ } ++ ++ data = (u32) virt_to_bus(scq->next); ++ ns_write_sram(card, scq->scd, &data, 1); ++ ++ return 0; ++} ++ ++ ++ ++static void process_tsq(ns_dev *card) ++{ ++ u32 scdi; ++ scq_info *scq; ++ ns_tsi *previous; ++ ++ if (ns_tsi_isempty(card->tsq.next)) ++ return; ++ while (!ns_tsi_isempty(card->tsq.next)) ++ { ++ if (!ns_tsi_tmrof(card->tsq.next)) ++ { ++ scdi = ns_tsi_getscdindex(card->tsq.next); ++ if (scdi == NS_TSI_SCDISVBR) ++ scq = card->scq0; ++ else ++ { ++ if (card->scd2vc[scdi] == NULL) ++ { ++ printk("nicstar%d: could not find VC from SCD index.\n", ++ card->index); ++ return; ++ } ++ scq = card->scd2vc[scdi]->scq; ++ } ++ drain_scq(card, scq, ns_tsi_getscqpos(card->tsq.next)); ++ scq->full = 0; ++ wake_up_interruptible(&(scq->scqfull_waitq)); ++ } ++ ++ ns_tsi_init(card->tsq.next); ++ previous = card->tsq.next; ++ if (card->tsq.next == card->tsq.last) ++ card->tsq.next = card->tsq.base; ++ else ++ card->tsq.next++; ++ } ++ writel((((u32) previous) - ((u32) card->tsq.base)), ++ card->membase + TSQH); ++} ++ ++ ++ ++static void drain_scq(ns_dev *card, scq_info *scq, int pos) ++{ ++ struct atm_vcc *vcc; ++ struct sk_buff *skb; ++ int i; ++ ++ XPRINTK("nicstar%d: drain_scq() called, scq at 0x%x, pos %d.\n", ++ card->index, (u32) scq, pos); ++ if (pos >= scq->num_entries) ++ { ++ printk("nicstar%d: Bad index on drain_scq().\n", card->index); ++ return; ++ } ++ ++ i = (int) (scq->tail - scq->base); ++ if (++i == scq->num_entries) ++ i = 0; ++ while (i != pos) ++ { ++ skb = scq->skb[i]; ++ XPRINTK("nicstar%d: freeing skb at 0x%x (index %d).\n", ++ card->index, (u32) skb, i); ++ if (skb != NULL) ++ { ++ vcc = skb->atm.vcc; ++ if (vcc->pop != NULL) ++ vcc->pop(vcc, skb); ++ else ++ dev_kfree_skb(skb); ++ scq->skb[i] = NULL; ++ } ++ if (++i == scq->num_entries) ++ i = 0; ++ } ++ scq->tail = scq->base + pos; ++} ++ ++ ++ ++static void process_rsq(ns_dev *card) ++{ ++ ns_rsqe *previous; ++ ++ if (!ns_rsqe_valid(card->rsq.next)) ++ return; ++ while (ns_rsqe_valid(card->rsq.next)) ++ { ++ dequeue_rx(card, card->rsq.next); ++ ns_rsqe_init(card->rsq.next); ++ previous = card->rsq.next; ++ if (card->rsq.next == card->rsq.last) ++ card->rsq.next = card->rsq.base; ++ else ++ card->rsq.next++; ++ } ++ writel((((u32) previous) - ((u32) card->rsq.base)), ++ card->membase + RSQH); ++} ++ ++ ++ ++static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) ++{ ++ u32 vpi, vci; ++ vc_map *vc; ++ struct sk_buff *iovb; ++ struct iovec *iov; ++ struct atm_vcc *vcc; ++ struct sk_buff *skb; ++ unsigned short aal5_len; ++ int len; ++ u32 stat; ++ ++ stat = readl(card->membase + STAT); ++ card->sbfqc = ns_stat_sfbqc_get(stat); ++ card->lbfqc = ns_stat_lfbqc_get(stat); ++ ++ skb = (struct sk_buff *) rsqe->buffer_handle; ++ vpi = ns_rsqe_vpi(rsqe); ++ vci = ns_rsqe_vci(rsqe); ++ if (vpi >= 1UL << card->vpibits || vci >= 1UL << card->vcibits) ++ { ++ printk("nicstar%d: SDU received for out-of-range vc %d.%d.\n", ++ card->index, vpi, vci); ++ recycle_rx_buf(card, skb); ++ return; ++ } ++ ++ vc = &(card->vcmap[vpi << card->vcibits | vci]); ++ if (!vc->rx) ++ { ++ RXPRINTK("nicstar%d: SDU received on non-rx vc %d.%d.\n", ++ card->index, vpi, vci); ++ recycle_rx_buf(card, skb); ++ return; ++ } ++ ++ vcc = vc->rx_vcc; ++ ++ if (vcc->qos.aal == ATM_AAL0) ++ { ++ struct sk_buff *sb; ++ unsigned char *cell; ++ int i; ++ ++ cell = skb->data; ++ for (i = ns_rsqe_cellcount(rsqe); i; i--) ++ { ++ if ((sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC)) == NULL) ++ { ++ printk("nicstar%d: Can't allocate buffers for aal0.\n", ++ card->index); ++ vcc->stats->rx_drop += i; ++ break; ++ } ++ if (!atm_charge(vcc, sb->truesize)) ++ { ++ RXPRINTK("nicstar%d: atm_charge() dropped aal0 packets.\n", ++ card->index); ++ vcc->stats->rx_drop += i - 1; /* already increased by 1 */ ++ kfree_skb(sb); ++ break; ++ } ++ /* Rebuild the header */ ++ skb_push(sb, NS_AAL0_HEADER); ++ *((u32 *) sb->data) = rsqe->word_1 << 4 | ++ (ns_rsqe_clp(rsqe) ? 0x00000001 : 0x00000000); ++ if (i == 1 && ns_rsqe_eopdu(rsqe)) ++ *((u32 *) sb->data) |= 0x00000002; ++ memcpy(sb->tail, cell, ATM_CELL_PAYLOAD); ++ skb_put(sb, ATM_CELL_PAYLOAD); ++ sb->atm.vcc = vcc; ++ sb->stamp = xtime; ++ vcc->push(vcc, sb); ++ vcc->stats->rx++; ++ cell += ATM_CELL_PAYLOAD; ++ } ++ ++ recycle_rx_buf(card, skb); ++ return; ++ } ++ ++ /* To reach this point, the AAL layer can only be AAL5 */ ++ ++ if ((iovb = vc->rx_iov) == NULL) ++ { ++ iovb = skb_dequeue(&(card->iovpool.queue)); ++ if (iovb == NULL) /* No buffers in the queue */ ++ { ++ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC); ++ if (iovb == NULL) ++ { ++ printk("nicstar%d: Out of iovec buffers.\n", card->index); ++ vcc->stats->rx_drop++; ++ recycle_rx_buf(card, skb); ++ return; ++ } ++ } ++ else ++ if (--card->iovpool.count < card->iovnr.min) ++ { ++ struct sk_buff *new_iovb; ++ if ((new_iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->iovpool.queue, new_iovb); ++ card->iovpool.count++; ++ } ++ } ++ vc->rx_iov = iovb; ++ iovb->atm.iovcnt = 0; ++ iovb->len = 0; ++ iovb->tail = iovb->data = iovb->head; ++ iovb->atm.vcc = vcc; ++ /* IMPORTANT: a pointer to the sk_buff containing the small or large ++ buffer is stored as iovec base, NOT a pointer to the ++ small or large buffer itself. */ ++ } ++ else if (iovb->atm.iovcnt >= NS_MAX_IOVECS) ++ { ++ printk("nicstar%d: received too big AAL5 SDU.\n", card->index); ++ vcc->stats->rx_err++; ++ recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS); ++ iovb->atm.iovcnt = 0; ++ iovb->len = 0; ++ iovb->tail = iovb->data = iovb->head; ++ iovb->atm.vcc = vcc; ++ } ++ iov = &((struct iovec *) iovb->data)[iovb->atm.iovcnt++]; ++ iov->iov_base = (void *) skb; ++ iov->iov_len = ns_rsqe_cellcount(rsqe) * 48; ++ iovb->len += iov->iov_len; ++ ++ if (iovb->atm.iovcnt == 1) ++ { ++ if (skb->list != &card->sbpool.queue) ++ { ++ printk("nicstar%d: Expected a small buffer, and this is not one.\n", ++ card->index); ++ which_list(card, skb); ++ vcc->stats->rx_err++; ++ recycle_rx_buf(card, skb); ++ vc->rx_iov = NULL; ++ recycle_iov_buf(card, iovb); ++ return; ++ } ++ } ++ else /* iovb->atm.iovcnt >= 2 */ ++ { ++ if (skb->list != &card->lbpool.queue) ++ { ++ printk("nicstar%d: Expected a large buffer, and this is not one.\n", ++ card->index); ++ which_list(card, skb); ++ vcc->stats->rx_err++; ++ recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, ++ iovb->atm.iovcnt); ++ vc->rx_iov = NULL; ++ recycle_iov_buf(card, iovb); ++ return; ++ } ++ } ++ ++ if (ns_rsqe_eopdu(rsqe)) ++ { ++ aal5_len = *((unsigned short *) ((u32) skb->data + iov->iov_len - 6)); ++ /* Swap byte order. Is it just me or the nicstar manual sais this should ++ already be in little endian format? */ ++ aal5_len = ((aal5_len & 0x00ff) << 8 | (aal5_len & 0xff00) >> 8); ++ len = (aal5_len == 0x0000) ? 0x10000 : aal5_len; ++ if (ns_rsqe_crcerr(rsqe) || ++ len + 8 > iovb->len || len + (47 + 8) < iovb->len) ++ { ++ if (ns_rsqe_crcerr(rsqe)) ++ printk("nicstar%d: AAL5 CRC error.\n", card->index); ++ else ++ printk("nicstar%d: AAL5 PDU size mismatch.\n", card->index); ++ vcc->stats->rx_err++; ++ recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, iovb->atm.iovcnt); ++ vc->rx_iov = NULL; ++ recycle_iov_buf(card, iovb); ++ return; ++ } ++ ++ /* By this point we (hopefully) have a complete SDU without errors. */ ++ ++ if (iovb->atm.iovcnt == 1) /* Just a small buffer */ ++ { ++ /* skb points to a small buffer */ ++ if (!atm_charge(vcc, skb->truesize)) ++ { ++ push_rxbufs(card, BUF_SM, (u32) skb, (u32) virt_to_bus(skb->data), ++ 0, 0); ++ } ++ else ++ { ++ skb_put(skb, len); ++ dequeue_sm_buf(card, skb); ++#ifdef NS_USE_DESTRUCTORS ++ skb->destructor = ns_sb_destructor; ++#endif /* NS_USE_DESTRUCTORS */ ++ skb->atm.vcc = vcc; ++ skb->stamp = xtime; ++ vcc->push(vcc, skb); ++ vcc->stats->rx++; ++ } ++ } ++ else if (iovb->atm.iovcnt == 2) /* One small plus one large buffer */ ++ { ++ struct sk_buff *sb; ++ ++ sb = (struct sk_buff *) (iov - 1)->iov_base; ++ /* skb points to a large buffer */ ++ ++ if (len <= NS_SMBUFSIZE) ++ { ++ if (!atm_charge(vcc, sb->truesize)) ++ { ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), ++ 0, 0); ++ } ++ else ++ { ++ skb_put(sb, len); ++ dequeue_sm_buf(card, sb); ++#ifdef NS_USE_DESTRUCTORS ++ sb->destructor = ns_sb_destructor; ++#endif /* NS_USE_DESTRUCTORS */ ++ sb->atm.vcc = vcc; ++ sb->stamp = xtime; ++ vcc->push(vcc, sb); ++ vcc->stats->rx++; ++ } ++ ++ push_rxbufs(card, BUF_LG, (u32) skb, ++ (u32) virt_to_bus(skb->data), 0, 0); ++ ++ } ++ else /* len > NS_SMBUFSIZE, the usual case */ ++ { ++ if (!atm_charge(vcc, skb->truesize)) ++ { ++ push_rxbufs(card, BUF_LG, (u32) skb, ++ (u32) virt_to_bus(skb->data), 0, 0); ++ } ++ else ++ { ++ dequeue_lg_buf(card, skb); ++#ifdef NS_USE_DESTRUCTORS ++ skb->destructor = ns_lb_destructor; ++#endif /* NS_USE_DESTRUCTORS */ ++ skb_push(skb, NS_SMBUFSIZE); ++ memcpy(skb->data, sb->data, NS_SMBUFSIZE); ++ skb_put(skb, len - NS_SMBUFSIZE); ++ skb->atm.vcc = vcc; ++ skb->stamp = xtime; ++ vcc->push(vcc, skb); ++ vcc->stats->rx++; ++ } ++ ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), ++ 0, 0); ++ ++ } ++ ++ } ++ else /* Must push a huge buffer */ ++ { ++ struct sk_buff *hb, *sb, *lb; ++ int remaining, tocopy; ++ int j; ++ ++ hb = skb_dequeue(&(card->hbpool.queue)); ++ if (hb == NULL) /* No buffers in the queue */ ++ { ++ ++ hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC); ++ if (hb == NULL) ++ { ++ printk("nicstar%d: Out of huge buffers.\n", card->index); ++ vcc->stats->rx_drop++; ++ recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, ++ iovb->atm.iovcnt); ++ vc->rx_iov = NULL; ++ recycle_iov_buf(card, iovb); ++ return; ++ } ++ else if (card->hbpool.count < card->hbnr.min) ++ { ++ struct sk_buff *new_hb; ++ if ((new_hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->hbpool.queue, new_hb); ++ card->hbpool.count++; ++ } ++ } ++ } ++ else ++ if (--card->hbpool.count < card->hbnr.min) ++ { ++ struct sk_buff *new_hb; ++ if ((new_hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->hbpool.queue, new_hb); ++ card->hbpool.count++; ++ } ++ if (card->hbpool.count < card->hbnr.min) ++ { ++ if ((new_hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->hbpool.queue, new_hb); ++ card->hbpool.count++; ++ } ++ } ++ } ++ ++ iov = (struct iovec *) iovb->data; ++ ++ if (!atm_charge(vcc, hb->truesize)) ++ { ++ recycle_iovec_rx_bufs(card, iov, iovb->atm.iovcnt); ++ if (card->hbpool.count < card->hbnr.max) ++ { ++ skb_queue_tail(&card->hbpool.queue, hb); ++ card->hbpool.count++; ++ } ++ else ++ kfree_skb(hb); ++ } ++ else ++ { ++ /* Copy the small buffer to the huge buffer */ ++ sb = (struct sk_buff *) iov->iov_base; ++ memcpy(hb->data, sb->data, iov->iov_len); ++ skb_put(hb, iov->iov_len); ++ remaining = len - iov->iov_len; ++ iov++; ++ /* Free the small buffer */ ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), ++ 0, 0); ++ ++ /* Copy all large buffers to the huge buffer and free them */ ++ for (j = 1; j < iovb->atm.iovcnt; j++) ++ { ++ lb = (struct sk_buff *) iov->iov_base; ++ tocopy = MIN(remaining, iov->iov_len); ++ memcpy(hb->tail, lb->data, tocopy); ++ skb_put(hb, tocopy); ++ iov++; ++ remaining -= tocopy; ++ push_rxbufs(card, BUF_LG, (u32) lb, ++ (u32) virt_to_bus(lb->data), 0, 0); ++ } ++#ifdef EXTRA_DEBUG ++ if (remaining != 0 || hb->len != len) ++ printk("nicstar%d: Huge buffer len mismatch.\n", card->index); ++#endif /* EXTRA_DEBUG */ ++ hb->atm.vcc = vcc; ++#ifdef NS_USE_DESTRUCTORS ++ hb->destructor = ns_hb_destructor; ++#endif /* NS_USE_DESTRUCTORS */ ++ hb->stamp = xtime; ++ vcc->push(vcc, hb); ++ vcc->stats->rx++; ++ } ++ } ++ ++ vc->rx_iov = NULL; ++ recycle_iov_buf(card, iovb); ++ } ++ ++} ++ ++ ++ ++#ifdef NS_USE_DESTRUCTORS ++ ++static void ns_sb_destructor(struct sk_buff *sb) ++{ ++ ns_dev *card; ++ u32 stat; ++ ++ card = (ns_dev *) sb->atm.vcc->dev->dev_data; ++ stat = readl(card->membase + STAT); ++ card->sbfqc = ns_stat_sfbqc_get(stat); ++ card->lbfqc = ns_stat_lfbqc_get(stat); ++ ++ do ++ { ++ sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); ++ if (sb == NULL) ++ break; ++ skb_queue_tail(&card->sbpool.queue, sb); ++ skb_reserve(sb, NS_AAL0_HEADER); ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0); ++ } while (card->sbfqc < card->sbnr.min); ++} ++ ++ ++ ++static void ns_lb_destructor(struct sk_buff *lb) ++{ ++ ns_dev *card; ++ u32 stat; ++ ++ card = (ns_dev *) lb->atm.vcc->dev->dev_data; ++ stat = readl(card->membase + STAT); ++ card->sbfqc = ns_stat_sfbqc_get(stat); ++ card->lbfqc = ns_stat_lfbqc_get(stat); ++ ++ do ++ { ++ lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); ++ if (lb == NULL) ++ break; ++ skb_queue_tail(&card->lbpool.queue, lb); ++ skb_reserve(lb, NS_SMBUFSIZE); ++ push_rxbufs(card, BUF_LG, (u32) lb, (u32) virt_to_bus(lb->data), 0, 0); ++ } while (card->lbfqc < card->lbnr.min); ++} ++ ++ ++ ++static void ns_hb_destructor(struct sk_buff *hb) ++{ ++ ns_dev *card; ++ ++ card = (ns_dev *) hb->atm.vcc->dev->dev_data; ++ ++ while (card->hbpool.count < card->hbnr.init) ++ { ++ hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); ++ if (hb == NULL) ++ break; ++ skb_queue_tail(&card->hbpool.queue, hb); ++ card->hbpool.count++; ++ } ++} ++ ++#endif /* NS_USE_DESTRUCTORS */ ++ ++ ++ ++static void recycle_rx_buf(ns_dev *card, struct sk_buff *skb) ++{ ++ if (skb->list == &card->sbpool.queue) ++ push_rxbufs(card, BUF_SM, (u32) skb, (u32) virt_to_bus(skb->data), 0, 0); ++ else if (skb->list == &card->lbpool.queue) ++ push_rxbufs(card, BUF_LG, (u32) skb, (u32) virt_to_bus(skb->data), 0, 0); ++ else ++ { ++ printk("nicstar%d: What kind of rx buffer is this?\n", card->index); ++ kfree_skb(skb); ++ } ++} ++ ++ ++ ++static void recycle_iovec_rx_bufs(ns_dev *card, struct iovec *iov, int count) ++{ ++ struct sk_buff *skb; ++ ++ for (; count > 0; count--) ++ { ++ skb = (struct sk_buff *) (iov++)->iov_base; ++ if (skb->list == &card->sbpool.queue) ++ push_rxbufs(card, BUF_SM, (u32) skb, (u32) virt_to_bus(skb->data), ++ 0, 0); ++ else if (skb->list == &card->lbpool.queue) ++ push_rxbufs(card, BUF_LG, (u32) skb, (u32) virt_to_bus(skb->data), ++ 0, 0); ++ else ++ { ++ printk("nicstar%d: What kind of rx buffer is this?\n", card->index); ++ kfree_skb(skb); ++ } ++ } ++} ++ ++ ++ ++static void recycle_iov_buf(ns_dev *card, struct sk_buff *iovb) ++{ ++ if (card->iovpool.count < card->iovnr.max) ++ { ++ skb_queue_tail(&card->iovpool.queue, iovb); ++ card->iovpool.count++; ++ } ++ else ++ kfree_skb(iovb); ++} ++ ++ ++ ++static void dequeue_sm_buf(ns_dev *card, struct sk_buff *sb) ++{ ++ skb_unlink(sb); ++#ifdef NS_USE_DESTRUCTORS ++ if (card->sbfqc < card->sbnr.min) ++#else ++ if (card->sbfqc < card->sbnr.init) ++ { ++ struct sk_buff *new_sb; ++ if ((new_sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->sbpool.queue, new_sb); ++ skb_reserve(new_sb, NS_AAL0_HEADER); ++ push_rxbufs(card, BUF_SM, (u32) new_sb, ++ (u32) virt_to_bus(new_sb->data), 0, 0); ++ } ++ } ++ if (card->sbfqc < card->sbnr.init) ++#endif /* NS_USE_DESTRUCTORS */ ++ { ++ struct sk_buff *new_sb; ++ if ((new_sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->sbpool.queue, new_sb); ++ skb_reserve(new_sb, NS_AAL0_HEADER); ++ push_rxbufs(card, BUF_SM, (u32) new_sb, ++ (u32) virt_to_bus(new_sb->data), 0, 0); ++ } ++ } ++} ++ ++ ++ ++static void dequeue_lg_buf(ns_dev *card, struct sk_buff *lb) ++{ ++ skb_unlink(lb); ++#ifdef NS_USE_DESTRUCTORS ++ if (card->lbfqc < card->lbnr.min) ++#else ++ if (card->lbfqc < card->lbnr.init) ++ { ++ struct sk_buff *new_lb; ++ if ((new_lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->lbpool.queue, new_lb); ++ skb_reserve(new_lb, NS_SMBUFSIZE); ++ push_rxbufs(card, BUF_LG, (u32) new_lb, ++ (u32) virt_to_bus(new_lb->data), 0, 0); ++ } ++ } ++ if (card->lbfqc < card->lbnr.init) ++#endif /* NS_USE_DESTRUCTORS */ ++ { ++ struct sk_buff *new_lb; ++ if ((new_lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC)) != NULL) ++ { ++ skb_queue_tail(&card->lbpool.queue, new_lb); ++ skb_reserve(new_lb, NS_SMBUFSIZE); ++ push_rxbufs(card, BUF_LG, (u32) new_lb, ++ (u32) virt_to_bus(new_lb->data), 0, 0); ++ } ++ } ++} ++ ++ ++ ++static int ns_proc_read(struct atm_dev *dev, loff_t *pos, char *page) ++{ ++ u32 stat; ++ ns_dev *card; ++ int left; ++ ++ left = (int) *pos; ++ card = (ns_dev *) dev->dev_data; ++ stat = readl(card->membase + STAT); ++ if (!left--) ++ return sprintf(page, "Pool count min init max \n"); ++ if (!left--) ++ return sprintf(page, "Small %5d %5d %5d %5d \n", ++ ns_stat_sfbqc_get(stat), card->sbnr.min, card->sbnr.init, ++ card->sbnr.max); ++ if (!left--) ++ return sprintf(page, "Large %5d %5d %5d %5d \n", ++ ns_stat_lfbqc_get(stat), card->lbnr.min, card->lbnr.init, ++ card->lbnr.max); ++ if (!left--) ++ return sprintf(page, "Huge %5d %5d %5d %5d \n", card->hbpool.count, ++ card->hbnr.min, card->hbnr.init, card->hbnr.max); ++ if (!left--) ++ return sprintf(page, "Iovec %5d %5d %5d %5d \n", card->iovpool.count, ++ card->iovnr.min, card->iovnr.init, card->iovnr.max); ++ /* Dump 25.6 Mbps PHY registers */ ++ if (card->max_pcr == IDT_25_PCR && !left--) ++ { ++ u32 phy_regs[4]; ++ u32 i; ++ ++ for (i = 0; i < 4; i++) ++ { ++ while (CMD_BUSY(card)); ++ writel(NS_CMD_READ_UTILITY | 0x00000200 | i, card->membase + CMD); ++ while (CMD_BUSY(card)); ++ phy_regs[i] = readl(card->membase + DR0) & 0x000000FF; ++ } ++ ++ return sprintf(page, "PHY regs: 0x%02X 0x%02X 0x%02X 0x%02X \n", ++ phy_regs[0], phy_regs[1], phy_regs[2], phy_regs[3]); ++ } ++#if 0 ++ /* Dump TST */ ++ if (left-- < NS_TST_NUM_ENTRIES) ++ { ++ if (card->tste2vc[left + 1] == NULL) ++ return sprintf(page, "%5d - VBR/UBR \n", left + 1); ++ else ++ return sprintf(page, "%5d - %d %d \n", left + 1, ++ card->tste2vc[left + 1]->tx_vcc->vpi, ++ card->tste2vc[left + 1]->tx_vcc->vci); ++ } ++#endif /* 0 */ ++ return 0; ++} ++ ++ ++ ++static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg) ++{ ++ ns_dev *card; ++ pool_levels pl; ++ int btype; ++ unsigned long flags; ++ ++ card = dev->dev_data; ++ switch (cmd) ++ { ++ case NS_GETPSTAT: ++ if (get_user(pl.buftype, &((pool_levels *) arg)->buftype)) ++ return -EFAULT; ++ switch (pl.buftype) ++ { ++ case NS_BUFTYPE_SMALL: ++ pl.count = ns_stat_sfbqc_get(readl(card->membase + STAT)); ++ pl.level.min = card->sbnr.min; ++ pl.level.init = card->sbnr.init; ++ pl.level.max = card->sbnr.max; ++ break; ++ ++ case NS_BUFTYPE_LARGE: ++ pl.count = ns_stat_lfbqc_get(readl(card->membase + STAT)); ++ pl.level.min = card->lbnr.min; ++ pl.level.init = card->lbnr.init; ++ pl.level.max = card->lbnr.max; ++ break; ++ ++ case NS_BUFTYPE_HUGE: ++ pl.count = card->hbpool.count; ++ pl.level.min = card->hbnr.min; ++ pl.level.init = card->hbnr.init; ++ pl.level.max = card->hbnr.max; ++ break; ++ ++ case NS_BUFTYPE_IOVEC: ++ pl.count = card->iovpool.count; ++ pl.level.min = card->iovnr.min; ++ pl.level.init = card->iovnr.init; ++ pl.level.max = card->iovnr.max; ++ break; ++ ++ default: ++ return -EINVAL; ++ ++ } ++ if (!copy_to_user((pool_levels *) arg, &pl, sizeof(pl))) ++ return (sizeof(pl)); ++ else ++ return -EFAULT; ++ ++ case NS_SETBUFLEV: ++ if (!suser()) ++ return -EPERM; ++ if (copy_from_user(&pl, (pool_levels *) arg, sizeof(pl))) ++ return -EFAULT; ++ if (pl.level.min >= pl.level.init || pl.level.init >= pl.level.max) ++ return -EINVAL; ++ if (pl.level.min == 0) ++ return -EINVAL; ++ switch (pl.buftype) ++ { ++ case NS_BUFTYPE_SMALL: ++ if (pl.level.max > TOP_SB) ++ return -EINVAL; ++ card->sbnr.min = pl.level.min; ++ card->sbnr.init = pl.level.init; ++ card->sbnr.max = pl.level.max; ++ break; ++ ++ case NS_BUFTYPE_LARGE: ++ if (pl.level.max > TOP_LB) ++ return -EINVAL; ++ card->lbnr.min = pl.level.min; ++ card->lbnr.init = pl.level.init; ++ card->lbnr.max = pl.level.max; ++ break; ++ ++ case NS_BUFTYPE_HUGE: ++ if (pl.level.max > TOP_HB) ++ return -EINVAL; ++ card->hbnr.min = pl.level.min; ++ card->hbnr.init = pl.level.init; ++ card->hbnr.max = pl.level.max; ++ break; ++ ++ case NS_BUFTYPE_IOVEC: ++ if (pl.level.max > TOP_IOVB) ++ return -EINVAL; ++ card->iovnr.min = pl.level.min; ++ card->iovnr.init = pl.level.init; ++ card->iovnr.max = pl.level.max; ++ break; ++ ++ default: ++ return -EINVAL; ++ ++ } ++ return 0; ++ ++ case NS_ADJBUFLEV: ++ if (!suser()) ++ return -EPERM; ++ btype = (int) arg; /* an int is the same size as a pointer */ ++ switch (btype) ++ { ++ case NS_BUFTYPE_SMALL: ++ while (card->sbfqc < card->sbnr.init) ++ { ++ struct sk_buff *sb; ++ ++ sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); ++ if (sb == NULL) ++ return -ENOMEM; ++ skb_queue_tail(&card->sbpool.queue, sb); ++ skb_reserve(sb, NS_AAL0_HEADER); ++ push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0); ++ } ++ break; ++ ++ case NS_BUFTYPE_LARGE: ++ while (card->lbfqc < card->lbnr.init) ++ { ++ struct sk_buff *lb; ++ ++ lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); ++ if (lb == NULL) ++ return -ENOMEM; ++ skb_queue_tail(&card->lbpool.queue, lb); ++ skb_reserve(lb, NS_SMBUFSIZE); ++ push_rxbufs(card, BUF_LG, (u32) lb, (u32) virt_to_bus(lb->data), 0, 0); ++ } ++ break; ++ ++ case NS_BUFTYPE_HUGE: ++ while (card->hbpool.count > card->hbnr.init) ++ { ++ struct sk_buff *hb; ++ ++ save_flags(flags); cli(); ++ hb = skb_dequeue(&card->hbpool.queue); ++ card->hbpool.count--; ++ restore_flags(flags); ++ if (hb == NULL) ++ printk("nicstar%d: huge buffer count inconsistent.\n", ++ card->index); ++ else ++ kfree_skb(hb); ++ ++ } ++ while (card->hbpool.count < card->hbnr.init) ++ { ++ struct sk_buff *hb; ++ ++ hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); ++ if (hb == NULL) ++ return -ENOMEM; ++ save_flags(flags); cli(); ++ skb_queue_tail(&card->hbpool.queue, hb); ++ card->hbpool.count++; ++ restore_flags(flags); ++ } ++ break; ++ ++ case NS_BUFTYPE_IOVEC: ++ while (card->iovpool.count > card->iovnr.init) ++ { ++ struct sk_buff *iovb; ++ ++ save_flags(flags); cli(); ++ iovb = skb_dequeue(&card->iovpool.queue); ++ card->iovpool.count--; ++ restore_flags(flags); ++ if (iovb == NULL) ++ printk("nicstar%d: iovec buffer count inconsistent.\n", ++ card->index); ++ else ++ kfree_skb(iovb); ++ ++ } ++ while (card->iovpool.count < card->iovnr.init) ++ { ++ struct sk_buff *iovb; ++ ++ iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL); ++ if (iovb == NULL) ++ return -ENOMEM; ++ save_flags(flags); cli(); ++ skb_queue_tail(&card->iovpool.queue, iovb); ++ card->iovpool.count++; ++ restore_flags(flags); ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ ++ } ++ return 0; ++ ++ default: ++ if (dev->phy->ioctl == NULL) return -EINVAL; ++ return dev->phy->ioctl(dev, cmd, arg); ++ } ++} ++ ++ ++ ++static void which_list(ns_dev *card, struct sk_buff *skb) ++{ ++ printk("It's a %s buffer.\n", skb->list == &card->sbpool.queue ? ++ "small" : skb->list == &card->lbpool.queue ? "large" : ++ skb->list == &card->hbpool.queue ? "huge" : ++ skb->list == &card->iovpool.queue ? "iovec" : "unknown"); ++} +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/nicstarmac.h Fri Aug 21 02:32:42 1998 +@@ -0,0 +1,14 @@ ++/****************************************************************************** ++ * ++ * nicstarmac.h ++ * ++ * Header file for nicstarmac.c ++ * ++ ******************************************************************************/ ++ ++ ++typedef unsigned int virt_addr_t; ++ ++u_int32_t nicstar_read_eprom_status( virt_addr_t base ); ++void nicstar_init_eprom( virt_addr_t base ); ++void nicstar_read_eprom( virt_addr_t, u_int8_t, u_int8_t *, u_int32_t); +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/nicstarmac.c Fri Aug 21 02:32:42 1998 +@@ -0,0 +1,269 @@ ++/* ++ * this file included by nicstar.c ++ */ ++ ++/* ++ * nicstarmac.c ++ * Read this ForeRunner's MAC address from eprom/eeprom ++ */ ++ ++#define CYCLE_DELAY 5 ++ ++/* This was the original definition ++#define osp_MicroDelay(microsec) \ ++ do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) ++*/ ++#define osp_MicroDelay(microsec) {unsigned long useconds = (microsec); \ ++ udelay((useconds));} ++ ++ ++/* The following tables represent the timing diagrams found in ++ * the Data Sheet for the Xicor X25020 EEProm. The #defines below ++ * represent the bits in the NICStAR's General Purpose register ++ * that must be toggled for the corresponding actions on the EEProm ++ * to occur. ++ */ ++ ++/* Write Data To EEProm from SI line on rising edge of CLK */ ++/* Read Data From EEProm on falling edge of CLK */ ++ ++#define CS_HIGH 0x0002 /* Chip select high */ ++#define CS_LOW 0x0000 /* Chip select low (active low)*/ ++#define CLK_HIGH 0x0004 /* Clock high */ ++#define CLK_LOW 0x0000 /* Clock low */ ++#define SI_HIGH 0x0001 /* Serial input data high */ ++#define SI_LOW 0x0000 /* Serial input data low */ ++ ++/* Read Status Register = 0000 0101b */ ++static u_int32_t rdsrtab[] = ++{ ++ CS_HIGH | CLK_HIGH, ++ CS_LOW | CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW | SI_HIGH, ++ CLK_HIGH | SI_HIGH, /* 1 */ ++ CLK_LOW | SI_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW | SI_HIGH, ++ CLK_HIGH | SI_HIGH /* 1 */ ++}; ++ ++ ++/* Read from EEPROM = 0000 0011b */ ++static u_int32_t readtab[] = ++{ ++ /* ++ CS_HIGH | CLK_HIGH, ++ */ ++ CS_LOW | CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW, ++ CLK_HIGH, /* 0 */ ++ CLK_LOW | SI_HIGH, ++ CLK_HIGH | SI_HIGH, /* 1 */ ++ CLK_LOW | SI_HIGH, ++ CLK_HIGH | SI_HIGH /* 1 */ ++}; ++ ++ ++/* Clock to read from/write to the eeprom */ ++static u_int32_t clocktab[] = ++{ ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW, ++ CLK_HIGH, ++ CLK_LOW ++}; ++ ++ ++#define NICSTAR_REG_WRITE(bs, reg, val) \ ++ while ( readl(bs + STAT) & 0x0200 ) ; \ ++ writel((val),(base)+(reg)) ++#define NICSTAR_REG_READ(bs, reg) \ ++ readl((base)+(reg)) ++#define NICSTAR_REG_GENERAL_PURPOSE GP ++ ++/* ++ * This routine will clock the Read_Status_reg function into the X2520 ++ * eeprom, then pull the result from bit 16 of the NicSTaR's General Purpose ++ * register. ++ */ ++ ++u_int32_t ++nicstar_read_eprom_status( virt_addr_t base ) ++{ ++ u_int32_t val; ++ u_int32_t rbyte; ++ int32_t i, j; ++ ++ /* Send read instruction */ ++ val = NICSTAR_REG_READ( base, NICSTAR_REG_GENERAL_PURPOSE ) & 0xFFFFFFF0; ++ ++ for (i=0; i=0; i--) ++ { ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | clocktab[j++]) ); ++ rbyte |= (((NICSTAR_REG_READ( base, NICSTAR_REG_GENERAL_PURPOSE) ++ & 0x00010000) >> 16) << i); ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | clocktab[j++]) ); ++ osp_MicroDelay( CYCLE_DELAY ); ++ } ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, 2 ); ++ osp_MicroDelay( CYCLE_DELAY ); ++ return rbyte; ++} ++ ++ ++/* ++ * This routine will clock the Read_data function into the X2520 ++ * eeprom, followed by the address to read from, through the NicSTaR's General ++ * Purpose register. ++ */ ++ ++static u_int8_t ++read_eprom_byte(u_int32_t base, u_int8_t offset) ++{ ++ u_int32_t val = 0; ++ int i,j=0; ++ u_int8_t tempread = 0; ++ ++ val = NICSTAR_REG_READ( base, NICSTAR_REG_GENERAL_PURPOSE ) & 0xFFFFFFF0; ++ ++ /* Send READ instruction */ ++ for (i=0; i=0; i--) ++ { ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | clocktab[j++] | ((offset >> i) & 1) ) ); ++ osp_MicroDelay(CYCLE_DELAY); ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | clocktab[j++] | ((offset >> i) & 1) ) ); ++ osp_MicroDelay( CYCLE_DELAY ); ++ } ++ ++ j = 0; ++ ++ /* Now, we can read data from the eeprom by clocking it in */ ++ for (i=7; i>=0; i--) ++ { ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | clocktab[j++]) ); ++ osp_MicroDelay( CYCLE_DELAY ); ++ tempread |= (((NICSTAR_REG_READ( base, NICSTAR_REG_GENERAL_PURPOSE ) ++ & 0x00010000) >> 16) << i); ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | clocktab[j++]) ); ++ osp_MicroDelay( CYCLE_DELAY ); ++ } ++ ++ NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, 2 ); ++ osp_MicroDelay( CYCLE_DELAY ); ++ return tempread; ++} ++ ++ ++void ++nicstar_init_eprom( virt_addr_t base ) ++{ ++ u_int32_t val; ++ ++ /* ++ * turn chip select off ++ */ ++ val = NICSTAR_REG_READ(base, NICSTAR_REG_GENERAL_PURPOSE) & 0xFFFFFFF0; ++ ++ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | CS_HIGH | CLK_HIGH)); ++ osp_MicroDelay( CYCLE_DELAY ); ++ ++ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | CS_HIGH | CLK_LOW)); ++ osp_MicroDelay( CYCLE_DELAY ); ++ ++ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | CS_HIGH | CLK_HIGH)); ++ osp_MicroDelay( CYCLE_DELAY ); ++ ++ NICSTAR_REG_WRITE(base, NICSTAR_REG_GENERAL_PURPOSE, ++ (val | CS_HIGH | CLK_LOW)); ++ osp_MicroDelay( CYCLE_DELAY ); ++} ++ ++ ++/* ++ * This routine will be the interface to the ReadPromByte function ++ * above. ++ */ ++ ++void ++nicstar_read_eprom( ++ virt_addr_t base, ++ u_int8_t prom_offset, ++ u_int8_t *buffer, ++ u_int32_t nbytes ) ++{ ++ u_int i; ++ ++ for (i=0; i, Aug. 6, 1997 $Revision: 1.3 $ $Date: 1997/08/07 18:23:14 $ ++ * ++ * Linux driver for the IDT77201 NICStAR PCI ATM controller. ++ * PHY component is expected to be 155 Mbps S/UNI-Lite or IDT 77155; ++ * see init_nicstar() for PHY initialization to change this. This driver ++ * expects the Linux ATM stack to support scatter-gather lists ++ * (skb->atm.iovcnt != 0) for Rx skb's passed to vcc->push. ++ * ++ * Implementing minimal-copy of received data: ++ * IDT always receives data into a small buffer, then large buffers ++ * as needed. This means that data must always be copied to create ++ * the linear buffer needed by most non-ATM protocol stacks (e.g. IP) ++ * Fix is simple: make large buffers large enough to hold entire ++ * SDU, and leave bytes empty at the start. Then ++ * copy small buffer contents to head of large buffer. ++ * Trick is to avoid fragmenting Linux, due to need for a lot of large ++ * buffers. This is done by 2 things: ++ * 1) skb->destructor / skb->atm.recycle_buffer ++ * combined, allow nicstar_free_rx_skb to be called to ++ * recycle large data buffers ++ * 2) skb_clone of received buffers ++ * See nicstar_free_rx_skb and linearize_buffer for implementation ++ * details. ++ * ++ * ++ * ++ * Copyright (c) 1996 University of Cambridge Computer Laboratory ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * M. Welsh, 6 July 1996 ++ * ++ * ++ */ +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/drivers/atm/suni.c Fri Aug 21 00:52:28 1998 +@@ -0,0 +1,303 @@ +/* drivers/atm/suni.c - PMC SUNI (PHY) driver */ + +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ @@ -3411,14 +7313,6 @@ + suni_hz(0); /* clear SUNI counters */ + (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ + cli(); -+{ -+int i; -+printk("SUNI:\n"); -+for (i = 0; i < 256; i++) { -+ printk(" %02x",dev->ops->phy_get(dev,i)); -+ if ((i & 15) == 15) printk("\n"); -+} -+} + if (!start_timer) restore_flags(flags); + else { + start_timer = 0; @@ -3480,7 +7374,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/suni.h Wed Aug 19 14:31:00 1998 ++++ work/drivers/atm/suni.h Fri Aug 21 01:09:01 1998 @@ -0,0 +1,210 @@ +/* drivers/atm/suni.h - PMC SUNI (PHY) declarations */ + @@ -3693,7 +7587,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/tonga.h Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/tonga.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,20 @@ +/* drivers/atm/tonga.h - Efficient Networks Tonga (PCI bridge) declarations */ + @@ -3716,7 +7610,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/uPD98401.h Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/uPD98401.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,292 @@ +/* drivers/atm/uPD98401.h - NEC uPD98401 (SAR) declarations */ + @@ -4011,7 +7905,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/uPD98402.c Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/uPD98402.c Fri Aug 21 00:52:28 1998 @@ -0,0 +1,224 @@ +/* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */ + @@ -4238,7 +8132,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/uPD98402.h Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/uPD98402.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,106 @@ +/* drivers/atm/uPD98402.h - NEC uPD98402 (PHY) declarations */ + @@ -4347,7 +8241,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/zatm.c Tue Aug 18 13:43:03 1998 ++++ work/drivers/atm/zatm.c Fri Aug 21 00:52:28 1998 @@ -0,0 +1,1870 @@ +/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */ + @@ -6220,7 +10114,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/zatm.h Wed Aug 19 14:31:06 1998 ++++ work/drivers/atm/zatm.h Fri Aug 21 01:09:07 1998 @@ -0,0 +1,136 @@ +/* drivers/atm/zatm.h - ZeitNet ZN122x device driver declarations */ + @@ -6359,7 +10253,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/drivers/atm/zeprom.h Tue Jun 9 21:32:11 1998 ++++ work/drivers/atm/zeprom.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,34 @@ +/* drivers/atm/zeprom.h - ZeitNet ZN122x EEPROM (NM93C46) declarations */ + @@ -6395,8 +10289,8 @@ +/* No other commands are needed. */ + +#endif ---- ref/drivers/block/genhd.c Fri Feb 6 19:29:22 1998 -+++ work/drivers/block/genhd.c Tue Jun 9 21:32:11 1998 +--- ref/drivers/block/genhd.c Sun Aug 16 20:48:40 1998 ++++ work/drivers/block/genhd.c Fri Aug 21 00:52:28 1998 @@ -58,6 +58,7 @@ extern int blk_dev_init(void); extern int scsi_dev_init(void); @@ -6405,7 +10299,7 @@ /* * disk_name() is used by genhd.c and md.c. -@@ -1111,6 +1112,9 @@ +@@ -1124,6 +1125,9 @@ #endif #ifdef CONFIG_INET net_dev_init(); @@ -6416,7 +10310,7 @@ #ifdef CONFIG_VT console_map_init(); --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/arequipa.h Wed Aug 19 14:31:31 1998 ++++ work/include/linux/arequipa.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,63 @@ +/* arequipa.h - Arequipa interface definitions */ + @@ -6482,7 +10376,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atm.h Tue Aug 18 14:51:43 1998 ++++ work/include/linux/atm.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,239 @@ +/* atm.h - general ATM declarations */ + @@ -6724,7 +10618,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atm_eni.h Tue Aug 11 19:45:45 1998 ++++ work/include/linux/atm_eni.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,15 @@ +/* atm_eni.h - Driver-specific declarations of the ENI driver (for use by + driver-specific utilities) */ @@ -6742,7 +10636,62 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atm_suni.h Wed Aug 19 14:59:10 1998 ++++ work/include/linux/atm_nicstar.h Fri Aug 21 02:32:42 1998 +@@ -0,0 +1,52 @@ ++/****************************************************************************** ++ * ++ * atm_nicstar.h ++ * ++ * Driver-specific declarations for use by NICSTAR driver specific utils. ++ * ++ * Author: Rui Prior ++ * ++ * (C) INESC 1998 ++ * ++ ******************************************************************************/ ++ ++ ++#ifndef LINUX_ATM_NICSTAR_H ++#define LINUX_ATM_NICSTAR_H ++ ++/* Note: non-kernel programs including this file must also include ++ * sys/types.h for struct timeval ++ */ ++ ++#include ++ ++#define NS_GETPSTAT _IOWR('a',ATMIOC_SARPRV+1,struct atmif_sioc) ++ /* get pool statistics */ ++#define NS_SETBUFLEV _IOW('a',ATMIOC_SARPRV+2,struct atmif_sioc) ++ /* set buffer level markers */ ++#define NS_ADJBUFLEV _IO('a',ATMIOC_SARPRV+3) ++ /* adjust buffer level */ ++ ++typedef struct buf_nr ++{ ++ unsigned min; ++ unsigned init; ++ unsigned max; ++} buf_nr; ++ ++ ++typedef struct pool_levels ++{ ++ int buftype; ++ int count; /* (At least for now) only used in NS_GETPSTAT */ ++ buf_nr level; ++} pool_levels; ++ ++/* type must be one of the following: */ ++#define NS_BUFTYPE_SMALL 1 ++#define NS_BUFTYPE_LARGE 2 ++#define NS_BUFTYPE_HUGE 3 ++#define NS_BUFTYPE_IOVEC 4 ++ ++ ++#endif /* LINUX_ATM_NICSTAR_H */ +--- /dev/null Tue Jan 1 05:00:00 1980 ++++ work/include/linux/atm_suni.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,19 @@ +/* atm_suni.h - Driver-specific declarations of the SUNI driver (for use by + driver-specific utilities) */ @@ -6764,7 +10713,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atm_tcp.h Tue Aug 18 13:21:08 1998 ++++ work/include/linux/atm_tcp.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,46 @@ +/* atm_tcp.h - Driver-specific declarations of the ATMTCP driver (for use by + driver-specific utilities) */ @@ -6813,7 +10762,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atm_zatm.h Tue Aug 11 19:45:52 1998 ++++ work/include/linux/atm_zatm.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,54 @@ +/* atm_zatm.h - Driver-specific declarations of the ZATM driver (for use by + driver-specific utilities) */ @@ -6870,7 +10819,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmarp.h Tue Aug 11 19:46:16 1998 ++++ work/include/linux/atmarp.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,42 @@ +/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */ + @@ -6915,7 +10864,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmclip.h Tue Aug 11 22:56:11 1998 ++++ work/include/linux/atmclip.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,25 @@ +/* atmclip.h - Classical IP over ATM */ + @@ -6943,7 +10892,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmdev.h Wed Aug 19 14:31:00 1998 ++++ work/include/linux/atmdev.h Fri Aug 21 01:09:01 1998 @@ -0,0 +1,309 @@ +/* atmdev.h - ATM device driver declarations */ + @@ -7255,7 +11204,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmioc.h Tue Aug 11 19:17:07 1998 ++++ work/include/linux/atmioc.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,39 @@ +/* atmioc.h - ranges for ATM-related ioctl numbers */ + @@ -7297,7 +11246,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmlec.h Wed Aug 19 14:31:31 1998 ++++ work/include/linux/atmlec.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,70 @@ +/* + * @@ -7370,7 +11319,7 @@ +}; +#endif /* _ATMLEC_H_ */ --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmmpc.h Wed Aug 19 18:48:36 1998 ++++ work/include/linux/atmmpc.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,98 @@ +#ifndef _ATMMPC_H_ +#define _ATMMPC_H_ @@ -7471,7 +11420,7 @@ +#endif /* _ATMMPC_H_ */ + --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmsap.h Tue Jun 9 21:32:11 1998 ++++ work/include/linux/atmsap.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,161 @@ +/* atmsap.h - ATM Service Access Point addressing definitions */ + @@ -7635,7 +11584,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/atmsvc.h Wed Aug 19 14:31:31 1998 ++++ work/include/linux/atmsvc.h Fri Aug 21 00:52:28 1998 @@ -0,0 +1,52 @@ +/* atmsvc.h - ATM signaling kernel-demon interface definitions */ + @@ -7689,8 +11638,8 @@ + (tp).max_pcr : (tp).min_pcr ? (tp).min_pcr : ATM_MAX_PCR) + +#endif ---- ref/include/linux/capability.h Sun Jun 7 20:22:35 1998 -+++ work/include/linux/capability.h Tue Aug 18 13:49:56 1998 +--- ref/include/linux/capability.h Thu Aug 20 01:38:43 1998 ++++ work/include/linux/capability.h Fri Aug 21 00:55:14 1998 @@ -121,6 +121,7 @@ #define CAP_LINUX_IMMUTABLE 9 @@ -7707,8 +11656,8 @@ #define CAP_NET_ADMIN 12 ---- ref/include/linux/if_arp.h Sun Jun 7 20:23:23 1998 -+++ work/include/linux/if_arp.h Tue Aug 18 14:07:42 1998 +--- ref/include/linux/if_arp.h Thu Aug 20 01:38:57 1998 ++++ work/include/linux/if_arp.h Fri Aug 21 01:07:19 1998 @@ -35,6 +35,7 @@ #define ARPHRD_ARCNET 7 /* ARCnet */ #define ARPHRD_APPLETLK 8 /* APPLEtalk */ @@ -7728,7 +11677,7 @@ /* ARP ioctl request. */ --- ref/include/linux/pkt_sched.h Tue Apr 28 20:10:10 1998 -+++ work/include/linux/pkt_sched.h Wed Aug 12 22:04:43 1998 ++++ work/include/linux/pkt_sched.h Fri Aug 21 00:52:29 1998 @@ -274,4 +274,17 @@ #define TCA_CBQ_MAX TCA_CBQ_POLICE @@ -7747,8 +11696,8 @@ +#define TCA_ATM_MAX TCA_ATM_ADDR + #endif ---- ref/include/linux/skbuff.h Sun Jun 7 20:22:36 1998 -+++ work/include/linux/skbuff.h Tue Aug 18 14:06:31 1998 +--- ref/include/linux/skbuff.h Thu Aug 20 01:38:56 1998 ++++ work/include/linux/skbuff.h Fri Aug 21 01:06:09 1998 @@ -106,6 +106,16 @@ __u32 shapestamp; /* Stamp for shaper */ __u16 shapepend; /* Pending */ @@ -7767,7 +11716,7 @@ /* These are just the default values. This is run time configurable. --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/linux/sonet.h Tue Jun 9 21:32:12 1998 ++++ work/include/linux/sonet.h Fri Aug 21 00:52:29 1998 @@ -0,0 +1,52 @@ +/* sonet.h - SONET/SHD physical layer control */ + @@ -7822,7 +11771,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/include/net/atmclip.h Wed Aug 19 14:31:25 1998 ++++ work/include/net/atmclip.h Fri Aug 21 01:12:51 1998 @@ -0,0 +1,62 @@ +/* net/atm/atmarp.h - RFC1577 ATM ARP */ + @@ -7886,8 +11835,8 @@ +int clip_encap(struct atm_vcc *vcc,int mode); + +#endif ---- ref/net/Config.in Tue Apr 28 20:10:10 1998 -+++ work/net/Config.in Tue Aug 11 19:04:05 1998 +--- ref/net/Config.in Mon Jun 8 19:02:34 1998 ++++ work/net/Config.in Fri Aug 21 00:52:29 1998 @@ -23,6 +23,23 @@ fi fi @@ -7912,19 +11861,18 @@ comment ' ' tristate 'The IPX protocol' CONFIG_IPX ---- ref/net/Makefile Sun Nov 30 23:00:39 1997 -+++ work/net/Makefile Tue Aug 11 19:03:31 1998 -@@ -9,7 +9,8 @@ - +--- ref/net/Makefile Mon Jul 27 08:35:57 1998 ++++ work/net/Makefile Fri Aug 21 00:52:29 1998 +@@ -10,7 +10,7 @@ MOD_SUB_DIRS := ipv4 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \ -- netrom rose lapb x25 wanrouter netlink sched packet sunrpc #decnet -+ netrom rose lapb x25 wanrouter netlink sched packet sunrpc \ -+ atm #decnet + netrom rose lapb x25 wanrouter netlink sched packet sunrpc \ +- econet #decnet ++ econet atm #decnet SUB_DIRS := core ethernet sched MOD_LIST_NAME := NET_MISC_MODULES -@@ -130,6 +131,16 @@ +@@ -131,6 +131,17 @@ ifeq ($(CONFIG_SUNRPC),m) MOD_SUB_DIRS += sunrpc endif @@ -7933,16 +11881,17 @@ +ifeq ($(CONFIG_ATM),y) +SUB_DIRS += atm +ifeq ($(CONFIG_ATM_LANE),m) -+ MOD_SUB_DIRS += atm ++ MOD_ATM = atm +endif +ifeq ($(CONFIG_ATM_MPOA),m) -+ MOD_SUB_DIRS += atm ++ MOD_ATM = atm +endif ++MOD_SUB_DIRS += $(MOD_ATM) endif ifeq ($(CONFIG_DECNET),y) ---- ref/net/protocols.c Sun Nov 30 23:00:40 1997 -+++ work/net/protocols.c Tue Jun 9 21:32:12 1998 +--- ref/net/protocols.c Fri Jul 10 22:51:41 1998 ++++ work/net/protocols.c Fri Aug 21 00:52:29 1998 @@ -79,6 +79,10 @@ extern void rif_init(struct net_proto *); #endif @@ -7954,7 +11903,7 @@ #ifdef NEED_LLC #define NEED_802 #include -@@ -115,6 +119,11 @@ +@@ -113,6 +117,11 @@ #ifdef CONFIG_TR { "RIF", rif_init }, /* RIF for Token ring */ #endif @@ -7966,8 +11915,8 @@ #ifdef NEED_LLC { "802.2LLC", llc_init }, /* 802.2 LLC */ ---- ref/net/ipv4/arp.c Thu May 14 19:26:23 1998 -+++ work/net/ipv4/arp.c Tue Jun 9 21:32:12 1998 +--- ref/net/ipv4/arp.c Sun Aug 9 21:23:47 1998 ++++ work/net/ipv4/arp.c Fri Aug 21 00:52:29 1998 @@ -115,6 +115,9 @@ #include #endif @@ -7992,7 +11941,7 @@ } --- ref/net/sched/Config.in Thu May 14 19:26:23 1998 -+++ work/net/sched/Config.in Fri Jul 24 17:33:35 1998 ++++ work/net/sched/Config.in Fri Aug 21 00:52:29 1998 @@ -7,6 +7,9 @@ tristate 'CSZ packet scheduler' CONFIG_NET_SCH_CSZ #tristate 'H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ @@ -8004,7 +11953,7 @@ tristate 'RED queue' CONFIG_NET_SCH_RED tristate 'SFQ queue' CONFIG_NET_SCH_SFQ --- ref/net/sched/Makefile Tue Apr 28 20:10:11 1998 -+++ work/net/sched/Makefile Fri Jul 24 02:13:11 1998 ++++ work/net/sched/Makefile Fri Aug 21 00:52:29 1998 @@ -7,6 +7,8 @@ # # Note 2! The CFLAGS definition is now in the main makefile... @@ -8025,9 +11974,9 @@ endif ifeq ($(CONFIG_NET_CLS_U32), y) ---- ref/net/sched/sch_api.c Fri May 8 09:08:02 1998 -+++ work/net/sched/sch_api.c Fri Jul 24 01:08:38 1998 -@@ -981,6 +981,9 @@ +--- ref/net/sched/sch_api.c Sat Jul 18 20:48:24 1998 ++++ work/net/sched/sch_api.c Fri Aug 21 00:52:29 1998 +@@ -983,6 +983,9 @@ #ifdef CONFIG_NET_SCH_PRIO INIT_QDISC(prio); #endif @@ -8038,7 +11987,7 @@ tc_filter_init(); #endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/sched/sch_atm.c Thu Aug 13 17:23:58 1998 ++++ work/net/sched/sch_atm.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,550 @@ +/* net/sched/sch_atm.c - ATM VC selection "queueing discipline" */ + @@ -8591,7 +12540,7 @@ +} +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/Makefile Tue Aug 11 20:43:58 1998 ++++ work/net/atm/Makefile Fri Aug 21 00:52:29 1998 @@ -0,0 +1,64 @@ +# +# Makefile for the ATM Protocol Families. @@ -8658,7 +12607,7 @@ +mpoa.o: mpc.o mpoa_caches.o mpoa_proc.o + ld -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/addr.c Tue Jun 9 21:32:12 1998 ++++ work/net/atm/addr.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,159 @@ +/* net/atm/addr.c - Local ATM address registry */ + @@ -8820,7 +12769,7 @@ + return total; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/addr.h Wed Aug 19 14:31:31 1998 ++++ work/net/atm/addr.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,18 @@ +/* net/atm/addr.h - Local ATM address registry */ + @@ -8841,7 +12790,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/clip.c Tue Aug 11 22:36:07 1998 ++++ work/net/atm/clip.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,645 @@ +/* clip.c - RFC1577 Classical IP over ATM */ + @@ -9489,8 +13438,8 @@ + return 0; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/common.c Tue Aug 18 13:47:50 1998 -@@ -0,0 +1,913 @@ ++++ work/net/atm/common.c Fri Aug 21 01:28:53 1998 +@@ -0,0 +1,916 @@ +/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */ + +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ @@ -10163,14 +14112,17 @@ +#if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE) + case SIOCSIFATMTCP: + if (!capable(CAP_NET_ADMIN)) return -EPERM; ++ if (!atm_tcp_ops.attach) return -ENOPKG; + error = atm_tcp_ops.attach(vcc,(int) arg); + if (error >= 0) sock->state = SS_CONNECTED; + return error; + case ATMTCP_CREATE: + if (!capable(CAP_NET_ADMIN)) return -EPERM; ++ if (!atm_tcp_ops.create_persistent) return -ENOPKG; + return atm_tcp_ops.create_persistent((int) arg); + case ATMTCP_REMOVE: + if (!capable(CAP_NET_ADMIN)) return -EPERM; ++ if (!atm_tcp_ops.remove_persistent) return -ENOPKG; + return atm_tcp_ops.remove_persistent((int) arg); +#endif + default: @@ -10405,7 +14357,7 @@ + return atm_do_getsockopt(sock,level,optname,optval,len); +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/common.h Tue Aug 18 14:14:27 1998 ++++ work/net/atm/common.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,46 @@ +/* net/atm/common.h - ATM sockets (common part for PVC and SVC) */ + @@ -10454,7 +14406,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/ipcommon.c Tue Jun 9 21:32:12 1998 ++++ work/net/atm/ipcommon.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,52 @@ +/* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */ + @@ -10509,7 +14461,7 @@ + skb_queue_head_init(from); +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/ipcommon.h Wed Aug 19 14:31:36 1998 ++++ work/net/atm/ipcommon.h Fri Aug 21 01:14:10 1998 @@ -0,0 +1,21 @@ +/* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */ + @@ -10533,7 +14485,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/misc.c Thu Jun 18 19:02:36 1998 ++++ work/net/atm/misc.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,182 @@ +/* net/atm/misc.c - Various functions for use by ATM drivers */ + @@ -10718,7 +14670,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/lec.c Tue Aug 11 19:12:33 1998 ++++ work/net/atm/lec.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,1976 @@ +/* + * lec.c: Lan Emulation driver @@ -12697,7 +16649,7 @@ +} + --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/lec.h Wed Aug 19 14:31:31 1998 ++++ work/net/atm/lec.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,143 @@ +/* + * @@ -12843,7 +16795,7 @@ +#endif _LEC_H_ + --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/lec_arpc.h Wed Aug 19 14:31:31 1998 ++++ work/net/atm/lec_arpc.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,112 @@ +/* + * Lec arp cache @@ -12958,7 +16910,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpc.c Wed Aug 19 18:49:00 1998 ++++ work/net/atm/mpc.c Fri Aug 21 00:52:29 1998 @@ -0,0 +1,1406 @@ +#include +#include @@ -14367,7 +18319,7 @@ +#endif /* MODULE */ + --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpc.h Wed Aug 19 18:49:00 1998 ++++ work/net/atm/mpc.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,73 @@ +#ifndef _MPC_H_ +#define _MPC_H_ @@ -14443,7 +18395,7 @@ + +#endif /* _MPC_H_ */ --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpoa_caches.c Wed Aug 19 18:49:00 1998 ++++ work/net/atm/mpoa_caches.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,535 @@ +#include +#include @@ -14981,7 +18933,7 @@ + return; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpoa_caches.h Wed Aug 19 18:49:00 1998 ++++ work/net/atm/mpoa_caches.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,87 @@ +#ifndef MPOA_CACHES_H +#define MPOA_CACHES_H @@ -15071,7 +19023,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/mpoa_proc.c Wed Aug 19 18:49:00 1998 ++++ work/net/atm/mpoa_proc.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,393 @@ +#include + @@ -15467,7 +19419,7 @@ + + --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/lane_mpoa_init.c Tue Aug 18 13:10:32 1998 ++++ work/net/atm/lane_mpoa_init.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,48 @@ +#include +#include @@ -15518,7 +19470,7 @@ +} +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/proc.c Tue Aug 11 20:42:50 1998 ++++ work/net/atm/proc.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,610 @@ +/* net/atm/proc.c - ATM /proc interface */ + @@ -16131,7 +20083,7 @@ + return error; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/protocols.h Tue Jun 9 21:32:12 1998 ++++ work/net/atm/protocols.h Fri Aug 21 00:52:30 1998 @@ -0,0 +1,16 @@ +/* net/atm/protocols.h - ATM protocol handler entry points */ + @@ -16150,7 +20102,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/pvc.c Wed Jun 17 02:46:50 1998 ++++ work/net/atm/pvc.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,158 @@ +/* net/atm/pvc.c - ATM PVC sockets */ + @@ -16311,7 +20263,7 @@ +#endif +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/raw.c Tue Jun 9 21:32:12 1998 ++++ work/net/atm/raw.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,80 @@ +/* net/atm/raw.c - Raw AAL0 and AAL5 transports */ + @@ -16394,7 +20346,7 @@ + return 0; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/resources.c Tue Aug 4 16:47:00 1998 ++++ work/net/atm/resources.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,186 @@ +/* net/atm/resources.c - Staticly allocated resources */ + @@ -16583,7 +20535,7 @@ +EXPORT_SYMBOL(shutdown_atm_dev); +EXPORT_SYMBOL(bind_vcc); --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/resources.h Wed Aug 19 14:31:31 1998 ++++ work/net/atm/resources.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,32 @@ +/* net/atm/resources.h - ATM-related resources */ + @@ -16618,7 +20570,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/signaling.c Tue Aug 18 13:43:42 1998 ++++ work/net/atm/signaling.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,252 @@ +/* net/atm/signaling.c - ATM signaling */ + @@ -16873,7 +20825,7 @@ + return 0; +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/signaling.h Wed Aug 19 14:31:31 1998 ++++ work/net/atm/signaling.h Fri Aug 21 01:14:05 1998 @@ -0,0 +1,25 @@ +/* net/atm/signaling.h - ATM signaling */ + @@ -16901,7 +20853,7 @@ + +#endif --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/svc.c Tue Aug 11 19:54:16 1998 ++++ work/net/atm/svc.c Fri Aug 21 00:52:30 1998 @@ -0,0 +1,387 @@ +/* net/atm/svc.c - ATM SVC sockets */ + @@ -17291,7 +21243,7 @@ + } +} --- /dev/null Tue Jan 1 05:00:00 1980 -+++ work/net/atm/tunable.h Tue Jun 9 21:32:12 1998 ++++ work/net/atm/tunable.h Fri Aug 21 00:52:30 1998 @@ -0,0 +1,26 @@ +/* net/atm/tunable.h - Tunable parameters of ATM support */ + @@ -17319,9 +21271,9 @@ + quota per PDU */ + +#endif ---- ref/net/core/skbuff.c Thu May 14 19:26:22 1998 -+++ work/net/core/skbuff.c Tue Aug 18 14:04:05 1998 -@@ -149,6 +149,10 @@ +--- ref/net/core/skbuff.c Wed Aug 19 02:02:57 1998 ++++ work/net/core/skbuff.c Fri Aug 21 00:52:30 1998 +@@ -160,6 +160,10 @@ skb->is_clone = 0; skb->cloned = 0; diff -ur --new-file old/atm/doc/usage.tex new/atm/doc/usage.tex --- old/atm/doc/usage.tex Wed Aug 19 19:03:36 1998 +++ new/atm/doc/usage.tex Fri Aug 21 00:53:35 1998 @@ -1,7 +1,7 @@ %%def%:= %:\begin{verbatim} -%:Usage instructions - ATM on Linux, release 0.42 +%:Usage instructions - ATM on Linux, release 0.43 %:------------------------------------------------- %: %:\end{verbatim} @@ -38,14 +38,14 @@ \title{ATM on Linux \\ User's guide \\ - Release 0.42 (alpha)} + Release 0.43 (alpha)} \author{Werner Almesberger \\ {\tt Werner.Almesberger@epfl.ch} \\ \\ Institute for computer Communications and Applications (ICA) \\ EPFL, CH-1015 Lausanne, Switzerland} -\date{August 19, 1998} +\date{August 21, 1998} \begin{document} \maketitle @@ -81,9 +81,9 @@ In order to install this package, you need \begin{itemize} \item the package itself - \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.42.tar.gz} - \item the Linux kernel, version 2.1.105, e.g. from - \url{ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.105.tar.gz} + \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.43.tar.gz} + \item the Linux kernel, version 2.1.117, e.g. from + \url{ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.117.tar.gz} \item Perl, version 4 or 5 \item if you want memory debugging: MPR version 1.6, e.g. from \url{ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/mpr-1.6.tar.gz} @@ -98,13 +98,13 @@ distribution: \begin{verbatim} -tar xfz atm-0.42.tar.gz +tar xfz atm-0.43.tar.gz \end{verbatim} and the kernel source: \begin{verbatim} -tar xfz linux-2.1.105.tar.gz +tar xfz linux-2.1.117.tar.gz \end{verbatim} Finally, you can extract the ATM-related patches: @@ -121,6 +121,7 @@ \name{Makefile}, and distribution scripts \item[\path{atm/sigd/}] UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: \name{atmsigd} + \item[\path{atm/sigd.new/}] Experimental version: \name{atmsigd.new} \item[\path{atm/saal/}] Signaling AAL library (SSCOP, SSCF, and SAAL) \item[\path{atm/qgen/}] Q.2931-style message handling \item[\path{atm/ilmid/}] ILMI address registration demon: \name{ilmid} @@ -189,8 +190,8 @@ %The TNETA1570 driver is for a board developed by Rolf Fiedler at TU Chemnitz, %see also \url{ftp://ftp.infotech.tu-chemnitz.de/pub/linux-atm}. -% Note that the file \path{drivers/atm/nicstar.h} contains a few configurable -% settings for the IDT 77201 driver. +Note that the file \path{drivers/atm/nicstar.h} contains a few configurable +settings for the IDT 77201 driver. Then build your kernel and reboot. diff -ur --new-file old/atm/doc/usage.txt new/atm/doc/usage.txt --- old/atm/doc/usage.txt Wed Aug 19 19:04:05 1998 +++ new/atm/doc/usage.txt Fri Aug 21 01:02:14 1998 @@ -1,4 +1,4 @@ -Usage instructions - ATM on Linux, release 0.42 +Usage instructions - ATM on Linux, release 0.43 ------------------------------------------------- For updates of ATM on Linux, please check the Web page at @@ -17,9 +17,9 @@ In order to install this package, you need - the package itself - ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.42.tar.gz - - the Linux kernel, version 2.1.105, e.g. from - ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.105.tar.gz + ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.43.tar.gz + - the Linux kernel, version 2.1.117, e.g. from + ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.117.tar.gz - Perl, version 4 or 5 - if you want memory debugging: MPR version 1.6, e.g. from ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/mpr-1.6.tar.gz @@ -33,11 +33,11 @@ all the files listed above there. Then extract the ATM on Linux distribution: -tar xfz atm-0.42.tar.gz +tar xfz atm-0.43.tar.gz and the kernel source: -tar xfz linux-2.1.105.tar.gz +tar xfz linux-2.1.117.tar.gz Finally, you can extract the ATM-related patches: @@ -49,6 +49,7 @@ atm/ Documentation in ASCII format, kernel patch, top-level Makefile, and distribution scripts atm/sigd/ UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: atmsigd + atm/sigd.new/ Experimental version: atmsigd.new atm/saal/ Signaling AAL library (SSCOP, SSCF, and SAAL) atm/qgen/ Q.2931-style message handling atm/ilmid/ ILMI address registration demon: ilmid @@ -93,6 +94,9 @@ Enable usec resolution timestamps (CONFIG_ATM_ZATM_EXACT_TS) IDT 77201 (NICStAR) (CONFIG_ATM_NICSTAR) + +Note that the file drivers/atm/nicstar.h contains a few configurable +settings for the IDT 77201 driver. Then build your kernel and reboot. diff -ur --new-file old/atm/maint/README.nstune new/atm/maint/README.nstune --- old/atm/maint/README.nstune Thu Jan 1 01:00:00 1970 +++ new/atm/maint/README.nstune Fri Aug 21 00:51:16 1998 @@ -0,0 +1,77 @@ +[ Note: this is the original README. Since the patch is already included in + atm.patch, only the instructions at the end of this file apply. - WA ] + +README file for release 7 of the nicstar device driver by Rui Prior. + + +* * Make sure you read carefully the Installation section before proceeding * * + +This is the 7th release of my nicstar device driver. +The driver is meant to work with the atm patch 0.38 applied to the 2.1.105 kernel. +It supports AAL0 and AAL5. Supported traffic classes are UBR and CBR. +Please note that this is not meant to be a final release, but a preliminary version. +The code isn't optimized, doesn't look very nice, and probably still has some bugs, but at least it doesn't seem to freeze or crash the kernel. +Hope it is useful. +NOTE: the code for extracting the MAC address from the EPROM was not written by me (I didn't want to reinvent the weel :-). It was taken from the frle-0.22 device driver for the 2.0 series of kernels. Please read the nicstarmac.copyright file for the copyright notice included in the frle-0.22 device driver. + + +Contact: +-------- + +To send some feedback or bug reports, my e-mail address is rprior@inescn.pt +My group's web page is at http://aramis.inescn.pt + + +Installation: +------------- + +To use the driver, first you have to apply the atm patch to the kernel sources. +Read the USAGE file in the atm distribution to learn how to do it. +Then create a directory, cd to that directory, and extract the files with + + tar xvfz nicstar.tgz + +Now you have to go to the linux kernel source directory (usually /usr/src/linux) and apply the nicstar patch. + + cd /usr/src/linux + patch -s -p1 < (nicstar_dir)/ns.patch + +where (nicstar_dir) is the directory where you extracted the files. +Configure and compile the kernel the usual way, and you should have it working. + +To compile the nstune utility, + + cd (nicstar_dir) + make + + +If you compiled the driver as a module you can insert it with + + insmod nicstar + +and when you don't need it anymore you can remove it with + + rmmod nicstar + + + +Tuning: +------- + +You can tune the buffer level watermarks by doing + + nstune itf {s|l|h|i} min init max + +where: +- itf is the atm interface number (most users only have one card, so this is 0. +- s for small buffers, l for large buffers, h for huge buffers or i for iovec buffers. +- min is the lower watermark, max the higher, and init is the initial level, and the level which is ideally sustained. + +For example: nstune 0 s 10 30 50 + +If you want to set the actual buffer level, not the watermarks, use + + nstune itf {s|l|h|i} + +which will set the corresponding buffer number to the init watermark level. +Please note that small and large buffer numbers can't be decreased by using nstune. diff -ur --new-file old/atm/maint/nstune.c new/atm/maint/nstune.c --- old/atm/maint/nstune.c Thu Jan 1 01:00:00 1970 +++ new/atm/maint/nstune.c Fri Jun 26 19:50:11 1998 @@ -0,0 +1,141 @@ +/****************************************************************************** + * + * nstune.c + * + * User level utility to tune the NICStAR device driver. + * + * Author: Rui Prior + * + * (C) INESC 1998 + * + ******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static void usage(const char *name) +{ + fprintf(stderr, "Set buffer level marks: \n"); + fprintf(stderr, "%s itf {s|l|h|i} min init max \n", name); + fprintf(stderr, "Set buffer level to init mark: \n"); + fprintf(stderr, "%s itf {s|l|h|i} \n", name); + fprintf(stderr, "\n"); + exit(1); +} + + +int main(int argc, char **argv) +{ + char *name, *end; + int itf, min, init, max; + int s; + pool_levels pl; + int btype; + struct atmif_sioc sioc; + + name = argv[0]; + if (argc != 6 && argc != 3) + usage(name); + itf = strtol(argv[1], &end, 0); + if (end == argv[1] || itf < 0) + usage(name); + if (argc == 6) + { + min = strtol(argv[3], &end, 0); + if (end == argv[3] || min <= 0) + usage(name); + init = strtol(argv[4], &end, 0); + if (end == argv[4] || init <= 0) + usage(name); + max = strtol(argv[5], &end, 0); + if (end == argv[5] || max <= 0) + usage(name); + if (min >= init || init >= max) + usage(name); + switch(*argv[2]) + { + case 's': + pl.buftype = NS_BUFTYPE_SMALL; + break; + case 'l': + pl.buftype = NS_BUFTYPE_LARGE; + break; + case 'h': + pl.buftype = NS_BUFTYPE_HUGE; + break; + case 'i': + pl.buftype = NS_BUFTYPE_IOVEC; + break; + default: + usage(name); + } + + sioc.number = itf; + sioc.arg = &pl; + sioc.length = sizeof(pl); + pl.level.min = min; + pl.level.init = init; + pl.level.max = max; + + s = socket(PF_ATMPVC, SOCK_DGRAM, 0); + if (s < 0) + { + perror("socket"); + return 2; + } + if (ioctl(s, NS_SETBUFLEV, &sioc) < 0) + { + perror("ioctl NS_SETBUFLEV"); + return 3; + } + + } + else /* argc == 2 */ + { + switch(*argv[2]) + { + case 's': + btype = NS_BUFTYPE_SMALL; + break; + case 'l': + btype = NS_BUFTYPE_LARGE; + break; + case 'h': + btype = NS_BUFTYPE_HUGE; + break; + case 'i': + btype = NS_BUFTYPE_IOVEC; + break; + default: + usage(name); + } + + sioc.number = itf; + sioc.arg = (void *) btype; + sioc.length = sizeof(void *); + + s = socket(PF_ATMPVC, SOCK_DGRAM, 0); + if (s < 0) + { + perror("socket"); + return 2; + } + if (ioctl(s, NS_ADJBUFLEV, &sioc) < 0) + { + perror("ioctl NS_ADJBUFLEV"); + return 3; + } + + } + + return 0; +} diff -ur --new-file old/atm/mkdist new/atm/mkdist --- old/atm/mkdist Wed Aug 19 16:40:37 1998 +++ new/atm/mkdist Fri Aug 21 01:02:50 1998 @@ -1,7 +1,7 @@ #!/bin/sh [ -r ./VERSION ] || exit 1 VERSION=`cat ./VERSION` -SRCDIR=$HOME/k/2105 +SRCDIR=$HOME/k/2117 ARCHDIR=$HOME/l/arch ( cd $SRCDIR @@ -26,6 +26,14 @@ atm/sigd/sap.h atm/sigd/sap.c atm/sigd/trace.c atm/sigd/trace.h \ atm/sigd/policy.h atm/sigd/policy.c \ atm/sigd/mkmess.pl atm/sigd/atmsigd.8 atm/sigd/atmsigd.conf.4 \ + atm/sigd.new/README \ + atm/sigd.new/Makefile atm/sigd.new/atmsigd.c atm/sigd.new/cfg.l \ + atm/sigd.new/cfg.y atm/sigd.new/io.h atm/sigd.new/io.c \ + atm/sigd.new/kernel.c atm/sigd.new/proto.h atm/sigd.new/proto.c \ + atm/sigd.new/uni.c atm/sigd.new/timeout.c atm/sigd.new/timeout.h \ + atm/sigd.new/sap.h atm/sigd.new/sap.c atm/sigd.new/trace.c \ + atm/sigd.new/trace.h atm/sigd.new/policy.h atm/sigd.new/policy.c \ + atm/sigd.new/mkmess.pl atm/sigd.new/atmsigd.8 atm/sigd.new/atmsigd.conf.4 \ atm/saal/Makefile atm/saal/saal.h atm/saal/saal.c atm/saal/sscf.h \ atm/saal/sscf.c atm/saal/sscop.h atm/saal/sscop.c atm/saal/queue.h \ atm/saal/queue.c atm/saal/pdu.h atm/saal/pdu.c \ @@ -64,7 +72,7 @@ atm/maint/atmdump.c atm/maint/atmtcp.c atm/maint/sonetdiag.c \ atm/maint/atmaddr.8 atm/maint/atmdiag.8 atm/maint/atmdump.8 \ atm/maint/atmtcp.8 atm/maint/zntune.c atm/maint/esi.c atm/maint/esi.8 \ - atm/maint/saaldump.c \ + atm/maint/saaldump.c atm/maint/README.nstune atm/maint/nstune.c \ atm/test/Makefile atm/test/aread.c atm/test/awrite.c atm/test/br.c \ atm/test/bw.c atm/test/ttcp.c atm/test/aping.c atm/test/window.c \ atm/test/align.c atm/test/isp.c atm/test/isp.h atm/test/ispl.l \ @@ -135,4 +143,3 @@ # atm/atm-$VERSION-1.spec | gzip -9 >$ARCHDIR/atm-$VERSION.tar.gz #atm/bind-4.9.4.T4B.ATM.patch -# atm/maint/README.nstune atm/maint/nstune.c \ diff -ur --new-file old/atm/mkpatch new/atm/mkpatch --- old/atm/mkpatch Wed Aug 19 19:02:34 1998 +++ new/atm/mkpatch Fri Aug 21 01:08:48 1998 @@ -1,4 +1,6 @@ #!/bin/sh +[ -d ref ] || { echo ref/ is missing; exit; } +[ -d work ] || { echo work/ is missing; exit; } >atm.patch for n in `awk '$1 !~ /^#/ {print$1}' <mess.c + +# +# The following hack makes sure that "make depend" finds q.out.h and is +# happy with it. Once qgen has been built, there will be ../qgen/q.out.h, +# which is first in the include file search path and therefore gets +# included. An second "make depend" will also use the right file. +# + +depend: fake_q.out.h + +fake_q.out.h: + echo "! This must not compile" >q.out.h diff -ur --new-file old/atm/sigd.new/README new/atm/sigd.new/README --- old/atm/sigd.new/README Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/README Fri Aug 21 00:47:03 1998 @@ -0,0 +1,25 @@ +This is a very early version of atmsigd that supports multiple signaling +entities in the same process. Everything's still a bit buggy and ugly. + +To play with it, ... + +1) Generate ATM interfaces 1 and 2: + # atmtcp -b -i 1 -l + # atmtcp -b -i 2 -c localhost +2) Create the following configuration file (name "config"): + entity 1.0.5 { mode user route +1 } + entity 2.0.5 { mode network default } +3) Launch atmsigd: + # ./atmsigd.new -c config +4) Add local addresses: + # atmaddr -a 1 +1 + # atmaddr -a 2 +2 +5) Test it: + % ttcp_atm -r -a + % ttcp_atm -t -a +1 + +Known bugs: + - wildcard bind removes wildcard + - selection of local address isn't consistent with signaling interface + selection + - everything else that's marked with @@@ diff -ur --new-file old/atm/sigd.new/atmsigd.8 new/atm/sigd.new/atmsigd.8 --- old/atm/sigd.new/atmsigd.8 Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/atmsigd.8 Thu Jun 25 13:08:40 1998 @@ -0,0 +1,105 @@ +.TH ATMSIGD 8 "June 25, 1998" "Linux" "Maintenance Commands" +.SH NAME +atmsigd \- ATM signaling demon +.SH SYNOPSIS +.B atmsigd +.RB [ \-b ] +.RB [ \-c\ \fIconfig_file\fP ] +.RB [ \-d ] +.RB [ \-D\ \fIdump_dir\fP ] +.RB [ \-l\ \fIlogfile\fP ] +.RB [ \-m\ \fImode\fP ] +.RB [ \-n ] +.RB [ \-q\ \fIqos\fP ] +.RB [ \-t\ \fItrace_length\fP ] +.RB [ [\fIitf\fP.]\fIvpi\fP.\fIvci\fP +.RB [ \fIinput\ output\fP ] ] +.SH DESCRIPTION +\fBatmsigd\fP implements the ATM UNI signaling protocol. Requests to +establish, accept, or close ATM SVCs are sent from the kernel (using a +comparably simple protocol) to the signaling demon, which then performs +the dialog with the network. +.P +Note that \fBatmsigd\fP is not able to accept or establish connections +until the local ATM address of the interface is configured by \fBilmid\fP +or manually using \fBatmaddr\fP. +.P +The default signaling VC (interface 0, VPI 0, VCI 5) can be overridden on +the command line by specifying a different PVC address. +.P +When overriding the default VC, optionally a pair of named pipes to use for +communicating with the user of signaling can be specified. Normally, the +kernel is the user of signaling and \fBatmsigd\fP opens a special socket for +communication with it. +.P +If \fBatmsigd\fP is killed, all system calls requiring interaction with it +will return with an error and set \fBerrno\fP to \fBEUNATCH\fP. +.SH OPTIONS +.IP \fB\-b\fP +Run in background (i.e. in a forked child process) after initializing. +.IP \fB\-c\ \fIconfig_file\fP +Use the specified configuration file instead of \fB/etc/atmsigd.conf\fP +If an option is specified in the configuration file and on the command +line, the command line has priority. +.IP \fB\-d\fP +Enables (lots of) debugging output. By default, \fBatmsigd\fP is comparably +quiet. +.IP \fB\-D\ \fIdump_dir\fP +Specifies the directory to which \fBatmsigd\fP will write status and trace +dumps. If tracing is not yet enabled, the trace size is automatically +set to a (small) default value. +.IP \fB\-l\ \fIlogfile\fP +Write diagnostic messages to the specified file. The special name +\fBsyslog\fP is used to send diagnostics to the system logger, \fBstderr\fP +is used to send diagnostics to standard error. If \fB\-l\fP is absent, the +setting in \fBatmsigd.conf\fP is used. If \fBatmsigd\fP doesn't specify a +destination either, messages are written to standard error. +.IP \fB\-m\ \fImode\fP +Set the mode of operation. The following modes are available: \fBuser\fP for +the user side (the default), \fBnetwork\fP for the network side (useful if you +have two PCs but no switch), and \fBswitch\fP for operation with a signaling +relay in a switch. +.IP \fB\-n\fP +Prints addresses in numeric format only, i.e. no address to name translation +is attempted. +.IP \fB\-q\ \fIqos\fP +Configures the signaling VC to use the specified quality of service (see +qos(7) for the syntax). +By default, UBR at link speed is used on the signaling VC. +.IP \fB\-t\ \fItrace_length\fP +Enables tracing and sets the number +of entries that should be kept in the trace buffer. +.SH FILES +.PD 0 +.TP 25 +.B /etc/atmsigd.conf +default configuration file +.TP 25 +.B /var/tmp/atmsigd.\fIpid\fB.status.\fIversion\fP +default location of status dumps +.TP 25 +.B /var/tmp/atmsigd.\fIpid\fB.trace.\fIversion\fP +default location of signaling trace dumps +.PD +.SH DEBUGGING +When receiving a \fBSIGUSR1\fP signals, \fBatmsigd\fP dumps the list of all +internal socket descriptors. With \fBSIGUSR2\fP, it dumps the contents of +the trace buffer. If a dump directory was set, dumps are written to files +called \fBatmsigd.\fP\fIpid\fP\fB.status.\fP\fInumber\fP and +\fBatmsigd.\fP\fIpid\fP\fB.trace.\fP\fInumber\fP, respectively, with +\fInumber\fP +starting at zero and being incremented for every dump. If no dump directory +is set, dumps are written to standard error. +.P +Dumps are also generated whenever \fBatmsigd\fP detects a fatal error and +terminates. No attempt is made to catch signals like \fBSIGSEGV\fP. +.SH BUGS +The generation of traces is a comparably slow +process which may already take several seconds for only 100 trace entries. +To generate a trace dump, \fBatmsigd\fP therefore forks a child process that +runs in parallel to the signaling demon. +.SH AUTHOR +Werner Almesberger, EPFL ICA +.SH "SEE ALSO" +atmaddr(8), atmsigd.conf(4), ilmid(8), qos(7) +.\"{{{}}} diff -ur --new-file old/atm/sigd.new/atmsigd.c new/atm/sigd.new/atmsigd.c --- old/atm/sigd.new/atmsigd.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/atmsigd.c Fri Aug 21 00:07:18 1998 @@ -0,0 +1,481 @@ +/* atmsigd.c - ATM signaling demon */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atm.h" +#include "atmd.h" +#include "qlib.h" + +#include "io.h" +#include "proto.h" +#include "saal.h" +#include "trace.h" + + +#define COMPONENT "SIGD" +#define CONFIG_FILE "/etc/atmsigd.conf" + + +extern int yyparse(void); +extern FILE *yyin; + +int debug = 0; +int pretty = A2T_PRETTY | A2T_NAME | A2T_LOCAL; +const char *dump_dir = NULL; + + +/* A little hack until we have full support for multiple signaling entities */ + +SIG_ENTITY _entity = { + 0, /* fd */ + sm_user, /* mode */ + -1, /* sig_pcr; obsolete @@@ */ + NULL /* sig_qos */ +}; + + +/* ------------------------------ SAAL relays ------------------------------ */ + + +static void q_estab_conf(void *user_data,void *uu_data,int uu_length) +{ + SIG_ENTITY *sig = user_data; + + saal_okay(sig); +} + + +static void q_rel_ind(void *user_data,void *uu_data,int uu_length) +{ + SIG_ENTITY *sig = user_data; + + saal_failure(sig); + saal_estab_req(&sig->saal,NULL,0); +} + + +static void q_restart(void *user_data,void *uu_data,int uu_length,int ind) +{ + SIG_ENTITY *sig = user_data; + + saal_failure(sig); + if (!ind) saal_okay(sig); + /* actually, ind should probably never be zero */ +} + + +void from_net(SIG_ENTITY *sig,void *buffer,int size) +{ + saal_pdu(&sig->saal,buffer,size); +} + + +void to_signaling(SIG_ENTITY *sig,void *msg,int size) +{ + trace_uni("TO NETWORK",sig,msg,size); + diag(COMPONENT,DIAG_DEBUG,"TO SAAL (%d.%d.%d): %s (0x%02x) CR 0x%06x " + "(%d bytes)",S_PVC(sig), + mid2name(((unsigned char *) msg)[5]),((unsigned char *) msg)[5], + (((unsigned char *) msg)[2] << 16) | (((unsigned char *) msg)[3] << 8) | + ((unsigned char *) msg)[4],size); + saal_send(&sig->saal,msg,size); +} + + +static void q_data_ind(void *user_data,void *data,int length) +{ + SIG_ENTITY *sig = user_data; + + trace_uni("FROM NETWORK",sig,data,length); + to_uni(sig,data,length); +} + + +static void q_cpcs_send(void *user_data,void *data,int length) +{ + SIG_ENTITY *sig = user_data; + + to_net(sig,data,length); +} + + +static SAAL_USER_OPS ops = { + NULL, /* no q_estab_ind - 5.5.6.9 says 5.5.6.11 and 5.5.6.11 says "may" */ + q_estab_conf, + q_rel_ind, + NULL, /* no q_rel_conf - what to do ? */ + q_restart, + q_data_ind, + NULL, /* no q_unitdata */ + q_cpcs_send +}; + + +/* -------------------------------- signals -------------------------------- */ + + +static volatile int got_usr1 = 0,got_usr2 = 0; + + +static void dump_addr(FILE *file,const char *label,struct sockaddr_atmsvc *addr) +{ + char buffer[MAX_ATM_ADDR_LEN+1]; + int i; + + if (!atmsvc_addr_in_use(*addr)) return; + fprintf(file," %s ",label); + if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) addr,A2T_NAME | + A2T_PRETTY | A2T_LOCAL) >= 0) fprintf(file,"%s\n",buffer); + else { + fprintf(file,"\n"); + } +} + + +static void dump_sap(FILE *file,const char *label,struct atm_sap *sap) +{ + char buffer[MAX_ATM_SAP_LEN+1]; + int i; + + fprintf(file," %s ",label); + if (sap2text(buffer,MAX_ATM_SAP_LEN+1,sap,S2T_NAME | S2T_LOCAL) >= 0) + fprintf(file,"%s\n",buffer); + else { + fprintf(file,"\n"); + } +} + + +static void dump_status(FILE *file,const char *banner) +{ + SIG_ENTITY *sig; + SOCKET *walk; + + if (entities) fprintf(file,"%s\n\n",banner); + for (sig = entities; sig; sig = sig->next) { + fprintf(file,"--- Entity %d.%d.%d ---\n",S_PVC(sig)); + for (walk = sockets; walk; walk = walk->next) { + fprintf(file,"0x%lx: %s, CR 0x%06lX, PVC %d.%d.%d\n",walk->id, + state_name[walk->state],walk->call_ref,walk->pvc.sap_addr.itf, + walk->pvc.sap_addr.vpi,walk->pvc.sap_addr.vci); + dump_addr(file,"local ",&walk->local); + dump_addr(file,"remote",&walk->remote); + dump_sap(file,"sap",&walk->sap); + } + } +} + + +static void dump_trace(FILE *file,const char *banner) +{ + static int busy = 0; + char *trace; + + if (busy++) abort(); + trace = get_trace(); + if (trace) { + fprintf(file,"%s\n\n",banner); + fprintf(file,"%s",trace); + } + busy--; +} + + +void poll_signals(void) +{ + static status_num = 0,trace_num = 0; + char path[PATH_MAX+1]; + FILE *file; + + if (got_usr1) { + got_usr1 = 0; + if (!dump_dir) file = stderr; + else { + sprintf(path,"atmsigd.%d.status.%d",getpid(),status_num++); + if ((file = fopen(path,"w"))) + diag(COMPONENT,DIAG_INFO,"Dumping to %s",path); + else { + perror(path); + file = stderr; + } + } + dump_status(file,"Status dump (on SIGUSR1)"); + if (file != stderr) (void) fclose(file); + } + if (got_usr2) { + pid_t pid; + + got_usr2 = 0; + if (!dump_dir) file = stderr; + else { + sprintf(path,"atmsigd.%d.trace.%d",getpid(),trace_num++); + if ((file = fopen(path,"w"))) + diag(COMPONENT,DIAG_INFO,"Dumping to %s",path); + else { + perror(path); + file = stderr; + } + } + if (!(pid = fork())) + dump_trace(file,"Message trace (on SIGUSR2)"); + else if (pid < 0) perror("fork"); + if (file != stderr) (void) fclose(file); + if (!pid) exit(0); + } +} + + +static void handle_signal(int sig) +{ + switch (sig) { + case SIGUSR1: + got_usr1 = 1; + break; + case SIGUSR2: + got_usr2 = 1; + break; + default: + break; + } +} + + +static void setup_signals(void) +{ + struct sigaction act; + + (void) signal(SIGCHLD,SIG_IGN); /* reap children automatially */ + act.sa_handler = handle_signal; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGUSR1,&act,NULL) < 0) { + perror("sigaction"); + exit(1); + } + if (sigaction(SIGUSR2,&act,NULL) < 0) { + perror("sigaction"); + exit(1); + } +} + + +/* ------------------------------- main ... ------------------------------- */ + + +static void trace_on_exit(int status,void *dummy) +{ + char path[PATH_MAX+1]; + FILE *file; + + if (!status) return; + if (!dump_dir) file = stderr; + else { + sprintf(path,"atmsigd.%d.trace.exit",getpid()); + if (!(file = fopen(path,"w"))) { + perror(path); + file = stderr; + } + } + dump_trace(file,"Message trace (after error exit)"); + if (file != stderr) (void) fclose(file); +} + + +static void manual_override(void) +{ + /* + * Gross hack to avoid changing the command-line parameters ... @@@ + */ + entities = &_entity; + _entity.next = NULL; +} + + +static void usage(const char *name) +{ + fprintf(stderr,"usage: %s [ -b ] [ -c config_file ] [ -d ] " + "[ -D dump_dir ]\n" + " [ -l logfile ] [ -n ] [ -m user|network|switch ] [ -q qos ]\n" + " [ -t trace_length ] [ [itf.]vpi.vci [ socket_path ] ]\n",name); + exit(1); +} + + +int main(int argc,char **argv) +{ + SIG_ENTITY *sig; + const char *config_file; + char *end; + int c,background; + int net = 0,allocate_ci = 1; + + set_application("atmsigd"); + config_file = CONFIG_FILE; + dump_dir = NULL; + background = 0; + memset(&_entity.signaling_pvc,0,sizeof(_entity.signaling_pvc)); + /* 1st pass to get the -c option */ + while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:")) != EOF) + if (c == 'c') config_file = optarg; + if (!(yyin = fopen(config_file,"r"))) + diag(COMPONENT,DIAG_WARN,"%s not found. - Using defaults.",config_file); + else if (yyparse()) + diag(COMPONENT,DIAG_FATAL,"Error in config file. - Aborting."); + if (!atmpvc_addr_in_use(_entity.signaling_pvc)) + _entity.signaling_pvc.sap_addr.vci = 5; + /* process all other options but -c */ + optind = 0; + while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:")) != EOF) + switch (c) { + case 'A': + manual_override(); + allocate_ci = 0; + break; + case 'b': + background = 1; + break; + case 'c': + /* already handled */ + break; + case 'd': + set_verbosity(NULL,DIAG_DEBUG); + set_verbosity("QMSG",DIAG_INFO); + set_verbosity("SSCOP",DIAG_INFO); + debug = 1; + /*q_dump = 1;*/ + break; + case 'D': + dump_dir = optarg; + if (!trace_size) trace_size = DEFAULT_TRACE_SIZE; + break; + case 'l': + set_logfile(optarg); + break; + case 'm': + manual_override(); + if (!strcmp(optarg,"user")) _entity.mode = sm_user; + else if (!strcmp(optarg,"network")) _entity.mode = sm_net; + else if (!strcmp(optarg,"switch")) _entity.mode = sm_switch; + else usage(argv[0]); + break; + case 'n': + pretty = A2T_PRETTY; + break; + case 'N': + manual_override(); + net = 1; + break; + case 'q': + manual_override(); + if (_entity.sig_pcr != -1) usage(argv[0]); + _entity.sig_qos = optarg; + break; + case 'P': /* obsolete */ + manual_override(); + if (_entity.sig_qos) usage(argv[0]); + _entity.sig_pcr = strtol(optarg,&end,0); + if (*end) usage(argv[0]); + diag(COMPONENT,DIAG_WARN,"option -P is obsolete, " + "please use -q qos instead"); + break; + case 't': + trace_size = strtol(optarg,&end,0); + if (*end) usage(argv[0]); + break; + default: + usage(argv[0]); + } + if (_entity.mode == sm_unknown) + if (net) { + if (allocate_ci) { + _entity.mode = sm_net; + diag(COMPONENT,DIAG_WARN,"option -N is obsolete, " + "please use -m network instead"); + } + else { + _entity.mode = sm_switch; + diag(COMPONENT,DIAG_WARN,"options -N -A are obsolete, " + "please use -m switch instead"); + } + } + else if (allocate_ci) _entity.mode = sm_user; + else usage(argv[0]); + if (optind < argc) { + manual_override(); + if (text2atm(argv[optind],(struct sockaddr *) &_entity.signaling_pvc, + sizeof(_entity.signaling_pvc),T2A_PVC) < 0) + diag(COMPONENT,DIAG_FATAL,"text2atm \"%s\": failed",argv[optind]); + optind++; + if (optind == argc-1) { + open_unix(argv[optind]); + optind++; + } + } + if (optind != argc) usage(argv[0]); + if (dump_dir) + if (chdir(dump_dir) < 0) + diag(COMPONENT,DIAG_ERROR,"chdir %s: %s",dump_dir,strerror(errno)); + for (sig = entities; sig; sig = sig->next) { + diag(COMPONENT,DIAG_INFO,"Linux ATM signaling " +#ifdef UNI30 + "UNI 3.0" +#endif +#ifdef UNI31 + "UNI 3.1" +#ifdef ALLOW_UNI30 + "+3.0compat" +#endif +#endif +#ifdef UNI40 + "UNI 4.0" +#ifdef Q2963_1 + "+Q.2931.1" +#endif +#endif + "/AAL5, version " VERSION " on %d.%d.%d",S_PVC(sig)); + diag(COMPONENT,DIAG_INFO,"Acting as %s", + sig->mode == sm_user ? "USER side" : + sig->mode == sm_net ? "NETWORK side" : "SWITCH"); + } + if (open_all()) return 1; + init_current_time(); + q_start(); + for (sig = entities; sig; sig = sig->next) { + if (sig->mode != sm_switch) init_addr(sig); + start_saal(&sig->saal,&ops,sig); + saal_estab_req(&sig->saal,NULL,0); + } + setup_signals(); + if (background) { + pid_t pid; + + pid = fork(); + if (pid < 0) + diag(COMPONENT,DIAG_FATAL,"fork: %s",strerror(errno)); + if (pid) { + diag(COMPONENT,DIAG_DEBUG,"Backgrounding (PID %d)",pid); + exit(0); + } + } + (void) on_exit(trace_on_exit,NULL); + poll_loop(); + close_all(); + for (sig = entities; sig; sig = sig->next) stop_saal(&sig->saal); + return 0; +} diff -ur --new-file old/atm/sigd.new/atmsigd.conf.4 new/atm/sigd.new/atmsigd.conf.4 --- old/atm/sigd.new/atmsigd.conf.4 Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/atmsigd.conf.4 Thu Jun 25 13:08:30 1998 @@ -0,0 +1,113 @@ +.TH ATMSIGD.CONF 4 "June 25, 1998" "Linux" "File Formats" +.SH NAME +atmsigd.conf \- configuration file for the ATM signaling demon +.SH SYNOPSIS +.B /etc/atmsigd.conf +.SH DESCRIPTION +\fBatmsigd.conf\fP contains configuration data for \fBatmsigd\fP. +\fBatmsigd\fP reads \fBatmsigd.conf\fP after parsing the command +line options, before connecting to the ATM network. +.P +Configuration parameters are arranged in functional groups. In order to +set a parameter, the name of the group, the name of the parameter, and +the parameter value(s) have to be specified, e.g. +.nf +.sp + sig level debug +.sp +.fi +decreases the logging threshold for messages related to signaling to the +\fBdebug\fP level. The following options are recognized: +.IP \fBdebug\ dump\ \fIpath\fP +Specifies the directory to which \fBatmsigd\fP will write status and trace +dumps. If tracing is not yet enabled, the trace size is automatically +set to a (small) default value. +.IP \fBdebug\ level\ \fIlevel\fP +Sets the default debug level to \fIlevel\fP. \fIlevel\fP can be any of +\fBdebug\fP, \fBinfo\fP, \fBwarn\fP, \fBerror\fP, and \fBfatal\fP. Only +messages with the same or a higher priority than the debug level are printed. +Note that +the command-line option \fB\-d\fP generates even more output (e.g. hexdumps +of all packets passing between \fBatmsigd\fP and the network) than +\fBdebug level debug\fP. +.IP \fBdebug\ log\ \fIpath\fP +Specifies the file to which \fBatmsigd\fP writes logging messages. When +using the special file name \fBsyslog\fP, messages are send to the +system logger instead. Log messages are written to standard output if no log +file is specified. Writing to standard output can also be explicitly requested +by using the special file name \fBstderr\fP. +.IP \fBdebug\ trace\ \fP[\fInumber\fP] +Enables tracing and optionally sets the number of entries that should be +kept in the trace buffer. A (small) default is used if the number is +omitted. +.IP \fBio\ level\ \fIlevel\fP +Sets the debug level for IO-related messages to \fIlevel\fP. +.IP \fBio\ qos\ \fIqos\fP +Configures the signaling VC to use the specified QOS (see qos(7) for the +syntax). By default, UBR at link speed is used on the signaling VC. +.IP \fBio\ vc\ \fP[\fIitf\fB.\fP]\fIvpi\fB.\fIvci\fP +Uses the specified VC for signaling messages instead of the usual 0.0.5. +.IP \fBsaal\ level\ \fIlevel\fP +Sets the debug level for messages related to SAAL (i.e. SSCF and SSCOP) to +\fIlevel\fP. +.IP \fBsig\ level\ \fIlevel\fP +Sets the debug level for messages related to signaling (Q.2931 or ISP) to +\fIlevel\fP. +.IP \fBsig\ mode\ \fImode\fP +Set the mode of operation. The following modes are available: \fBuser\fP for +the user side, \fBnetwork\fP for the network side, and \fBswitch\fP for +operation in a switch. The default behaviour is \fBuser\fP. +.IP \fBsig\ uni30\fP +Use UNI 3.0 signaling. This option is not implemented. The signaling mode +must be configured at compile time. +.IP \fBsig\ uni31\fP +Use UNI 3.1 signaling. This option is not implemented. The signaling mode +must be configured at compile time. +.IP \fBsig\ vpci\ \fIvpci\fB\ itf\ \fIitf\fP +Sets up a very simplistic type of routing. All calls with VPCI values +equal to or greater than \fIvpci\fP will be routed to \fIitf\fP, and their +VPI values will be set to the signaled VPCI minus \fIvpci\fP. Multiple +\fBsig vpci\fP entries can be used to support an arbitrary number of +interfaces. +Example: with \fBsig vpci 4 itf 1\fP, a call signaled for +VPCI/VCI 0.x is routed to 0.0.x, a call signaled for 6.y is routed to +1.2.y, etc. +.IP \fBpolicy\ level\ \fIlevel\fP +Sets the debug level for messages related to policy decisions to \fIlevel\fP. +.IP \fBpolicy\ \fIdecision\ direction\ address\fP +Takes the specified \fIdecision\fP for calls from or to \fIaddress\fP. +\fIdecision\fP can be either \fBallow\fP or \fBreject\fP. \fIdirection\fP is +either \fBfrom\fP or \fBto\fP. The \fIaddress\fP may be wildcarded by +prepending a slash and the number of significant bits (NSAP) or digits (E.164). +The rules are searched in the order in which they appear in \fBatmsigd.conf\fP +until the first match. If no rule matches, the call is allowed. +.P +When setting multiple parameters in the same group, the group name doesn't +have to be repeated if it is followed by the parameters in curly braces. +Example: +.nf +.sp + debug { + level warn + dump /var/tmp + log syslog + trace 100 + } +.sp +.fi +.P +Line breaks can be inserted in \fBatmsigd.conf\fP wherever spaces or tabs +are allowed. Everything between a `#' and the end of the line is considered +a comment. The `#' character cannot be escaped. +.P +If an option is specified in \fBatmsigd.conf\fP and on the command +line, the command line has priority. +.COMPATIBILITY +For historical reasons, synonyms (e.g. abbreviated or long forms) exist for +most keywords. Future versions of \fBatmsigd\fP will only recognize the +syntax described on this man page. +.SH AUTHOR +Werner Almesberger, EPFL ICA +.SH "SEE ALSO" +atmsigd(8), qos(7), syslogd(8) +.\"{{{}}} diff -ur --new-file old/atm/sigd.new/cfg.l new/atm/sigd.new/cfg.l --- old/atm/sigd.new/cfg.l Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/cfg.l Thu Aug 20 23:02:30 1998 @@ -0,0 +1,104 @@ +%{ +/* cfg.l - configuration language */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include + +#include "atm.h" + +#include "y.tab.h" + + +static int lineno = 1; +static int token; /* f@#%ing flex doesn't grok return after BEGIN */ + + +void yyerror(const char *s); +void yywarn(const char *s); + +%} + +%s N +%s P + +%% + BEGIN(N); + +level return TOK_LEVEL; +debug return TOK_DEBUG; +info return TOK_INFO; +warn return TOK_WARN; +error return TOK_ERROR; +fatal return TOK_FATAL; +sig return TOK_SIG; +uni30 return TOK_UNI30; +uni31 return TOK_UNI31; +uni40 return TOK_UNI40; +[qQ].2963.1 return TOK_Q2963_1; +mode return TOK_MODE; +user return TOK_USER; +network return TOK_NET; +switch return TOK_SWITCH; +saal return TOK_SAAL; +vc return TOK_VC; +io return TOK_IO; +itf return TOK_ITF; +vpci return TOK_VPCI; +pcr return TOK_PCR; +policy return TOK_POLICY; +allow return TOK_ALLOW; +reject return TOK_REJECT; +entity return TOK_ENTITY; +default return TOK_DEFAULT; +dump { BEGIN(P); + token = TOK_DUMP_DIR; } +log { BEGIN(P); + token = TOK_LOGFILE; } +qos { BEGIN(P); /* syntacticly close to a path */ + token = TOK_QOS; } +from { BEGIN(P); /* syntacticly close to a path */ + token = TOK_FROM; } +to { BEGIN(P); /* syntacticly close to a path */ + token = TOK_TO; } +route { BEGIN(P); /* syntacticly close to a path */ + token = TOK_ROUTE; } +trace return TOK_TRACE; +[0-9]+ { char *end; + yylval.num = strtoul(yytext,&end,10); + if (*end) yyerror("invalid number"); + return TOK_NUMBER; } +[0-9]+\.[0-9]+(\.[0-9]+)? { + if (text2atm(yytext,(struct sockaddr *) &yylval.pvc, + sizeof(yylval.pvc),T2A_PVC) < 0) + yyerror("invalid signaling channel"); + return TOK_PVC; + } +

[^\t\n ]+ { BEGIN(N); + yylval.str = strdup(yytext); /* tiny leak ... */ + if (!yylval.str) { + perror("strdup"); + exit(1); + } + return token; } +\n?[\t ]* lineno += *yytext == '\n'; +#[^\n]*\n lineno++; +. return *yytext; + +%% + +void yywarn(const char *s) +{ + fprintf(stderr,"line %d: %s near \"%s\"\n",lineno,s,yytext); +} + + +void yyerror(const char *s) +{ + yywarn(s); + exit(1); +} diff -ur --new-file old/atm/sigd.new/cfg.y new/atm/sigd.new/cfg.y --- old/atm/sigd.new/cfg.y Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/cfg.y Fri Aug 21 00:39:20 1998 @@ -0,0 +1,374 @@ +%{ +/* cfg.y - configuration language */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include + +#include "atm.h" +#include "atmd.h" + +#include "proto.h" +#include "io.h" +#include "trace.h" +#include "policy.h" + + +static RULE *rule; +static SIG_ENTITY *curr_sig = &_entity; + + +static int hex2num(char digit) +{ + if (isdigit(digit)) return digit-'0'; + if (islower(digit)) return toupper(digit)-'A'+10; + return digit-'A'+10; +} + + +static void put_address(char *address) +{ + char *mask; + + mask = strchr(address,'/'); + if (mask) *mask++ = 0; + if (text2atm(address,(struct sockaddr *) &rule->addr,sizeof(rule->addr), + T2A_SVC | T2A_WILDCARD | T2A_NAME | T2A_LOCAL) < 0) { + yyerror("invalid address"); + return; + } + if (!mask) rule->mask = -1; + else rule->mask = strtol(mask,NULL,10); + add_rule(rule); +} + +%} + +%union { + int num; + char *str; + struct sockaddr_atmpvc pvc; +}; + + +%token TOK_LEVEL TOK_DEBUG TOK_INFO TOK_WARN TOK_ERROR TOK_FATAL +%token TOK_SIG TOK_UNI30 TOK_UNI31 TOK_UNI40 TOK_Q2963_1 TOK_SAAL +%token TOK_VC TOK_IO TOK_MODE TOK_USER TOK_NET TOK_SWITCH TOK_VPCI +%token TOK_ITF TOK_PCR TOK_TRACE TOK_POLICY TOK_ALLOW TOK_REJECT +%token TOK_ENTITY TOK_DEFAULT +%token TOK_NUMBER +%token TOK_DUMP_DIR TOK_LOGFILE TOK_QOS TOK_FROM TOK_TO TOK_ROUTE +%token TOK_PVC + +%type level opt_trace_size action + +%% + +all: + global local + ; + +global: + | item global + ; + +local: + | entity local + ; + +item: + TOK_LEVEL level + { + set_verbosity(NULL,$2); + } + | TOK_SIG sig + | TOK_SAAL saal + | TOK_IO io + | TOK_DEBUG debug + | TOK_POLICY policy + ; + +entity: + TOK_ENTITY TOK_PVC + { + SIG_ENTITY *sig,**walk; + + if (atmpvc_addr_in_use(_entity.signaling_pvc)) + yyerror("can't use io vc and entity ... in the same " + "configuration"); + if (entities == &_entity) entities = NULL; + for (sig = entities; sig; sig = sig->next) + if (atm_equal((struct sockaddr *) &sig->signaling_pvc, + (struct sockaddr *) &$2,0,0)) + yyerror("duplicate PVC address %d.%d.%d",S_PVC(sig)); + curr_sig = alloc_t(SIG_ENTITY); + *curr_sig = _entity; + curr_sig->signaling_pvc = $2; + curr_sig->next = NULL; + for (walk = &entities; *walk; walk = &(*walk)->next); + *walk = curr_sig; + } + opt_options + ; + +opt_options: + | '{' options '}' + ; + +options: + | option options + ; + +option: + TOK_VPCI TOK_NUMBER TOK_ITF TOK_NUMBER + { + enter_vpci(curr_sig,$2,$4); + } + | TOK_MODE mode + | TOK_QOS + { + curr_sig->sig_qos = $1; + } + | TOK_ROUTE + { + struct sockaddr_atmsvc addr; + char *mask; + + mask = strchr($1,'/'); + if (mask) *mask++ = 0; + if (text2atm($1,(struct sockaddr *) &addr,sizeof(addr), + T2A_SVC | T2A_WILDCARD | T2A_NAME | T2A_LOCAL) < 0) { + yyerror("invalid address"); + return; + } + add_route(curr_sig,&addr,mask ? strtol(mask,NULL,10) : INT_MAX); + } + | TOK_DEFAULT + { + add_route(curr_sig,NULL,0); + } + ; + +sig: + sig_item + | '{' sig_items '}' + ; + +sig_items: + | sig_item sig_items + ; + +saal: + saal_item + | '{' saal_items '}' + ; + +saal_items: + | saal_item saal_items + ; + +io: + io_item + | '{' io_items '}' + ; + +io_items: + | io_item io_items + ; + +debug: + debug_item + | '{' debug_items '}' + ; + +debug_items: + | debug_item debug_items + ; + +policy: + policy_item + | '{' policy_items '}' + ; + +policy_items: + | policy_item policy_items + ; + +sig_item: + TOK_LEVEL level + { + set_verbosity("UNI",$2); + set_verbosity("KERNEL",$2); + set_verbosity("SAP",$2); + } + | TOK_VPCI TOK_NUMBER TOK_ITF TOK_NUMBER + { + enter_vpci(curr_sig,$2,$4); + } + | TOK_UNI30 + { +#ifndef UNI30 + yyerror("Sorry, not supported yet"); +#endif + } + | TOK_UNI31 + { +#ifndef UNI31 + yyerror("Sorry, not supported yet"); +#endif + } + | TOK_UNI40 + { +#ifndef UNI40 + yyerror("Sorry, not supported yet"); +#endif + } + | TOK_Q2963_1 + { +#ifndef Q2963_1 + yyerror("Sorry, not supported yet"); +#endif + } + | TOK_NET + { + yywarn("sig net is obsolete, please use sig mode net instead"); + curr_sig->mode = sm_net; + } + | TOK_MODE mode + ; + +saal_item: + TOK_LEVEL level + { + set_verbosity("SSCF",$2); + set_verbosity("SSCOP",$2); + } + ; + +io_item: + TOK_LEVEL level + { + set_verbosity("IO",$2); + } + | TOK_VC TOK_PVC + { + curr_sig->signaling_pvc = $2; + } + | TOK_PCR TOK_NUMBER + { + yywarn("io pcr is obsolete, please use io qos instead"); + curr_sig->sig_pcr = $2; + } + | TOK_QOS + { + curr_sig->sig_qos = $1; + } + ; + +debug_item: + TOK_LEVEL level + { + set_verbosity(NULL,$2); + } + | TOK_DUMP_DIR + { + dump_dir = $1; + if (!trace_size) trace_size = DEFAULT_TRACE_SIZE; + } + | TOK_LOGFILE + { + set_logfile($1); + } + | TOK_TRACE opt_trace_size + { + trace_size = $2; + } + ; + +opt_trace_size: + { + $$ = DEFAULT_TRACE_SIZE; + } + | TOK_NUMBER + { + $$ = $1; + } + ; + +level: + TOK_DEBUG + { + $$ = DIAG_DEBUG; + } + | TOK_INFO + { + $$ = DIAG_INFO; + } + | TOK_WARN + { + $$ = DIAG_WARN; + } + | TOK_ERROR + { + $$ = DIAG_ERROR; + } + | TOK_FATAL + { + $$ = DIAG_FATAL; + } + ; + +mode: + TOK_USER + { + curr_sig->mode = sm_user; + } + | TOK_NET + { + curr_sig->mode = sm_net; + } + | TOK_SWITCH + { + curr_sig->mode = sm_switch; + } + ; + +policy_item: + TOK_LEVEL level + { + set_verbosity("POLICY",$2); + } + | action + { + rule = alloc_t(RULE); + rule->type = $1; + } + direction + ; + +action: + TOK_ALLOW + { + $$ = ACL_ALLOW; + } + | TOK_REJECT + { + $$ = ACL_REJECT; + } + ; + +direction: + TOK_FROM + { + rule->type |= ACL_IN; + put_address($1); + } + | TOK_TO + { + rule->type |= ACL_OUT; + put_address($1); + } + ; diff -ur --new-file old/atm/sigd.new/io.c new/atm/sigd.new/io.c --- old/atm/sigd.new/io.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/io.c Thu Aug 20 23:05:40 1998 @@ -0,0 +1,379 @@ +/* io.c - I/O operations */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* linux/atmsvc.h includes linux/atm.h */ +#include + +#include "atm.h" +#include "atmd.h" +#include "uni.h" +#include "pdu.h" + +#include "proto.h" +#include "io.h" +#include "trace.h" + + +#define COMPONENT "IO" + + +struct timeval now; + +int stop = 0; + +static int kernel = -1; +static int need_connect = 0; /* non-zero if connection to kernel isn't + bi-directional yet */ + + +/* ----- kernel interface -------------------------------------------------- */ + + +static int open_kernel(void) +{ + int s; + + if ((s = socket(PF_ATMSVC,SOCK_DGRAM,0)) < 0) { + perror("socket"); + return -1; + } + if (ioctl(s,ATMSIGD_CTRL,0) < 0) { + perror("ioctl ATMSIGD_CTRL"); + return -1; + } + return s; +} + + +void open_unix(const char *path) +{ + kernel = un_create(path,0600); + if (kernel < 0) + diag(COMPONENT,DIAG_FATAL,"un_create %s: %s",path,strerror(errno)); + need_connect = 1; +} + + +static void recv_kernel(void) +{ + static unsigned char buffer[sizeof(struct atmsvc_msg)+1]; + int size; + + if (!need_connect) size = read(kernel,buffer,sizeof(buffer)); + else { + size = un_recv_connect(kernel,buffer,sizeof(buffer)); + need_connect = 0; + } + if (size < 0) { + diag(COMPONENT,DIAG_ERROR,"read kernel: %s",strerror(errno)); + return; + } + if (size != sizeof(struct atmsvc_msg)) + diag(COMPONENT,DIAG_FATAL,"kernel message size %d != %d",size, + sizeof(struct atmsvc_msg)); + trace_kernel("FROM KERNEL",(struct atmsvc_msg *) buffer); + from_kernel((struct atmsvc_msg *) buffer,size); +} + + +void to_kernel(struct atmsvc_msg *msg) +{ + int wrote; + + diag("KERNEL",DIAG_DEBUG,"TO KERNEL: %s (%d) for 0x%lx/0x%lx", + as_name[msg->type],msg->reply,msg->vcc,msg->listen_vcc); + /* should be "IO" ... */ + trace_kernel("TO KERNEL",msg); + wrote = write(kernel,msg,sizeof(*msg)); + if (wrote == sizeof(*msg)) return; + if (wrote < 0) { + perror("kernel write"); + return; + } + diag(COMPONENT,DIAG_ERROR,"bad kernel write: wanted %d, wrote %d", + sizeof(*msg),wrote); +} + + +static void close_kernel(void) +{ + (void) close(kernel); /* may get major complaints from the kernel ... */ +} + + +/* ----- signaling interface ----------------------------------------------- */ + + +static int open_signaling(SIG_ENTITY *sig) +{ + struct atm_qos qos; + int s; + + if ((s = socket(PF_ATMPVC,SOCK_DGRAM,0)) < 0) { + perror("socket"); + return -1; + } + memset(&qos,0,sizeof(qos)); + qos.aal = ATM_AAL5; + qos.rxtp.max_sdu = qos.txtp.max_sdu = MAX_Q_MSG; + if (sig->sig_qos) { + if (text2qos(sig->sig_qos,&qos,T2Q_DEFAULTS) < 0) { + fprintf(stderr,"invalid qos: %s\n",sig->sig_qos); + return -1; + } + } + else { + if (sig->sig_pcr == -1) + qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_UBR; + else { + qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_CBR; + qos.rxtp.min_pcr = qos.txtp.min_pcr = sig->sig_pcr; + } + } + if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) { + perror("setsockopt SO_ATMQOS"); + return -1; + } + sig->signaling_pvc.sap_family = AF_ATMPVC; + if (bind(s,(struct sockaddr *) &sig->signaling_pvc, + sizeof(sig->signaling_pvc)) < 0) { + perror("bind"); + return -1; + } + return s; +} + + +static void recv_signaling(SIG_ENTITY *sig) +{ + static unsigned char buffer[MAX_Q_MSG]; + int size; + + size = read(sig->signaling,buffer,MAX_Q_MSG); + if (size < 1) { + perror("read signaling"); + return; + } + diag(COMPONENT,DIAG_DEBUG,"FROM NET (%d.%d.%d): %s PDU (%d bytes)", + S_PVC(sig),pdu_name[size > 3 && !(size & 3) ? buffer[size-4] & 0xf : 0], + size); + if (debug) diag_dump(COMPONENT,DIAG_DEBUG,NULL,buffer,size); + from_net(sig,buffer,size); +} + + +void to_net(SIG_ENTITY *sig,void *msg,int size) +{ + int wrote; + + diag(COMPONENT,DIAG_DEBUG,"TO NET (%d.%d.%d): %s PDU (%d bytes)",S_PVC(sig), + pdu_name[size > 3 && !(size & 3) ? ((unsigned char *) msg)[size-4] & 0xf + : 0],size); + if (debug) diag_dump(COMPONENT,DIAG_DEBUG,NULL,msg,size); + wrote = write(sig->signaling,msg,size); + if (wrote == size) return; + if (wrote < 0) { + perror("signaling write"); + return; + } + diag(COMPONENT,DIAG_WARN,"bad signaling write: wanted %d, wrote %d",size, + wrote); +} + + +static void close_signaling(SIG_ENTITY *sig) +{ + (void) close(sig->signaling); +} + + +/* ----- addresses --------------------------------------------------------- */ + + +LOCAL_ADDR local_addr[MAX_LOCAL_ADDRS+1]; + + +struct sockaddr_atmsvc *get_local(int itf) +{ + LOCAL_ADDR *local; + + for (local = local_addr; local->state != ls_unused; local++) + if (local->state == ls_same && atmsvc_addr_in_use(local->addr) && + (local->itf == itf || itf == ATM_ITF_ANY)) return &local->addr; + return NULL; +} + + +int get_addr(int itf) +{ + struct atmif_sioc req; + struct sockaddr_atmsvc buffer[MAX_LOCAL_ADDRS]; + LOCAL_ADDR *from,*to; + int addrs,i; + + for (from = to = local_addr; from->state != ls_unused; from++) + if (from->state != ls_removed) { + from->state = itf == from->itf ? ls_removed : ls_same; + *to++ = *from; + } + req.number = itf; + req.arg = buffer; + req.length = sizeof(buffer); + if (ioctl(entities->signaling,ATM_GETADDR,&req) < 0) + diag(COMPONENT,DIAG_FATAL,"ioctl ATM_GETADDR yields \"%s\"", + strerror(errno)); + addrs = req.length/sizeof(struct sockaddr_atmsvc); + for (i = 0; i < addrs; i++) { + for (from = local_addr; from->state != ls_unused; from++) + if (from->itf == itf && atm_equal((struct sockaddr *) buffer+i, + (struct sockaddr *) &from->addr,0,0)) break; + if (from->state != ls_unused) from->state = ls_same; + else if (to == local_addr+MAX_LOCAL_ADDRS-1) + diag(COMPONENT,DIAG_WARN,"local address table overflow"); + else { + to->state = ls_added; + to->itf = itf; + to->addr = buffer[i]; + to++; + } + } + to->state = ls_unused; + return addrs; +} + + +/* ----- common part ------------------------------------------------------- */ + + +int open_all(void) +{ + SIG_ENTITY *sig,*purge; + + if (kernel == -1) kernel = open_kernel(); + if (kernel < 0) return -1; + for (sig = entities; sig; sig = sig->next) { + sig->signaling = open_signaling(sig); + if (sig->signaling < 0) { + for (purge = entities; purge != sig; purge = purge->next) + close_signaling(purge); + close_kernel(); + return -1; + } + } + local_addr[0].state = ls_unused; + return 0; +} + + +void close_all(void) +{ + SIG_ENTITY *sig; + + close_kernel(); + for (sig = entities;sig; sig = sig->next) close_signaling(sig); +} + + +void init_current_time(void) +{ + gettimeofday(&now,NULL); +} + + +void poll_loop(void) +{ + SIG_ENTITY *sig; + fd_set perm,set; + int fds,ret; + + FD_ZERO(&perm); + FD_SET(kernel,&perm); + fds = kernel+1; + for (sig = entities; sig; sig = sig->next) { + FD_SET(sig->signaling,&perm); + if (fds <= sig->signaling) fds = sig->signaling+1; + } + gettimeofday(&now,NULL); + while (!stop) { + set = perm; + poll_signals(); + /* + * Here we have a small race condition: if a signal is delivered after + * poll_signals tests for it but before select sleeps, we miss that + * signal. If it is sent again, we're of course likely to get it. This + * isn't worth fixing, because those signals are only used for + * debugging anyway. + */ + ret = select(fds,&set,NULL,NULL,next_timer()); + if (ret < 0) { + if (errno != EINTR) perror("select"); + } + else { + diag(COMPONENT,DIAG_DEBUG,"----------"); + gettimeofday(&now,NULL); + if (FD_ISSET(kernel,&set)) recv_kernel(); + for (sig = entities; sig; sig = sig->next) + if (FD_ISSET(sig->signaling,&set)) recv_signaling(sig); + expire_timers(); + /* expire timers after handling messges to make sure we don't + time out unnecessarily because of scheduling delays */ + } + } +} + + +/* + * The allocation strategy could be improved as follows: we should try + * vci = prev_vci++ first and only resort to ATM_VCI_ANY if that fails several + * times (and we should actually skip over those which are in use by SVCs. This + * way we avoid using VCIs that just became available. Doing it "right" seems + * to be getting complex, though. + */ + + +int get_pvc(int itf,int *vci) +{ + struct sockaddr_atmpvc addr; + struct atm_qos qos; + int s,error; + + if ((s = socket(PF_ATMPVC,SOCK_DGRAM,0)) < 0) + diag(COMPONENT,DIAG_FATAL,"get_pvc: %s",strerror(errno)); + memset(&qos,0,sizeof(qos)); + qos.aal = ATM_AAL5; + qos.rxtp.traffic_class = qos.txtp.traffic_class = ATM_UBR; + qos.rxtp.max_sdu = qos.txtp.max_sdu = 1; /* smallest possible SDU size */ + if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) + diag(COMPONENT,DIAG_FATAL,"setsockopt SO_ATMQOS: %s",strerror(errno)); + memset(&addr,0,sizeof(addr)); + addr.sap_family = AF_ATMPVC; + addr.sap_addr.itf = itf; + addr.sap_addr.vpi = 0; /* @@@ */ + addr.sap_addr.vci = ATM_VCI_ANY; + error = 0; + if (bind(s,(struct sockaddr *) &addr,sizeof(addr)) < 0) error = errno; + else { + int size; + + size = sizeof(addr); + if (getsockname(s,(struct sockaddr *) &addr,&size) < 0) + diag(COMPONENT,DIAG_FATAL,"get_pvc: %s",strerror(errno)); + *vci = addr.sap_addr.vci; + return s; + } + (void) close(s); + return -error; +} diff -ur --new-file old/atm/sigd.new/io.h new/atm/sigd.new/io.h --- old/atm/sigd.new/io.h Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/io.h Thu Aug 20 19:50:44 1998 @@ -0,0 +1,43 @@ +/* io.h - I/O operations */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#ifndef IO_H +#define IO_H + +#include +#include +#include + +#include "proto.h" + + +#define MAX_LOCAL_ADDRS 128 + + +typedef struct { + enum { ls_unused,ls_added,ls_removed,ls_same } state; + int itf; + struct sockaddr_atmsvc addr; +} LOCAL_ADDR; + + +extern LOCAL_ADDR local_addr[]; + + +int open_all(void); +void open_unix(const char *name); +void init_current_time(void); +void poll_loop(void); +void close_all(void); + +void to_kernel(struct atmsvc_msg *msg); +void to_net(SIG_ENTITY *sig,void *msg,int size); + +struct sockaddr_atmsvc *get_local(int itf); +int get_addr(int itf); + +int get_pvc(int itf,int *vci); + +#endif diff -ur --new-file old/atm/sigd.new/kernel.c new/atm/sigd.new/kernel.c --- old/atm/sigd.new/kernel.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/kernel.c Thu Aug 20 23:58:42 1998 @@ -0,0 +1,574 @@ +/* kernel.c - Processing of incoming kernel messages */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include +#include /* linux/atmsvc.h includes linux/atm.h */ +#include +#include + +#include "atm.h" +#include "atmd.h" +#include "uni.h" +#include "qlib.h" +#include + +#include "proto.h" +#include "sap.h" +#include "io.h" +#include "policy.h" +#include "timeout.h" + + +#define COMPONENT "KERNEL" + + +static int send_setup(SOCKET *sock) +{ + static unsigned long call_ref = 0; + struct sockaddr_atmsvc *local; + SOCKET *walk; + Q_DSC dsc; + int error,size; + + do { + if (++call_ref == 0x7fffff) call_ref = 1; + for (walk = sockets; walk; walk = walk->next) + if (walk->call_ref == call_ref) break; + } + while (walk); + sock->call_ref = call_ref; + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_call_ref,call_ref); + q_assign(&dsc,QF_msg_type,ATM_MSG_SETUP); + q_assign(&dsc,QF_aal_type,5); /* AAL 5 */ +#ifdef UNI30 + q_assign(&dsc,QF_aal_mode,1); /* Message mode - LANE seems to really want + this */ +#endif + q_assign(&dsc,QF_sscs_type,0); /* unspecified - LANE wants this */ + error = sap_encode(&dsc,&sock->remote,&sock->sap,&sock->qos); + q_assign(&dsc,QF_bearer_class,16); /* BCOB-X */ +#if defined(UNI30) || defined(ALLOW_UNI30) + q_assign(&dsc,QF_trans_cap,ATM_TC_VBR_NRT_R00); + /* force presence - UNI 3.0 wants this */ +#endif + q_assign(&dsc,QF_upcc,0); /* p2p */ +#ifndef UNI30 + q_assign(&dsc,QF_qos_cs,Q2931_CS_ITU); +#endif + q_assign(&dsc,QF_qos_fw,0); /* QOS 0 */ + q_assign(&dsc,QF_qos_bw,0); + local = NULL; + if (atmsvc_addr_in_use(sock->local)) local = &sock->local; + else { + sock->sig = route_call(&sock->remote); + if (!sock->sig) { + if (!error) error = -EHOSTUNREACH; + } + else { + local = get_local(sock->sig->signaling_pvc.sap_addr.itf); + if (!local) local = get_local(ATM_ITF_ANY); + if (local) sock->local = *local; + else if (!error) { + error = -EADDRNOTAVAIL; + diag(COMPONENT,DIAG_ERROR,"no local address"); + } + } + } + if (local) + if (*local->sas_addr.pub) { + q_assign(&dsc,QF_cgpn_plan,ATM_NP_E164); + q_assign(&dsc,QF_cgpn_type,ATM_TON_INTRNTNL); + q_write(&dsc,QF_cgpn,(void *) local->sas_addr.pub, + strlen(local->sas_addr.pub)); + } + else { + q_assign(&dsc,QF_cgpn_plan,ATM_NP_AEA); + q_assign(&dsc,QF_cgpn_type,ATM_TON_UNKNOWN); + q_write(&dsc,QF_cgpn,(void *) local->sas_addr.prv,ATM_ESA_LEN); + } + if (sock->sig->mode != sm_user) { + if (!atmpvc_addr_in_use(sock->pvc)) { + int vpci,vci; + + if (sock->sig->mode == sm_switch) + diag(COMPONENT,DIAG_FATAL,"No CI allocator (use -A)"); + vpci = 0; + sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); + sock->pvc.sap_addr.vpi = vpci; + vci = get_vci(sock->pvc.sap_addr.itf); + if (vci < 0) { + (void) q_close(&dsc); + return vci; + } + sock->pvc.sap_addr.vci = vci; + } + q_assign(&dsc,QF_vpi,sock->pvc.sap_addr.vpi); + q_assign(&dsc,QF_vci,sock->pvc.sap_addr.vci); + } + if ((size = q_close(&dsc)) < 0) error = -EINVAL; + else if (!error) to_signaling(sock->sig,q_buffer,size); + return error; +} + + +static int send_connect(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_CONNECT); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if (sock->ep_ref >= 0) q_assign(&dsc,QF_ep_ref,sock->ep_ref); + q_assign(&dsc,QF_aal_type,5); +#ifdef UNI30 + q_assign(&dsc,QF_aal_mode,1); /* Message mode - LANE seems to really want + this */ +#endif + q_assign(&dsc,QF_sscs_type,0); /* unspecified - LANE wants this */ + q_assign(&dsc,QF_fw_max_sdu,sock->qos.rxtp.max_sdu); + q_assign(&dsc,QF_bw_max_sdu,sock->qos.txtp.max_sdu); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); + return 0; +} + + +#ifdef Q2963_1 + +static void send_modify_request(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_MODIFY_REQ); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if (sock->new_qos.txtp.traffic_class) + q_assign(&dsc,QF_fw_pcr_01,SELECT_TOP_PCR(sock->new_qos.txtp)); + if (sock->new_qos.rxtp.traffic_class) + q_assign(&dsc,QF_bw_pcr_01,SELECT_TOP_PCR(sock->new_qos.rxtp)); + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); +} + + +static void send_modify_ack(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_MODIFY_ACK); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if (sock->qos.rxtp.traffic_class != ATM_NONE && + sock->qos.rxtp.max_pcr < sock->new_qos.rxtp.max_pcr) { + q_assign(&dsc,QF_type_of_report,ATM_TOR_MOD_CONF); + START_TIMER(sock,T361); + } + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); +} + +#endif + + +static void dispatch(SOCKET *sock,struct atmsvc_msg *msg) +{ + int error; + + switch (msg->type) { + case as_bind: /* only in NULL state */ + if (sock) break; + if (msg->svc.sas_family != AF_ATMSVC) { + SEND_ERROR(msg->vcc,-EAFNOSUPPORT); + return; + } + if (!atmsvc_addr_in_use(msg->svc)) +#ifdef BE_PICKY_ABOUT_BINDING_LOCAL_WILDCARD_ADDRESSES + if (local_addr[0].state != ls_same) + SEND_ERROR(msg->vcc,-EADDRNOTAVAIL); + else +#endif + send_kernel(msg->vcc,0,as_okay,0,NULL,NULL, + &local_addr[0].addr,NULL,NULL); + else { + LOCAL_ADDR *walk; + + for (walk = local_addr; walk->state != ls_unused; walk++) + if (walk->state == ls_same && + atm_equal((struct sockaddr *) &walk->addr, + (struct sockaddr *) &msg->svc,(ATM_ESA_LEN-1)*8, + AXE_WILDCARD)) break; + if(*msg->svc.sas_addr.pub) + diag(COMPONENT,DIAG_DEBUG,"binding to E.164 address " + "%s\n",msg->svc.sas_addr.pub); + if (walk->state == ls_unused) { + SEND_ERROR(msg->vcc,-EADDRNOTAVAIL); + return; + } + sock->sig = get_sig_entity(walk->itf); + if (!sock->sig) { + diag(COMPONENT,DIAG_ERROR,"signaling entity lookup failed"); + SEND_ERROR(msg->vcc,-EADDRNOTAVAIL); + return; + } + send_kernel(msg->vcc,0,as_okay,0,NULL,NULL,NULL,NULL,NULL); + } + return; + case as_connect: /* NULL state only */ + if (sock) break; + if (!allow(&msg->svc,ACL_OUT)) { + SEND_ERROR(msg->vcc,-EACCES); + return; + } + sock = new_sock(msg->vcc); + sock->remote = msg->svc; + sock->qos = msg->qos; + sock->sap = msg->sap; + sock->state = ss_connecting; + sock->pvc = msg->pvc; + error = send_setup(sock); + if (error) { + SEND_ERROR(msg->vcc,error); + free_sock(sock); + return; + } + START_TIMER(sock,T303); + new_state(sock,ss_connecting); + return; + case as_accept: + if (sock->state == ss_zombie) { + SEND_ERROR(msg->vcc,-ECONNABORTED); /* -ERESTARTSYS ? */ + free_sock(sock); + return; + } + if (sock->state != ss_indicated && sock->state != ss_proceeding) + break; + if (sock->state == ss_indicated && sock->sig->mode != sm_user) + diag(COMPONENT,DIAG_FATAL,"No CI allocator (use -A)"); + error = send_connect(sock); + if (!error) { + START_TIMER(sock,T313); + new_state(sock,ss_accepting); + return; + } + SEND_ERROR(sock->id,error); + send_release(sock,0); /* @@@ */ + START_TIMER(sock,T308_1); + new_state(sock,ss_wait_rel); + return; + case as_reject: /* ZOMBIE, INDICATED, or PROCEEDING */ + switch (sock->state) { + case ss_indicated: + send_release_complete(sock->sig,sock->call_ref, + ATM_CV_CALL_REJ); + /* fall through */ + case ss_zombie: + free_sock(sock); + return; + case ss_proceeding: + send_release(sock,ATM_CV_CALL_REJ); + /* @@@ should use msg->reply */ + START_TIMER(sock,T308_1); + new_state(sock,ss_wait_rel); + return; + default: + break; + } + break; + case as_listen: /* NULL */ + if (sock) break; + if (msg->svc.sas_family != AF_ATMSVC) { + SEND_ERROR(msg->vcc,-EAFNOSUPPORT); + return; + } + if (msg->qos.aal != ATM_AAL5) { + SEND_ERROR(msg->vcc,-EINVAL); + return; + } + if (lookup_sap(&msg->svc,&msg->sap,&msg->qos,NULL,NULL,NULL,1)) { + SEND_ERROR(msg->vcc,-EADDRINUSE); + return; + } + sock = new_sock(msg->vcc); + sock->local = msg->svc; + sock->sap = msg->sap; + sock->qos = msg->qos; + send_kernel(sock->id,0,as_okay,0,NULL,NULL,NULL,NULL,NULL); + sock->state = ss_listening; + return; + case as_close: /* all but INDICATED, PROCEEDING, ZOMBIE, and WAIT_REL */ + if (sock && (sock->state == ss_indicated || + sock->state == ss_proceeding || sock->state == ss_zombie || + sock->state == ss_wait_rel)) break; + switch (sock ? sock->state : ss_null) { + case ss_listening: + send_close(sock); + if (sock->listen) new_state(sock,ss_listen_zombie); + else free_sock(sock); + return; + case ss_zombie: + send_close(sock); + /* fall through */ + case ss_wait_close: + free_sock(sock); + /* fall through */ + case ss_null: + case ss_rel_req: + return; + case ss_connecting: + case ss_accepting: +#ifdef Q2963_1 + case ss_mod_req: +#endif + STOP_TIMER(sock); + /* fall through */ +#ifdef Q2963_1 + case ss_mod_lcl: + case ss_mod_rcv: + case ss_mod_fin_ok: + case ss_mod_fin_fail: + case ss_mod_fin_ack: +#endif + case ss_connected: +#ifdef Q2963_1 + if (timer_handler(sock->conn_timer) == on_T361) + STOP_TIMER(sock); +#endif + if (sock->state == ss_connected) + diag(COMPONENT,DIAG_INFO,"Active close (CR 0x%06X)", + sock->call_ref); + send_release(sock, +#if defined(UNI31) || defined(UNI40) + ATM_CV_NORMAL_CLEAR +#else + ATM_CV_NORMAL_UNSPEC +#endif + ); + START_TIMER(sock,T308_1); + new_state(sock,ss_rel_req); + return; + case ss_rel_ind: + send_release_complete(sock->sig,sock->call_ref,0); /* @@@ */ + free_sock(sock); + return; + default: + break; + } + break; + case as_identify: + if (sock->state != ss_indicated && sock->state != ss_proceeding) + break; + if (!atmpvc_addr_in_use(msg->pvc)) { + if (sock->sig->mode == sm_switch) + diag(COMPONENT,DIAG_FATAL,"No CI allocator (use -A)"); + return; + } + if (sock->sig->mode == sm_net) + diag(COMPONENT,DIAG_FATAL,"CI allocation role conflict"); + sock->pvc = msg->pvc; + if (send_call_proceeding(sock)) + diag(COMPONENT,DIAG_FATAL,"s_c_p failed"); + new_state(sock,ss_proceeding); + return; +#ifdef Q2963_1 + case as_modify: + if (sock && (sock->state == ss_mod_lcl || + sock->state == ss_mod_req || sock->state == ss_mod_rcv || + sock->state == ss_mod_fin_ok || sock->state == ss_mod_fin_fail || + sock->state == ss_mod_fin_ack)) { + send_kernel(sock->id,0,as_okay,-EALREADY,NULL,NULL,NULL,NULL, + NULL); + return; + } + if (!sock || sock->state != ss_connected || !sock->owner) { + send_kernel(sock->id,0,as_okay,-EBADFD,NULL,NULL,NULL,NULL, + NULL); + return; + } + if (sock->qos.txtp.traffic_class != msg->qos.txtp.traffic_class || + sock->qos.rxtp.traffic_class != msg->qos.rxtp.traffic_class) { + /* @@@ may do more checking */ + send_kernel(sock->id,0,as_okay,-EINVAL,NULL,NULL,NULL,NULL, + NULL); + return; + } + sock->new_qos = msg->qos; + send_kernel(sock->id,0,as_modify,ATM_MF_INC_RSV | ATM_MF_DEC_SHP, + NULL,NULL,NULL,NULL,&msg->qos); + new_state(sock,ss_mod_lcl); + return; + case as_okay: + switch (sock ? sock->state : ss_null) { + case ss_mod_lcl: + send_modify_request(sock); + START_TIMER(sock,T360); + new_state(sock,ss_mod_req); + return; + case ss_mod_rcv: + send_modify_ack(sock); + sock->qos = sock->new_qos; + new_state(sock,ss_connected); + return; + case ss_mod_fin_ok: + send_kernel(sock->id,0,as_okay,0,NULL,NULL,NULL,NULL,NULL); + new_state(sock,ss_connected); + return; + case ss_mod_fin_fail: + send_kernel(sock->id,0,as_okay,sock->error,NULL,NULL,NULL, + NULL,NULL); + sock->error = 0; + /* fall through */ + case ss_mod_fin_ack: + new_state(sock,ss_connected); + /* fall through */ + default: + return; /* ignore stray as_okay */ + } + case as_error: + switch (sock ? sock->state : ss_null) { + case ss_mod_lcl: + send_kernel(sock->id,0,as_okay,msg->reply,NULL,NULL,NULL, + NULL,NULL); + new_state(sock,ss_connected); + return; + case ss_mod_rcv: + send_modify_reject(sock,ATM_CV_RES_UNAVAIL); + new_state(sock,ss_connected); + return; + case ss_mod_fin_ok: + diag(COMPONENT,DIAG_ERROR,"QOS commit failed"); + send_kernel(sock->id,0,as_okay,0,NULL,NULL,NULL,NULL,NULL); + /* @@@ clear call instead ? */ + new_state(sock,ss_connected); + return; + case ss_mod_fin_fail: + diag(COMPONENT,DIAG_ERROR,"QOS rollback failed"); + send_kernel(sock->id,0,as_okay,sock->error,NULL,NULL,NULL, + NULL,NULL); + sock->error = 0; + /* @@@ clear call instead ? */ + new_state(sock,ss_connected); + return; + case ss_mod_fin_ack: + /* @@@ maybe we should even clear the call now */ + diag(COMPONENT,DIAG_ERROR,"QOS commit failed"); + new_state(sock,ss_connected); + return; + default: + return; /* ignore stray as_error */ + } +#endif + default: + diag(COMPONENT,DIAG_WARN,"invalid message %d",(int) msg->type); + return; + } + diag(COMPONENT,DIAG_WARN,"message %s is incompatible with state %s (%d)", + as_name[msg->type],state_name[sock ? sock->state : ss_null], + (int) (sock ? sock->state : ss_null)); +} + + +static void dispatch_listen(SOCKET *sock,struct atmsvc_msg *msg) +{ + SOCKET *next; + + if (!sock) { + diag(COMPONENT,DIAG_WARN,"message %s is incompatible with state %s " + "(%d)",as_name[msg->type],state_name[ss_null],ss_null); + return; + } + if (!(next = sock->listen)) { + diag(COMPONENT,DIAG_WARN, + "socket 0x%lx got accept/reject/identify with empty listen queue", + msg->vcc); + return; + } + sock->listen = next->listen; + if (sock->state == ss_listen_zombie && !sock->listen) free_sock(sock); + next->listen = NULL; + next->id = msg->vcc; + dispatch(next,msg); +} + + +void itf_load(int itf) +{ + char buf[MAX_ATM_ADDR_LEN+1]; + LOCAL_ADDR *walk; + + (void) get_addr(itf); + for (walk = local_addr; walk->state != ls_unused; walk++) + if (walk->itf == itf) { + if (atm2text(buf,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) + &walk->addr,pretty) < 0) strcpy(buf,""); + switch (walk->state) { + case ls_added: + diag(COMPONENT,DIAG_INFO,"Added local ATM address %s at " + "itf %d",buf,itf); + walk->state = ls_same; + break; + case ls_removed: + diag(COMPONENT,DIAG_INFO,"Removed local ATM address %s at " + "itf %d",buf,itf); + walk->state = ls_unused; +/* + * This is probably wrong. What if we delete an entry from the middle of + * the list ? @@@ + */ + /* @@@ delete SVCs using that address ? */ + break; + default: + break; + } + } +} + + +void from_kernel(struct atmsvc_msg *msg,int size) +{ + void (*dispatcher)(SOCKET *,struct atmsvc_msg *); + SOCKET *curr; + + if (msg->type == as_itf_notify) { + itf_load(msg->pvc.sap_addr.itf); + return; + } + if (msg->type == as_terminate) { +#if 0 /* need to pass some ID ... @@@ */ + if (mode != sm_switch) { + diag(COMPONENT,DIAG_ERROR,"Ignoring as_terminate received in %s " + "mode",mode == sm_user ? "USER" : "NETWORK"); + return; + } + clear_all_calls(sig); +#endif + stop = 1; + diag(COMPONENT,DIAG_INFO,"Going down on as_terminate"); + /* + * Fix this - we need to shut it down more gracefully. + */ + return; + } + if (msg->listen_vcc && (msg->type == as_accept || msg->type == as_reject || + msg->type == as_identify)) { + dispatcher = dispatch_listen; + for (curr = sockets; curr; curr = curr->next) + if (msg->listen_vcc == curr->id && (curr->state == ss_listening || + curr->state == ss_listen_zombie)) + break; + } + else { + dispatcher = dispatch; + for (curr = sockets; curr; curr = curr->next) + if (msg->vcc == curr->id) break; + } + diag(COMPONENT,DIAG_DEBUG,"FROM KERNEL: %s for socket %p (0x%lx/0x%lx) " + "in state %s",as_name[msg->type],curr,msg->vcc,msg->listen_vcc, + state_name[curr ? curr->state : ss_null]); + dispatcher(curr,msg); +} diff -ur --new-file old/atm/sigd.new/mkmess.pl new/atm/sigd.new/mkmess.pl --- old/atm/sigd.new/mkmess.pl Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/mkmess.pl Wed Jan 31 19:12:06 1996 @@ -0,0 +1,16 @@ +#!/usr/bin/perl +while (<>) { + next if !/ATM_CV_/; + chop; + chop($_ = $_.<>) unless /\*\//; + s/\s+/ /g; + /ATM_CV_\S+\s+(\d+)\s+\/\*\s*(.*\S)\s*\*\//; + $map[$1] = $2; +} +print "/* THIS IS A MACHINE-GENERATED FILE. DO NOT EDIT ! */\n\n"; +print "const char *cause_text[] = {\n"; +for ($i = 0; $i < 128; $i++) { + print " \"".(defined $map[$i] ? $map[$i] : "unknown cause $i")."\"". + ($i == 127 ? "\n" : ",\n"); +} +print "};\n"; diff -ur --new-file old/atm/sigd.new/policy.c new/atm/sigd.new/policy.c --- old/atm/sigd.new/policy.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/policy.c Thu Apr 9 14:51:34 1998 @@ -0,0 +1,54 @@ +/* policy.c - Access control policies */ + +/* Written 1997,1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include + +#include "proto.h" /* for "pretty" */ +#include "policy.h" + + +#define COMPONENT "POLICY" + + +static RULE *rules = NULL; +static RULE **next = &rules; + + +void add_rule(RULE *rule) +{ + rule->hits = 0; + rule->next = NULL; + *next = rule; + next = &rule->next; +} + + +int allow(const struct sockaddr_atmsvc *addr,int direction) +{ + RULE *rule; + char buffer[MAX_ATM_ADDR_LEN+1]; + int count; + + if ((direction & (ACL_IN | ACL_OUT)) == (ACL_IN | ACL_OUT)) + diag(COMPONENT,DIAG_ERROR,"allow: ACL_IN && ACL_OUT"); + count = 0; + for (rule = rules; rule; rule = rule->next) { + count++; + if (!(rule->type & direction)) continue; + if (!atm_equal((struct sockaddr *) addr, + (struct sockaddr *) &rule->addr,rule->mask,AXE_PRVOPT | + (rule->mask == -1 ? 0 : AXE_WILDCARD))) continue; + rule->hits++; + if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) addr, + pretty) < 0) + strcpy(buffer,""); + diag(COMPONENT,DIAG_DEBUG,"Rule %d: %s call %s %s",count, + rule->type & ACL_ALLOW ? "allowed" : "rejected", + direction & ACL_IN ? "from" : "to",buffer); + return rule->type & ACL_ALLOW; + } + return 1; +} diff -ur --new-file old/atm/sigd.new/policy.h new/atm/sigd.new/policy.h --- old/atm/sigd.new/policy.h Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/policy.h Wed Oct 29 21:03:15 1997 @@ -0,0 +1,27 @@ +/* policy.h - Access control policies */ + +/* Written 1997 by Werner Almesberger, EPFL-LRC */ + + +#ifndef POLICY_H +#define POLICY_H + +#define ACL_ALLOW 1 +#define ACL_REJECT 2 +#define ACL_IN 4 +#define ACL_OUT 8 + + +typedef struct _rule { + int type; + struct sockaddr_atmsvc addr; + int mask; /* -1 for none */ + int hits; + struct _rule *next; +} RULE; + + +void add_rule(RULE *rule); +int allow(const struct sockaddr_atmsvc *addr,int direction); + +#endif diff -ur --new-file old/atm/sigd.new/proto.c new/atm/sigd.new/proto.c --- old/atm/sigd.new/proto.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/proto.c Fri Aug 21 00:04:03 1998 @@ -0,0 +1,437 @@ +/* proto.c - Common protocol functions and structures */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC */ + + +#include +#include +#include +#include +#include + +#include "atmd.h" +#include "uni.h" +#include "qlib.h" +#include + +#include "io.h" +#include "proto.h" +#include "sap.h" + + +#define COMPONENT "SIGD" + + +const char *state_name[] = { /* formatting aligned with STATE */ + "", "ss_null", "ss_listening", "ss_connecting", + "ss_connected", "ss_indicated", "ss_accepting", "ss_zombie", + "ss_wait_rel", "ss_wait_close","ss_rel_req", "ss_rel_ind", + "ss_proceeding","ss_listen_zombie", + "ss_mod_lcl", "ss_mod_req", "ss_mod_rcv", "ss_mod_fin_ack", + "ss_mod_fin_ok","ss_mod_fin_fail" + }; + +const char *cs_name[] = { + "NULL", "CALL_INIT", "", "OUT_PROC", + "", "", "CALL_PRES", "", + "CONN_REQ", "IN_PROC", "ACTIVE", "REL_REQ", + "REL_IND", "MOD_REQ", "MOD_RCV" }; + +const char *as_name[] = { "","as_bind","as_connect","as_accept", + "as_reject","as_listen","as_okay","as_error","as_indicate","as_close", + "as_itf_notify","as_modify","as_identify","as_terminate" }; + +const CALL_STATE state_map[] = { /* formatting aligned with STATE */ + cs_null, cs_null, cs_null, cs_call_init, + cs_active, cs_in_proc, cs_conn_req, cs_null, + cs_rel_req, cs_null, cs_rel_req, cs_rel_ind, + cs_in_proc, cs_null, +#ifdef Q2963_1 + cs_active, cs_mod_req, cs_mod_rcv, cs_active, + cs_active, cs_active +#endif + }; + +const PARTY_STATE eps_map[] = { + ps_null, ps_add_init, ps_null, ps_add_init, /* 0 */ + ps_null, ps_null, ps_add_recv, ps_null, /* 4 */ + ps_active, ps_add_recv, ps_active, ps_active, /* 8 */ + ps_active }; /*12 */ + + +SIG_ENTITY *entities = &_entity; +SOCKET *sockets = NULL; +unsigned char q_buffer[MAX_Q_MSG]; + + +SOCKET *new_sock(unsigned long id) +{ + SOCKET *sock; + + sock = alloc_t(SOCKET); + sock->sig = NULL; + sock->state = ss_invalid; + memset(&sock->pvc,0,sizeof(sock->pvc)); + sock->qos.txtp.traffic_class = sock->qos.rxtp.traffic_class = ATM_UBR; + sock->id = id; + memset(&sock->local,0,sizeof(sock->local)); + memset(&sock->remote,0,sizeof(sock->remote)); + memset(&sock->sap,0,sizeof(sock->sap)); + sock->error = 0; + sock->call_state = cs_null; + sock->ep_ref = -1; + sock->conn_timer = NULL; + sock->listen = NULL; + sock->next = sockets; + sockets = sock; + return sock; +} + + +void free_sock(SOCKET *sock) +{ + SOCKET **walk; + + diag(COMPONENT,DIAG_DEBUG,"freeing socket 0x%lx@%p",sock->id,sock); + for (walk = &sockets; *walk != sock; walk = &(*walk)->next); + if (!*walk) + diag(COMPONENT,DIAG_FATAL, + "INTERNAL ERROR: freeing non-existing socket 0x%lx",sock->id); + *walk = sock->next; + if (sock->conn_timer) { + diag(COMPONENT,DIAG_ERROR,"socket 0x%lx has timer (%p) running", + sock->id,sock->conn_timer->callback); + stop_timer(sock->conn_timer); + } + if (sock->listen) + diag(COMPONENT,DIAG_ERROR,"socket 0x%lx has non-empty listen queue", + sock->id); + sock->state = ss_invalid; + free(sock); +} + + +void new_state(SOCKET *sock,STATE state) +{ + diag(COMPONENT,DIAG_DEBUG,"socket 0x%lx enters state %s (UNI %s)", + sock->id,state_name[state],cs_name[state_map[state]]); + sock->state = state; + sock->call_state = state_map[state]; +} + + +SOCKET *lookup_sap(const struct sockaddr_atmsvc *addr, + const struct atm_sap *sap,const struct atm_qos *qos, + struct sockaddr_atmsvc *res_addr,struct atm_sap *res_sap, + struct atm_qos *res_qos,int exact_match) +{ + SOCKET *walk,*wildcard; + int new_wc; + + new_wc = !atmsvc_addr_in_use(*addr); + wildcard = NULL; + for (walk = sockets; walk; walk = walk->next) + if (walk->state == ss_listening && sap_compat(&walk->local, + addr,res_addr,&walk->sap,sap,res_sap,&walk->qos,qos,res_qos)) + if (atmsvc_addr_in_use(walk->local)) return walk; + else if (exact_match) { + if (new_wc) return walk; + } + else wildcard = walk; + return wildcard; +} + + +const char *mid2name(unsigned char mid) +{ + switch (mid) { + case ATM_MSG_NATIONAL: + return "National specific message escape"; + case ATM_MSG_SETUP: + return "SETUP"; + case ATM_MSG_ALERTING: + return "ALERTING"; + case ATM_MSG_CALL_PROC: + return "CALL_PROCEEDING"; + case ATM_MSG_CONNECT: + return "CONNECT"; + case ATM_MSG_CONN_ACK: + return "CONNECT_ACK"; + case ATM_MSG_RESTART: + return "RESTART"; + case ATM_MSG_RELEASE: + return "RELEASE"; + case ATM_MSG_REST_ACK: + return "REST_ACK"; + case ATM_MSG_REL_COMP: + return "REL_COMP"; + case ATM_MSG_NOTIFY: + return "NOTIFY"; + case ATM_MSG_STATUS_ENQ: + return "STATUS_ENQ"; + case ATM_MSG_STATUS: + return "STATUS"; + case ATM_MSG_ADD_PARTY: + return "ADD_PARTY"; + case ATM_MSG_ADD_PARTY_ACK: + return "ADD_PARTY_ACK"; + case ATM_MSG_ADD_PARTY_REJ: + return "ADD_PARTY_REJECT"; + case ATM_MSG_PARTY_ALERT: + return "PARTY_ALERTING"; + case ATM_MSG_DROP_PARTY: + return "DROP_PARTY"; + case ATM_MSG_DROP_PARTY_ACK: + return "DROP_PARTY_ACK"; + case ATM_MSG_MODIFY_REQ: + return "MODIFY_REQUEST"; + case ATM_MSG_MODIFY_ACK: + return "MODIFY_ACK"; + case ATM_MSG_MODIFY_REJ: + return "MODIFY_REJECT"; + case ATM_MSG_CONN_AVAIL: + return "CONN_AVAIL"; + case ATM_MSG_LEAF_FAILURE: + return "LEAF SETUP FAIL"; + case ATM_MSG_LEAF_REQUEST: + return "LEAF SETUP REQ"; + case ATM_MSG_RESERVED: + return "Reserved..."; + default: + return "???"; + } +} + + +void send_kernel(unsigned long vcc,unsigned long listen_vcc, + enum atmsvc_msg_type type,int reply,const struct sockaddr_atmpvc *pvc, + const struct sockaddr_atmsvc *svc,const struct sockaddr_atmsvc *local, + const struct atm_sap *sap,const struct atm_qos *qos) +{ + struct atmsvc_msg *msg; + + msg = alloc_t(struct atmsvc_msg); + msg->vcc = vcc; + msg->listen_vcc = listen_vcc; + msg->type = type; + msg->reply = reply; + if (pvc) msg->pvc = *pvc; + else memset(&msg->pvc,0,sizeof(msg->pvc)); + if (sap) msg->sap = *sap; + else memset(&msg->sap,0,sizeof(msg->sap)); + if (qos) msg->qos = *qos; + else memset(&msg->qos,0,sizeof(msg->qos)); + if (local) msg->local = *local; + else memset(&msg->local,0,sizeof(msg->local)); + if (svc) msg->svc = *svc; + else memset(&msg->svc,0,sizeof(msg->svc)); + to_kernel(msg); + free(msg); +} + + +void send_release(SOCKET *sock,unsigned char reason,...) +{ + va_list ap; + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_RELEASE); + q_assign(&dsc,QF_call_ref,sock->call_ref); + q_assign(&dsc,QF_cause,reason); + va_start(ap,reason); + switch (reason) { + case ATM_CV_TIMER_EXP: + { + char buf[4]; + + sprintf(buf,"%d",va_arg(ap,int)); + q_write(&dsc,QF_timer,buf,3); + break; + } + default: + } + va_end(ap); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); +} + + +void send_release_complete(SIG_ENTITY *sig,unsigned long call_ref, + unsigned char cause) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_REL_COMP); + q_assign(&dsc,QF_call_ref,call_ref); + if (cause) q_assign(&dsc,QF_cause,cause); /* @@@ more data */ + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); +} + + +void send_modify_reject(SOCKET *sock,unsigned char reason) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_MODIFY_REJ); + q_assign(&dsc,QF_call_ref,sock->call_ref); + q_assign(&dsc,QF_cause,reason); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); +} + + +void set_error(SOCKET *sock,int code) +{ + if (!sock->error) sock->error = code; +} + + +void send_close(SOCKET *sock) +{ + if (sock->error == 1234) diag(COMPONENT,DIAG_FATAL,"BUG! BUG! BUG!"); + send_kernel(sock->id,0L,as_close,sock->error,NULL,NULL,NULL,NULL,NULL); + sock->error = 1234; +} + + +void q_report(int severity,const char *msg,...) +{ + va_list ap; + + va_start(ap,msg); + vdiag("QMSG",severity,msg,ap); + va_end(ap); +} + + +int get_vci(int itf) +{ + SOCKET *walk; + int s,vci; + + s = get_pvc(itf,&vci); + if (s < 0) return s; + for (walk = sockets; walk; walk = walk->next) + if (walk->pvc.sap_addr.itf == itf && walk->pvc.sap_addr.vci == vci) { + vci = get_vci(itf); /* this recursion will keep all the busy ones + open until we return */ + break; + } + (void) close(s); + return vci; +} + + +void enter_vpci(SIG_ENTITY *sig,int vpci,int itf) +{ + VPCI *entry; + + for (entry = sig->vpcis; entry; entry = entry->next) + if (entry->vpci == vpci) { + diag(COMPONENT,DIAG_ERROR,"ignoring duplicate VPCI %d (itf %d)", + vpci,itf); + return; + } + entry = alloc_t(VPCI); + entry->vpci = vpci; + entry->itf = itf; + entry->next = sig->vpcis; + sig->vpcis = entry; +} + + +int get_itf(SIG_ENTITY *sig,int *vpci) +{ + VPCI *best,*walk; + + best = NULL; + for (walk = sig->vpcis; walk; walk = walk->next) + if (walk->vpci <= *vpci && (!best || best->vpci < walk->vpci)) + best = walk; + if (!best) return sig->signaling_pvc.sap_addr.itf; + *vpci -= best->vpci; + return best->itf; +} + + +void init_addr(SIG_ENTITY *sig) +{ + VPCI *walk; + + itf_load(sig->signaling_pvc.sap_addr.itf); + for (walk = sig->vpcis; walk; walk = walk->next) itf_load(walk->itf); +} + + +/* + * The following code is stolen from switch/route.c. Eventually, all this + * should move into a library. + */ + + +#include + + +typedef struct _route { + struct sockaddr_atmsvc addr; + int len; + SIG_ENTITY *sig; + struct _route *next; +} ROUTE; + + +static ROUTE *routes = NULL; + + +void add_route(SIG_ENTITY *sig,struct sockaddr_atmsvc *addr,int len) +{ + ROUTE *route; + + for (route = routes; route; route = route->next) + if (route->len == len && (!len || atm_equal((struct sockaddr *) addr, + (struct sockaddr *) &route->addr,len, + AXE_PRVOPT | (len == INT_MAX ? 0 : AXE_WILDCARD)))) + diag(COMPONENT,DIAG_FATAL,"duplicate route"); + route = alloc_t(ROUTE); + if (addr) route->addr = *addr; + route->len = len; + route->sig = sig; + route->next = routes; + routes = route; +} + + +SIG_ENTITY *route_call(struct sockaddr_atmsvc *addr) +{ + ROUTE *best,*route; + int best_len; + + if (!routes) return entities; + best = NULL; + best_len = -1; + for (route = routes; route; route = route->next) + if (route->len > best_len && (!route->len || + atm_equal((struct sockaddr *) addr,(struct sockaddr *) &route->addr, + route->len, + AXE_PRVOPT | (route->len == INT_MAX ? 0 : AXE_WILDCARD)))) { + if (route->len == INT_MAX) return route->sig; + best_len = route->len; + best = route; + } + return best ? best->sig : NULL; +} + + +SIG_ENTITY *get_sig_entity(int itf) +{ + SIG_ENTITY *sig; + + for (sig = entities; sig; sig = sig->next) + if (sig->signaling_pvc.sap_addr.itf == itf) return sig; + /* should also check for vpcis @@@ */ + return NULL; +} diff -ur --new-file old/atm/sigd.new/proto.h new/atm/sigd.new/proto.h --- old/atm/sigd.new/proto.h Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/proto.h Thu Aug 20 23:01:42 1998 @@ -0,0 +1,164 @@ +/* proto.h - Common protocol functions and structures */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC */ + + +#ifndef PROTO_H +#define PROTO_H + +#include +#include + +#include "atmsap.h" +#include "atmd.h" +#include "saal.h" + + +typedef enum { /* formatting aligned with state_map and others */ + ss_invalid, ss_null, ss_listening, ss_connecting, + ss_connected, ss_indicated, ss_accepting, ss_zombie, + ss_wait_rel, ss_wait_close, ss_rel_req, ss_rel_ind, + ss_proceeding, ss_listen_zombie, +#ifdef Q2963_1 + ss_mod_lcl, ss_mod_req, ss_mod_rcv, ss_mod_fin_ack, + ss_mod_fin_ok, ss_mod_fin_fail +#endif +} STATE; + +typedef enum { cs_null,cs_call_init,cs_out_proc = 3,cs_conn_req = 8, + cs_in_proc,cs_active,cs_rel_req,cs_rel_ind, +#ifdef Q2963_1 + cs_mod_req,cs_mod_rcv +#endif + } CALL_STATE; + +typedef enum { ps_null,ps_add_init,ps_add_recv = 6,ps_drop_init = 11, + ps_drop_recv,ps_active = 10 } PARTY_STATE; + +typedef enum { sm_unknown,sm_user,sm_net,sm_switch } SIGNALING_MODE; + +typedef struct _vpci { + int vpci; + int itf; + struct _vpci *next; +} VPCI; + +typedef struct _sig_entity { + int signaling; /* fd */ + SIGNALING_MODE mode; + int sig_pcr; /* @@@ remove soon */ + const char *sig_qos; + struct sockaddr_atmpvc signaling_pvc; + SAAL_DSC saal; + VPCI *vpcis; + struct _sig_entity *next; +} SIG_ENTITY; + +typedef struct _socket { + STATE state; + SIG_ENTITY *sig; + struct sockaddr_atmpvc pvc; + /* --- socket layer information ---------------------------------------- */ + unsigned long id; + struct sockaddr_atmsvc local; /* local address */ + struct sockaddr_atmsvc remote; /* remote address */ + struct atm_sap sap; /* SAP (BHLI and BLLI) */ + struct atm_qos qos; /* QOS parameters */ +#ifdef Q2963_1 + struct atm_qos new_qos; /* during modification */ + int owner; /* non-zero if connection owner */ +#endif + int error; /* error code for close */ + /* --- UNI information ------------------------------------------------- */ + CALL_STATE call_state; + unsigned long call_ref; /* bit 24 like when sending */ + short ep_ref; /* endpoint reference; -1 for p2p */ + TIMER *conn_timer; /* current connection timer */ + /* --- some meta-information ------------------------------------------- */ + struct _socket *listen; /* to pending connections, also used for "more" */ + struct _socket *next; /* next socket */ +} SOCKET; + +extern SOCKET *sockets; + +/* + * SOCKET uses a horrible linked list structure. Lists should be at least + * doubly-linked and there should be a few hashes (by id and by call_ref) for + * reasonable fast lookup. All this will have to wait till that version is + * stable enough to be useful to test the "real" thing against it. + */ + +#define S_PVC(e) \ + (e)->signaling_pvc.sap_addr.itf, \ + (e)->signaling_pvc.sap_addr.vpi, \ + (e)->signaling_pvc.sap_addr.vci + +extern SIG_ENTITY *entities; +extern SIG_ENTITY _entity; + +extern const CALL_STATE state_map[]; +extern const PARTY_STATE eps_map[]; +extern const char *state_name[],*cs_name[],*as_name[]; + +extern unsigned char q_buffer[]; + +#define DEFAULT_TRACE_SIZE 20 + +extern int pretty; +extern const char *dump_dir; + +extern int stop; + + +#define SEND_ERROR(vcc,code) \ + send_kernel(vcc,0L,as_error,code,NULL,NULL,NULL,NULL,NULL) + + +void poll_signals(void); + +void from_kernel(struct atmsvc_msg *msg,int size); +void itf_load(int itf); + +void to_uni(SIG_ENTITY *sig,void *msg,int size); +void send_kernel(unsigned long vcc,unsigned long listen_vcc, + enum atmsvc_msg_type type,int reply,const struct sockaddr_atmpvc *pvc, + const struct sockaddr_atmsvc *svc,const struct sockaddr_atmsvc *local, + const struct atm_sap *sap,const struct atm_qos *qos); +void from_net(SIG_ENTITY *sig,void *msg,int size); +void to_signaling(SIG_ENTITY *sig,void *msg,int size); +void saal_failure(SIG_ENTITY *sig); +void saal_okay(SIG_ENTITY *sig); +void clear_all_calls(SIG_ENTITY *sig); +void clear_all_calls_on_T309(SIG_ENTITY *sig); + +SOCKET *new_sock(unsigned long id); +void free_sock(SOCKET *sock); +void new_state(SOCKET *sock,STATE state); +SOCKET *lookup_sap(const struct sockaddr_atmsvc *addr, + const struct atm_sap *sap,const struct atm_qos *qos, + struct sockaddr_atmsvc *res_addr,struct atm_sap *res_sap, + struct atm_qos *res_qos,int exact_match); + +void send_release(SOCKET *sock,unsigned char reason,...); +void send_release_complete(SIG_ENTITY *sig,unsigned long call_ref, + unsigned char cause); +int send_call_proceeding(SOCKET *sock); +void send_modify_reject(SOCKET *sock,unsigned char reason); + +const char *mid2name(unsigned char mid); + +void set_error(SOCKET *sock,int code); +void send_close(SOCKET *sock); + +int get_vci(int itf); + +void enter_vpci(SIG_ENTITY *sig,int vpci,int itf); +int get_itf(SIG_ENTITY *sig,int *vpci); +void init_addr(SIG_ENTITY *sig); + +void add_route(SIG_ENTITY *sig,struct sockaddr_atmsvc *addr,int len); +SIG_ENTITY *route_call(struct sockaddr_atmsvc *addr); + +SIG_ENTITY *get_sig_entity(int itf); + +#endif diff -ur --new-file old/atm/sigd.new/sap.c new/atm/sigd.new/sap.c --- old/atm/sigd.new/sap.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/sap.c Sat Jun 6 02:08:06 1998 @@ -0,0 +1,339 @@ +/* sap.c - SAP manipulations */ + +/* Written 1996-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include +#include /* sigh, again */ +#include + +#include "atm.h" +#include "atmd.h" +#include "uni.h" +#include "qlib.h" +#include + +#include "common.h" +#include "sap.h" + + +#define COMPONENT "SAP" + + +static int class_compat(const struct atm_trafprm *tx, + const struct atm_trafprm *rx) +{ + if (tx->traffic_class == ATM_NONE || tx->traffic_class == ATM_ANYCLASS) + return 1; + if (rx->traffic_class == ATM_UBR || rx->traffic_class == ATM_ANYCLASS) + return 1; + /* don't apply CAC to PCR */ + if (tx->traffic_class != rx->traffic_class) return 0; + /* ignore special cases like CBR to VBR for now */ + switch (tx->traffic_class) { + case ATM_CBR: + if (!rx->max_pcr || rx->max_pcr == ATM_MAX_PCR) return 1; + return tx->min_pcr <= rx->max_pcr; + /* Actually, we shouldn't look at min_pcr, because there's no + bandwidth negotiation anyway. */ + default: + diag(COMPONENT,DIAG_ERROR,"unsupported traffic class %d\n", + tx->traffic_class); + return 0; + } +} + + +int sap_compat(const struct sockaddr_atmsvc *old_addr, + const struct sockaddr_atmsvc *new_addr,struct sockaddr_atmsvc *res_addr, + const struct atm_sap *old_sap,const struct atm_sap *new_sap, + struct atm_sap *res_sap,const struct atm_qos *old_qos, + const struct atm_qos *new_qos,struct atm_qos *res_qos) +{ + if (atmsvc_addr_in_use(*old_addr) && + !atm_equal((struct sockaddr *) old_addr,(struct sockaddr *) new_addr,0,0)) + return 0; + if (res_qos) *res_qos = *new_qos; + if (old_qos->txtp.max_sdu && new_qos->rxtp.max_sdu && + old_qos->txtp.max_sdu > new_qos->rxtp.max_sdu) return 0; + if (new_qos->txtp.max_sdu && old_qos->rxtp.max_sdu && + new_qos->txtp.max_sdu > old_qos->rxtp.max_sdu) return 0; + if (!class_compat(&old_qos->txtp,&new_qos->rxtp) || + !class_compat(&new_qos->txtp,&old_qos->rxtp)) return 0; + if (!sap_equal(old_sap,new_sap, + SXE_COMPATIBLE | SXE_NEGOTIATION | (res_sap ? SXE_RESULT : 0),res_sap)) + return 0; + return 1; +} + + +static int encode_blli(Q_DSC *dsc,const struct atm_blli *blli) +{ + if (blli->l2_proto != ATM_L2_NONE) { + q_assign(dsc,QF_uil2_proto,blli->l2_proto); + switch (blli->l2_proto) { + case ATM_L2_X25_LL: + case ATM_L2_X25_ML: + case ATM_L2_HDLC_ARM: + case ATM_L2_HDLC_NRM: + case ATM_L2_HDLC_ABM: + case ATM_L2_Q922: + case ATM_L2_ISO7776: + if (blli->l2.itu.mode != ATM_IMD_NONE) + q_assign(dsc,QF_l2_mode,blli->l2.itu.mode); + if (blli->l2.itu.window) + q_assign(dsc,QF_window_size,blli->l2.itu.window); + break; + case ATM_L2_USER: + q_assign(dsc,QF_user_l2,blli->l2.user); + break; + default: + break; + } + } + if (blli->l3_proto != ATM_L3_NONE) { + q_assign(dsc,QF_uil3_proto,blli->l3_proto); + switch (blli->l3_proto) { + case ATM_L3_X25: + case ATM_L3_ISO8208: + case ATM_L3_X223: + if (blli->l3.itu.mode != ATM_IMD_NONE) + q_assign(dsc,QF_l3_mode,blli->l3.itu.mode); + if (blli->l3.itu.def_size) + q_assign(dsc,QF_def_pck_size,blli->l3.itu.def_size); + if (blli->l3.itu.window) + q_assign(dsc,QF_pck_win_size,blli->l3.itu.window); + break; + case ATM_L3_TR9577: + q_assign(dsc,QF_ipi_high,blli->l3.tr9577.ipi >> 1); + q_assign(dsc,QF_ipi_low,blli->l3.tr9577.ipi & 1); + if (blli->l3.tr9577.ipi == NLPID_IEEE802_1_SNAP) { + q_write(dsc,QF_oui,blli->l3.tr9577.snap,3); + q_write(dsc,QF_pid,blli->l3.tr9577.snap+3,2); + } + break; + case ATM_L3_USER: + q_assign(dsc,QF_user_l3,blli->l3.user); + break; + default: + diag(COMPONENT,DIAG_ERROR,"bad l3_proto (%d)", + blli->l3_proto); + return -EINVAL; + } + } + return 0; +} + + +int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *addr, + const struct atm_sap *sap,const struct atm_qos *qos) +{ + int error,pcr; + + if (*addr->sas_addr.pub) + q_write(dsc,QF_cdpn_e164,(void *) addr->sas_addr.pub, + strlen(addr->sas_addr.pub)); + else if (*addr->sas_addr.prv) + q_write(dsc,QF_cdpn_esa,(void *) addr->sas_addr.prv,ATM_ESA_LEN); + else return -EDESTADDRREQ; + if (qos->txtp.traffic_class == ATM_UBR || qos->rxtp.traffic_class == + ATM_UBR) q_assign(dsc,QF_best_effort,0); +#ifdef UNI40 + if (qos->txtp.traffic_class == ATM_CBR || qos->rxtp.traffic_class == + ATM_CBR) q_assign(dsc,QF_trans_cap,ATM_TC_CBR); +#endif + switch (qos->txtp.traffic_class) { + case ATM_NONE: + q_assign(dsc,QF_fw_pcr_01,0); + break; + case ATM_UBR: + /* fall through */ + case ATM_CBR: + /* here's a bit of policy: send the highest value we have */ + pcr = SELECT_TOP_PCR(qos->txtp); + diag(COMPONENT,DIAG_DEBUG,"fwd %d (%d..%d)",pcr, + qos->txtp.min_pcr,qos->txtp.max_pcr); + if (pcr == ATM_MAX_PCR) pcr = ATM_OC3_PCR; + q_assign(dsc,QF_fw_pcr_01,pcr); + break; + default: + diag(COMPONENT,DIAG_ERROR,"bad TX class (%d)", + qos->txtp.traffic_class); + return -EINVAL; + } + switch (qos->rxtp.traffic_class) { + case ATM_NONE: + q_assign(dsc,QF_bw_pcr_01,0); + break; + case ATM_UBR: + /* fall through */ + case ATM_CBR: + pcr = SELECT_TOP_PCR(qos->rxtp); + diag(COMPONENT,DIAG_DEBUG,"bwd %d (%d..%d)",pcr, + qos->rxtp.min_pcr,qos->rxtp.max_pcr); + if (pcr == ATM_MAX_PCR) pcr = ATM_OC3_PCR; + q_assign(dsc,QF_bw_pcr_01,pcr); + break; + default: + diag(COMPONENT,DIAG_ERROR,"bad RX class (%d)", + qos->rxtp.traffic_class); + return -EINVAL; + } + if (qos->txtp.max_sdu) q_assign(dsc,QF_fw_max_sdu,qos->txtp.max_sdu); + if (qos->rxtp.max_sdu) q_assign(dsc,QF_bw_max_sdu,qos->rxtp.max_sdu); + /* @@@ bearer class ? */ + /* @@@ QOS class ? */ + if (sap->bhli.hl_type != ATM_HL_NONE) { + q_assign(dsc,QF_hli_type,sap->bhli.hl_type-1); + switch (sap->bhli.hl_type) { + case ATM_HL_ISO: + q_write(dsc,QF_iso_hli,sap->bhli.hl_info,sap->bhli.hl_length); + break; + case ATM_HL_USER: + q_write(dsc,QF_user_hli,sap->bhli.hl_info,sap->bhli.hl_length); + break; +#ifdef UNI30 + case ATM_HL_HLP: + q_write(dsc,QF_hlp,sap->bhli.hl_info,4); + break; +#endif + case ATM_HL_VENDOR: + q_write(dsc,QF_hli_oui,sap->bhli.hl_info,3); + q_write(dsc,QF_app_id, sap->bhli.hl_info+3,4); + break; + default: + diag(COMPONENT,DIAG_ERROR,"bad hl_type (%d)", + sap->bhli.hl_type); + return -EINVAL; + } + } + if (!blli_in_use(sap->blli[0])) return 0; + q_instance(dsc,QG_blli1); + error = encode_blli(dsc,sap->blli); + if (error) return 0; + if (!blli_in_use(sap->blli[1])) return 0; + q_instance(dsc,QG_blli2); + error = encode_blli(dsc,sap->blli+1); + if (error) return 0; + if (!blli_in_use(sap->blli[2])) return 1; + q_instance(dsc,QG_blli3); + return encode_blli(dsc,sap->blli+2); +} + + +static void decode_blli(Q_DSC *dsc,struct atm_blli *blli) +{ +#define GET(var,field) \ + ({ if (q_present(dsc,field)) blli->var = q_fetch(dsc,field); }) + + if (q_present(dsc,QF_uil2_proto)) { + blli->l2_proto = q_fetch(dsc,QF_uil2_proto); + GET(l2.itu.mode,QF_l2_mode); + GET(l2.itu.window,QF_window_size); + GET(l2.user,QF_user_l2); + } + if (q_present(dsc,QF_uil3_proto)) { + blli->l3_proto = q_fetch(dsc,QF_uil3_proto); + GET(l3.itu.mode,QF_l3_mode); + GET(l3.itu.def_size,QF_def_pck_size); + GET(l3.itu.window,QF_pck_win_size); + GET(l3.user,QF_user_l3); + if (q_present(dsc,QF_ipi_high)) { + blli->l3.tr9577.ipi = q_fetch(dsc,QF_ipi_high) << 1; + if (blli->l3.tr9577.ipi != NLPID_IEEE802_1_SNAP) + blli->l3.tr9577.ipi |= q_fetch(dsc,QF_ipi_low); + else if (!q_present(dsc,QF_oui)) blli->l3.tr9577.ipi |= 1; + else { + q_read(dsc,QF_oui,blli->l3.tr9577.snap,3); + q_read(dsc,QF_pid,blli->l3.tr9577.snap+3,2); + } + } + } +#undef GET +} + + +void sap_decode(Q_DSC *dsc,struct sockaddr_atmsvc *addr,struct atm_sap *sap, + struct atm_qos *qos) +{ + memset(addr,0,sizeof(*addr)); + memset(sap,0,sizeof(*sap)); + memset(qos,0,sizeof(*qos)); + addr->sas_family = AF_ATMSVC; + if (q_present(dsc,QF_cdpn_e164)) + (void) q_read(dsc,QF_cdpn_e164,(void *) &addr->sas_addr.pub, + ATM_E164_LEN); + else if (q_present(dsc,QF_cdpn_esa)) + (void) q_read(dsc,QF_cdpn_esa,(void *) &addr->sas_addr.prv, + ATM_ESA_LEN); + if (q_present(dsc,QF_aal_type)) + if (q_fetch(dsc,QF_aal_type) != 5) + diag(COMPONENT,DIAG_ERROR,"AAL type %d requested", + q_fetch(dsc,QF_aal_type)); + if (q_present(dsc,QF_best_effort)) { + qos->txtp.traffic_class = qos->rxtp.traffic_class = ATM_UBR; + diag(COMPONENT,DIAG_DEBUG,"UBR"); + } + else { + qos->txtp.traffic_class = qos->rxtp.traffic_class = ATM_CBR; + diag(COMPONENT,DIAG_DEBUG,"CBR"); + } + qos->txtp.max_pcr = qos->rxtp.max_pcr = 0; + /* unbalanced decoding - always sets upper bound */ + if (q_present(dsc,QF_fw_pcr_01)) { + qos->rxtp.min_pcr = 0; + qos->rxtp.max_pcr = q_fetch(dsc,QF_fw_pcr_01); + } + if (q_present(dsc,QF_bw_pcr_01)) { + qos->txtp.min_pcr = 0; + qos->txtp.max_pcr = q_fetch(dsc,QF_bw_pcr_01); + } + if (!qos->txtp.max_pcr) qos->txtp.traffic_class = ATM_NONE; + if (!qos->rxtp.max_pcr) qos->rxtp.traffic_class = ATM_NONE; + diag(COMPONENT,DIAG_DEBUG,"fwd %d..%d bwd %d..%d", + qos->rxtp.min_pcr,qos->rxtp.max_pcr,qos->txtp.min_pcr, + qos->txtp.max_pcr); + /* SHOULD ... fail call if anything is missing ... @@@ */ + if (q_present(dsc,QF_bw_max_sdu)) + qos->txtp.max_sdu = q_fetch(dsc,QF_bw_max_sdu); + if (q_present(dsc,QF_fw_max_sdu)) + qos->rxtp.max_sdu = q_fetch(dsc,QF_fw_max_sdu); + if (q_present(dsc,QG_bhli)) { + sap->bhli.hl_type = q_fetch(dsc,QF_hli_type)+1; + switch (sap->bhli.hl_type) { + case ATM_HL_ISO: + sap->bhli.hl_length = q_length(dsc,QF_iso_hli); + q_read(dsc,QF_iso_hli,sap->bhli.hl_info,sap->bhli.hl_length); + break; + case ATM_HL_USER: + sap->bhli.hl_length = q_length(dsc,QF_user_hli); + q_read(dsc,QF_user_hli,sap->bhli.hl_info,sap->bhli.hl_length); + break; +#ifdef UNI30 + case ATM_HL_HLP: + sap->bhli.hl_length = 4; + q_read(dsc,QF_hlp,sap->bhli.hl_info,4); + break; +#endif + case ATM_HL_VENDOR: + sap->bhli.hl_length = 7; + q_read(dsc,QF_hli_oui,sap->bhli.hl_info,3); + q_read(dsc,QF_app_id,sap->bhli.hl_info+3,4); + break; + default: + diag(COMPONENT,DIAG_FATAL,"unrecognized hl_type"); + } + } + if (!q_present(dsc,QG_blli1)) return; + q_instance(dsc,QG_blli1); + decode_blli(dsc,sap->blli); + if (!q_present(dsc,QG_blli2)) return; + q_instance(dsc,QG_blli2); + decode_blli(dsc,sap->blli+1); + if (!q_present(dsc,QG_blli3)) return; + q_instance(dsc,QG_blli3); + decode_blli(dsc,sap->blli+2); +} diff -ur --new-file old/atm/sigd.new/sap.h new/atm/sigd.new/sap.h --- old/atm/sigd.new/sap.h Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/sap.h Wed Nov 5 00:23:04 1997 @@ -0,0 +1,24 @@ +/* sap.h - SAP manipulations */ + +/* Written 1996,1997 by Werner Almesberger, EPFL-LRC */ + + +#ifndef SAP_H +#define SAP_H + +#include + +#include "atmsap.h" + + +int sap_compat(const struct sockaddr_atmsvc *old_addr, + const struct sockaddr_atmsvc *new_addr,struct sockaddr_atmsvc *res_addr, + const struct atm_sap *old_sap,const struct atm_sap *new_sap, + struct atm_sap *res_sap,const struct atm_qos *old_qos, + const struct atm_qos *new_qos,struct atm_qos *res_qos); +int sap_encode(Q_DSC *dsc,const struct sockaddr_atmsvc *addr, + const struct atm_sap *sap,const struct atm_qos *qos); +void sap_decode(Q_DSC *dsc,struct sockaddr_atmsvc *addr,struct atm_sap *sap, + struct atm_qos *qos); + +#endif diff -ur --new-file old/atm/sigd.new/timeout.c new/atm/sigd.new/timeout.c --- old/atm/sigd.new/timeout.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/timeout.c Thu Aug 20 20:05:03 1998 @@ -0,0 +1,119 @@ +/* timeout.c - Processing of signaling timeout events */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include + +#include "atmd.h" + +#include "uni.h" +#include "proto.h" +#include "timeout.h" + + +#define COMPONENT "UNI" + + +static void complain(SOCKET *sock,const char *timer) +{ + diag(COMPONENT,DIAG_FATAL,"Timer %s expired in incompatible state %s", + timer,state_name[sock->state]); +} + + +void on_T303(void *user) /* CONNECTING */ +{ + SOCKET *sock = user; + + diag(COMPONENT,DIAG_DEBUG,"T303 on 0x%lx",sock->id); + if (sock->state != ss_connecting) complain(sock,"T303"); + SEND_ERROR(sock->id,-ETIMEDOUT); + sock->conn_timer = NULL; + free_sock(sock); +} + + +void on_T308_1(void *user) /* WAIT_REL or REL_REQ */ +{ + SOCKET *sock = user; + + diag(COMPONENT,DIAG_DEBUG,"T308_1 on 0x%lx",sock->id); + if (sock->state != ss_wait_rel && sock->state != ss_rel_req) + complain(sock,"T308_1"); + send_release(sock,ATM_CV_TIMER_EXP,308); /* @@@ ? */ + sock->conn_timer = NULL; + START_TIMER(sock,T308_2); +} + + +void on_T308_2(void *user) /* WAIT_REL or REL_REQ */ +{ + SOCKET *sock = user; + + diag(COMPONENT,DIAG_WARN,"Trouble: T308_2 has expired"); + if (sock->state != ss_wait_rel && sock->state != ss_rel_req) + complain(sock,"T308_2"); + sock->conn_timer = NULL; + if (sock->state == ss_rel_req) send_close(sock); + free_sock(sock); +} + + +void on_T309(void *user) +{ + diag(COMPONENT,DIAG_DEBUG,"T309 has expired"); + clear_all_calls_on_T309(user); +} + + +void on_T310(void *user) +{ + on_T303(user); + diag(COMPONENT,DIAG_DEBUG,"(it's actually T310)"); +} + + +void on_T313(void *user) /* ACCEPTING */ +{ + SOCKET *sock = user; + + diag(COMPONENT,DIAG_DEBUG,"T313 on 0x%lx",sock->id); + if (sock->state != ss_accepting) complain(sock,"T313"); + send_release(sock,ATM_CV_TIMER_EXP,313); + sock->conn_timer = NULL; + START_TIMER(sock,T308_1); + new_state(sock,ss_rel_req); +} + + +#ifdef Q2963_1 + +void on_T360(void *user) +{ + SOCKET *sock = user; + + diag(COMPONENT,DIAG_DEBUG,"T360 on 0x%lx",sock->id); + if (sock->state != ss_mod_req) complain(sock,"T360"); + send_release(sock,ATM_CV_TIMER_EXP,360); + sock->conn_timer = NULL; + START_TIMER(sock,T308_1); + new_state(sock,ss_rel_req); +} + + +void on_T361(void *user) +{ + SOCKET *sock = user; + + diag(COMPONENT,DIAG_DEBUG,"T361 on 0x%lx",sock->id); + if (sock->state != ss_connected) complain(sock,"T361"); + sock->qos = sock->new_qos; + send_kernel(sock->id,0,as_modify,ATM_MF_SET,NULL,NULL,NULL,NULL,&sock->qos); + sock->conn_timer = NULL; + new_state(sock,ss_mod_fin_ack); +} + +#endif diff -ur --new-file old/atm/sigd.new/timeout.h new/atm/sigd.new/timeout.h --- old/atm/sigd.new/timeout.h Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/timeout.h Mon Oct 20 17:05:28 1997 @@ -0,0 +1,34 @@ +/* timeout.h - Processing of signaling timeout events */ + +/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */ + + +#ifndef TIMEOUT_H +#define TIMEOUT_H + +#define T303_TIME 4000000 /* 4 sec */ +#define T308_1_TIME 30000000 /* 30 sec */ +#define T308_2_TIME 30000000 /* 30 sec */ +#define T309_TIME 10000000 /* 10 sec */ +#define T310_TIME 10000000 /* 10 sec */ +#define T313_TIME 4000000 /* 4 sec */ + +#define T360_TIME 30000000 /* 30 sec */ +#define T361_TIME 20000000 /* 20 sec */ + + +#define START_TIMER(u,t) { assert(!u->conn_timer); \ + u->conn_timer = start_timer(t##_TIME,on_##t,u); } +#define STOP_TIMER(u) { stop_timer(u->conn_timer); u->conn_timer = NULL; } + + +void on_T303(void *user); +void on_T308_1(void *user); +void on_T308_2(void *user); +void on_T309(void *user); +void on_T310(void *user); +void on_T313(void *user); +void on_T360(void *user); +void on_T361(void *user); + +#endif diff -ur --new-file old/atm/sigd.new/trace.c new/atm/sigd.new/trace.c --- old/atm/sigd.new/trace.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/trace.c Fri Aug 21 00:30:56 1998 @@ -0,0 +1,289 @@ +/* trace.c - Support functions for message tracing */ + +/* Written 1996-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include +#include +#include +#include /* linux/atmsvc.h includes linux/atm.h */ +#include + +#include "atm.h" +#include "atmd.h" +#include "atmsap.h" +#include "trace.h" +#include "proto.h" + + +#define DUMP_MODE +#include "qlib.h" + + +typedef struct _entry { + int number; + struct timeval time; + void (*print)(void *msg,int size); + const char *comment; + void *msg; + int size; + struct _entry *next; +} ENTRY; + + +int trace_size = 0; + +static int current_size = 0; +static int sequence = 0; +static ENTRY *first = NULL,*last = NULL; +static char *string = NULL; +static int curr_len; +static int new_line; + + +static inline void append_chunk(const char *str,int len) +{ + if (!string) curr_len = 0; + if (!(string = realloc(string,curr_len+len+1))) { + perror("realloc"); + exit(1); + } + memcpy(string+curr_len,str,len); + curr_len += len; + string[curr_len] = 0; +} + + +static void vappend(const char *fmt,va_list ap) +{ + const char *walk,*next; + + for (walk = next = fmt; *walk; walk++) + if (*walk == '%') { + if (walk != next) append_chunk(next,walk-next); + if (*++walk == 's') { + const char *str; + + str = va_arg(ap,const char *); + append_chunk(str,strlen(str)); + } + else { + char buf[21]; /* big enough for 64 bits */ + int num; + + while (isdigit(*walk) || *walk == 'l') walk++; /* @@@ FIXME */ + if (*walk != 'd' && *walk != 'x') { + fprintf(stderr,"bad format character %c (%d)\n",*walk, + *walk); + exit(1); + } + num = va_arg(ap,int); + sprintf(buf,*walk == 'd' ? "%d" : "%x",num); + append_chunk(buf,strlen(buf)); + } + next = walk+1; + } + if (walk != next) append_chunk(next,walk-next); +} + + +static void append(const char *fmt,...) +{ + va_list ap; + + va_start(ap,fmt); + vappend(fmt,ap); + va_end(ap); +} + + +static void print_text(void *msg,int size) +{ + append(" %s\n",msg); +} + + +static void append_svc(const struct sockaddr_atmsvc *svc) +{ + char buffer[MAX_ATM_ADDR_LEN+1]; + + if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) svc, + A2T_NAME | A2T_LOCAL | A2T_PRETTY) < 0) strcpy(buffer,""); + append("%s\n",buffer); +} + + +static void append_sap(const struct atm_sap *sap) +{ + char buffer[MAX_ATM_SAP_LEN+1]; + + if (sap2text(buffer,MAX_ATM_SAP_LEN+1,sap,S2T_NAME | S2T_LOCAL) < 0) + strcpy(buffer,""); + append("%s\n",buffer); +} + + +static void append_qos(const struct atm_qos *qos) +{ + char buffer[MAX_ATM_QOS_LEN+1]; + + if (qos2text(buffer,MAX_ATM_QOS_LEN+1,qos,0) < 0) + strcpy(buffer,""); + append("%s\n",buffer); +} + + +static void qd_vdump(const char *msg,va_list ap) +{ + if (new_line) append(" "); + vappend(msg,ap); + if (string && curr_len) new_line = string[curr_len-1] == '\n'; + else new_line = 1; +} + + +void qd_dump(const char *msg,...) +{ + va_list ap; + + va_start(ap,msg); + qd_vdump(msg,ap); + va_end(ap); +} + + +void qd_report(int severity,const char *msg,...) +{ + va_list ap; + + if (severity > Q_ERROR) return; + va_start(ap,msg); + qd_vdump(msg,ap); + va_end(ap); + qd_dump("\n"); +} + + +static void print_uni(void *msg,int size) +{ + Q_DSC dsc; + + (void) qd_open(&dsc,msg,size); + qd_close(&dsc); +} + + +static void print_kernel(void *msg,int size) +{ + static const char *type[] = { "as_catch_null","as_bind","as_connect", + "as_accept","as_reject","as_listen","as_okay","as_error","as_indicate", + "as_close","as_itf_notify","as_modify","as_identify" }; + struct atmsvc_msg *m = msg; + + append(" %s (vcc 0x%x, listen_vcc 0x%x)\n",m->type < sizeof(type)/ + sizeof(*type) ? type[m->type] : "???",m->vcc,m->listen_vcc); + append(" reply %d",m->reply); + if (m->reply) { + const char *error; + + error = strerror(m->reply > 0 ? m->reply : -m->reply); + append(" (%s)",error ? error : "???"); + } + append(", aal %d\n",m->qos.aal); + append(" pvc %d.%d.%d\n",m->pvc.sap_addr.itf,m->pvc.sap_addr.vpi, + m->pvc.sap_addr.vci); + append(" local "); + append_svc(&m->local); + append(" qos "); + append_qos(&m->qos); + append(" svc "); + append_svc(&m->svc); + append(" sap "); + append_sap(&m->sap); +} + + +static void store(void (*print)(void *msg,int size),const char *comment, + void *msg,int size) +{ + ENTRY *entry; + + entry = alloc_t(ENTRY); + (void) gettimeofday(&entry->time,NULL); + entry->number = sequence++; + entry->print = print; + entry->comment = comment; + entry->msg = msg; + entry->size = size; + entry->next = NULL; + if (current_size < trace_size) current_size++; + else { /* handle trace_size < 1 (< 0) too */ + ENTRY *next; + + next = first->next; + free(first->msg); + free(first); + if (first == last) last = NULL; /* someone set trace_size < 2 */ + first = next; + } + if (last) last->next = entry; + else first = entry; + last = entry; +} + + +void trace_msg(const char *msg) +{ + char *buf; + + if (!trace_size) return; + buf = alloc(strlen(msg)+1); + strcpy(buf,msg); + store(&print_text,"MESSAGE",buf,strlen(msg)); +} + + +void trace_uni(const char *comment,const SIG_ENTITY *sig,const void *msg, + int size) +{ + static char comm[200]; /* @@@ should just copy the PVC */ + void *buf; + + if (!trace_size) return; + sprintf(comm,"%s (%d.%d.%d)",comment,S_PVC(sig)); + buf = alloc(size); + memcpy(buf,msg,size); + store(&print_uni,strdup(comm),buf,size); /* @@@ leak + unchecked NULL ! */ +} + + +void trace_kernel(const char *comment,const struct atmsvc_msg *msg) +{ + struct atmsvc_msg *buf; + + if (!trace_size) return; + buf = alloc_t(struct atmsvc_msg); + *buf = *msg; + store(&print_kernel,comment,buf,sizeof(*msg)); +} + + +char *get_trace(void) +{ + ENTRY *walk; + + if (string) { + free(string); + string = NULL; + } + for (walk = first; walk; walk = walk->next) { + append("%6d (%d.%06d) %s:\n",walk->number,walk->time.tv_sec, + walk->time.tv_usec,walk->comment); + new_line = 1; + walk->print(walk->msg,walk->size); + } + return string; +} diff -ur --new-file old/atm/sigd.new/trace.h new/atm/sigd.new/trace.h --- old/atm/sigd.new/trace.h Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/trace.h Thu Aug 20 18:06:41 1998 @@ -0,0 +1,22 @@ +/* trace.h - Support functions for message tracing */ + +/* Written 1996,1998 by Werner Almesberger, EPFL-LRC */ + + +#ifndef TRACE_H +#define TRACE_H + +#include + +#include "proto.h" + + +extern int trace_size; + +void trace_msg(const char *msg); +void trace_uni(const char *comment,const SIG_ENTITY *sig,const void *msg, + int size); +void trace_kernel(const char *comment,const struct atmsvc_msg *msg); +char *get_trace(void); + +#endif diff -ur --new-file old/atm/sigd.new/uni.c new/atm/sigd.new/uni.c --- old/atm/sigd.new/uni.c Thu Jan 1 01:00:00 1970 +++ new/atm/sigd.new/uni.c Fri Aug 21 00:43:09 1998 @@ -0,0 +1,794 @@ +/* uni.c - Processing of incoming UNI signaling messages */ + +/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "atm.h" +#include "atmd.h" +#include "uni.h" +#include "qlib.h" +#include + +#include "proto.h" +#include "sap.h" +#include "io.h" +#include "policy.h" +#include "timeout.h" +#include "trace.h" + + +#define COMPONENT "UNI" + + +extern const char *cause_text[]; /* from mess.c */ + + +static Q_DSC in_dsc; +static TIMER *t309 = NULL; + + +int send_call_proceeding(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_CALL_PROC); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if (sock->sig->mode == sm_net) { + int vpci,vci; + + sock->pvc.sap_family = AF_ATMPVC; + vpci = sock->sig->signaling_pvc.sap_addr.itf; + sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); + sock->pvc.sap_addr.vpi = vpci; + vci = get_vci(sock->pvc.sap_addr.itf); + if (vci < 0) { + (void) q_close(&dsc); + return vci; + } + sock->pvc.sap_addr.vci = vci; + } + if (sock->sig->mode != sm_user) { + q_assign(&dsc,QF_vpi,sock->pvc.sap_addr.vpi); + q_assign(&dsc,QF_vci,sock->pvc.sap_addr.vci); + } + if (sock->ep_ref >= 0) q_assign(&dsc,QF_ep_ref,sock->ep_ref); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); + return 0; +} + + +static void setup_call(SIG_ENTITY *sig,unsigned long call_ref) +{ + SOCKET *sock,*this,**walk; + struct sockaddr_atmsvc in_addr; + struct atm_sap in_sap; + struct atm_qos in_qos; + int i; + + sap_decode(&in_dsc,&in_addr,&in_sap,&in_qos); + if (!atmsvc_addr_in_use(in_addr)) { + send_release_complete(sig,call_ref,ATM_CV_UNALLOC); + return; + } + if (!allow(&in_addr,ACL_IN)) { + send_release_complete(sig,call_ref,ATM_CV_REJ_CLIR); + return; + } + this = new_sock(0); + this->sig = sig; + sock = lookup_sap(&in_addr,&in_sap,&in_qos,&this->local,&this->sap, + &this->qos,0); + if (!sock) { + free_sock(this); + send_release_complete(sig,call_ref,ATM_CV_INCOMP_DEST); + return; + } + this->qos.aal = ATM_AAL5; /* hack @@@ */ + this->state = sig->mode == sm_net ? ss_proceeding : ss_indicated; + this->call_state = cs_in_proc; + this->call_ref = call_ref; + if (q_present(&in_dsc,QF_ep_ref)) + this->ep_ref = q_fetch(&in_dsc,QF_ep_ref); +#ifdef CISCO + else +#endif + if (sig->mode == sm_net) { + int error; + + error = send_call_proceeding(this); + if (error) { + free_sock(this); + send_release_complete(sig,call_ref,ATM_CV_NO_CI); + return; + } + } + /* if (sock->local) *this->local->sas_addr = sock->local->sas_addr; ??? */ + diag(COMPONENT,DIAG_DEBUG,"AAL type %ld",q_fetch(&in_dsc,QF_aal_type)); + if (sig->mode == sm_user) { /* already set by send_call_proceeding */ + int vpci; + + vpci = q_fetch(&in_dsc,QF_vpi); + this->pvc.sap_family = AF_ATMPVC; + this->pvc.sap_addr.itf = get_itf(sig,&vpci); + this->pvc.sap_addr.vpi = vpci; + this->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); + } + diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d.%d.%d",this->pvc.sap_addr.itf, + this->pvc.sap_addr.vpi,this->pvc.sap_addr.vci); + if (q_present(&in_dsc,QF_cgpn)) { /* should handle E.164 too */ + char buffer[MAX_ATM_ADDR_LEN+1]; + int plan; + + plan = q_fetch(&in_dsc,QF_cgpn_plan); + switch (plan) { + case ATM_NP_AEA: + i = q_read(&in_dsc,QF_cgpn,(void *) this->remote.sas_addr.prv, + ATM_ESA_LEN); + break; + case ATM_NP_E164: + i = q_read(&in_dsc,QF_cgpn,(void *) this->remote.sas_addr.pub, + ATM_E164_LEN); + break; + default: + diag(COMPONENT,DIAG_WARN,"Ignoring cgpn with unrecognized " + "numbering plan 0x%x\n",plan); + i = 0; + } + if (i) { + this->remote.sas_family = AF_ATMSVC; + if (atm2text(buffer,MAX_ATM_ADDR_LEN+1, + (struct sockaddr *) &this->remote,pretty) < 0) + strcpy(buffer,""); + diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer); + } + } + send_kernel(0,sock->id,as_indicate,0,&this->pvc,&this->remote,&in_addr, + &this->sap,&this->qos); + for (walk = &sock->listen; *walk; walk = &(*walk)->listen); + *walk = this; + diag(COMPONENT,DIAG_DEBUG,"SE vpi.vci=%d.%d",this->pvc.sap_addr.vpi, + this->pvc.sap_addr.vci); +} + + +static void send_status(SIG_ENTITY *sig,SOCKET *sock,unsigned long call_ref, + unsigned char cause,...) +{ + va_list ap; + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_STATUS); + if (sock) { + q_assign(&dsc,QF_call_ref,sock->call_ref); + q_assign(&dsc,QF_call_state,(int) sock->call_state); + if (sock->ep_ref >= 0) { + q_assign(&dsc,QF_ep_ref,sock->ep_ref); + q_assign(&dsc,QF_ep_state,eps_map[sock->call_state]); + } + } + else { + q_assign(&dsc,QF_call_ref,call_ref); + q_assign(&dsc,QF_call_state,0); /* U0 - Null / REST 0 - Null */ + } + q_assign(&dsc,QF_cause,cause); + va_start(ap,cause); + switch (cause) { + case ATM_CV_UNKNOWN_MSG_TYPE: + case ATM_CV_INCOMP_MSG: + q_assign(&dsc,QF_bad_msg_type,va_arg(ap,unsigned char)); + break; + case ATM_CV_MAND_IE_MISSING: + case ATM_CV_INVALID_IE: + { + unsigned char ie; + + ie = va_arg(ap,unsigned char); + q_write(&dsc,QF_ie_id6,&ie,1); + break; + } + default: + ; + } + va_end(ap); + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); +} + + +static void send_status_enq(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_STATUS_ENQ); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if (sock->ep_ref >= 0) q_assign(&dsc,QF_ep_ref,sock->ep_ref); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); + /* @@@ should start T322 */ +} + + +static void send_connect_ack(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_CONN_ACK); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); +} + + +static void send_restart_ack(SIG_ENTITY *sig,int vpi,int vci) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_REST_ACK); + q_assign(&dsc,QF_call_ref,0); + if (!vpi && !vci) q_assign(&dsc,QF_rst_class,ATM_RST_ALL_VC); + else { + q_assign(&dsc,QF_rst_class,ATM_RST_IND_VC); + q_assign(&dsc,QF_vpi,vpi); + q_assign(&dsc,QF_vci,vci); + } + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); +} + + +static void send_drop_party_ack(SIG_ENTITY *sig,unsigned long call_ref, + short ep_ref,unsigned char cause) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_DROP_PARTY_ACK); + q_assign(&dsc,QF_call_ref,call_ref); + q_assign(&dsc,QF_ep_ref,ep_ref); + q_assign(&dsc,QF_cause,cause); + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); +} + + +#ifdef Q2963_1 + +static void send_conn_avail(SOCKET *sock) +{ + Q_DSC dsc; + int size; + + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,ATM_MSG_CONN_AVAIL); + q_assign(&dsc,QF_call_ref,sock->call_ref); + if ((size = q_close(&dsc)) >= 0) to_signaling(sock->sig,q_buffer,size); +} + +#endif + + +static void uni_call(SOCKET *sock,unsigned char mid) +{ + char buffer[MAX_ATM_ADDR_LEN+1]; + int error; + + switch (mid) { + case ATM_MSG_STATUS: /* 5.5.6.12 */ + { + CALL_STATE state; + + /* + * NOTE: T322 isn't implemented yet, but when it is, make sure + * to only stop it on STATUS iff the cause is + * ATM_CV_RESP_STAT_ENQ. Supplementary services break if + * you stop on any STATUS. + */ + state = q_fetch(&in_dsc,QF_call_state); + if (state == cs_null) break; /* clear call */ + if (sock->call_state == cs_rel_req || sock->call_state == + cs_rel_ind) return; + if (state != sock->call_state) + diag(COMPONENT,DIAG_WARN,"STATUS %s received in state %s", + cs_name[state],cs_name[sock->call_state]); + } + return; + default: + ; + } + switch (mid) { + case ATM_MSG_CALL_PROC: /* CONNECTING, WAIT_REL, REL_REQ */ + if (sock->state == ss_wait_rel || sock->state == ss_rel_req) { + send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG, + ATM_MSG_CALL_PROC); + return; + } + if (sock->state != ss_connecting) break; + /* check for 2nd CALL_PROC @@@ */ + STOP_TIMER(sock); + if (q_present(&in_dsc,QG_conn_id)) { + int vpci; + + vpci = q_fetch(&in_dsc,QF_vpi); + sock->pvc.sap_family = AF_ATMPVC; + sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); + sock->pvc.sap_addr.vpi = vpci; + sock->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); + diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d.%d.%d", + sock->pvc.sap_addr.itf,sock->pvc.sap_addr.vpi, + sock->pvc.sap_addr.vci); + } + START_TIMER(sock,T310); + sock->call_state = cs_out_proc; + return; + case ATM_MSG_CONNECT: /* CONNECTING, REL_REQ */ + if (sock->state == ss_rel_req) { + send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG,ATM_MSG_CONNECT); + return; + } + if (sock->state != ss_connecting) break; + STOP_TIMER(sock); + if (q_present(&in_dsc,QG_conn_id)) { + int vpci; + + vpci = q_fetch(&in_dsc,QF_vpi); + sock->pvc.sap_family = AF_ATMPVC; + sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); + sock->pvc.sap_addr.vpi = vpci; + sock->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); + diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d/%d.%d", + sock->pvc.sap_addr.itf,sock->pvc.sap_addr.vpi, + sock->pvc.sap_addr.vci); + } + error = 0; + if (!sock->pvc.sap_addr.vpi && !sock->pvc.sap_addr.vci) + error = -EPROTO; + /* more problems */ + if (error) { + set_error(sock,error); + send_release(sock,0); /* @@@ cause follows reason ??? */ + START_TIMER(sock,T308_1); + new_state(sock,ss_rel_req); + return; + } + send_connect_ack(sock); + /* @@@ fill in sock->remote */ + /* @@@ fill in traffic parameters */ + send_kernel(sock->id,0,as_okay,0,&sock->pvc,NULL,&sock->local, + &sock->sap,&sock->qos); + new_state(sock,ss_connected); +#ifdef Q2963_1 + sock->owner = 1; +#endif + if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) + &sock->remote,0) < 0) strcpy(buffer,""); + diag(COMPONENT,DIAG_INFO,"Active open succeeded (CR 0x%06X, " + "ID 0x%08x, to %s)",sock->call_ref,sock->id,buffer); + return; + case ATM_MSG_CONN_ACK: /* ACCEPTING, WAIT_REL, REL_REQ */ + diag(COMPONENT,DIAG_DEBUG,"CA vpi.vci=%d.%d", + sock->pvc.sap_addr.vpi,sock->pvc.sap_addr.vci); + if (sock->state == ss_wait_rel || sock->state == ss_rel_req) { + send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG, + ATM_MSG_CONN_ACK); + return; + } + if (sock->state != ss_accepting) break; + STOP_TIMER(sock); + send_kernel(sock->id,0,as_okay,0,NULL,NULL,&sock->local,&sock->sap, + NULL); + new_state(sock,ss_connected); +#ifdef Q2963_1 + sock->owner = 0; +#endif + if (atm2text(buffer,MAX_ATM_ADDR_LEN+1, (struct sockaddr *) + &sock->remote,0) < 0) strcpy(buffer,""); + diag(COMPONENT,DIAG_INFO,"Passive open succeeded (CR 0x%06X, " + "ID 0x%08x, from %s)",sock->call_ref,sock->id,buffer); + return; + case ATM_MSG_RELEASE: /* all states */ + { + unsigned char cause; + + cause = q_fetch(&in_dsc,QF_cause); + diag(COMPONENT,DIAG_DEBUG,"Cause %d (%s)",cause,cause > 127 ? + "invalid cause" : cause_text[cause]); + } + switch (sock->state) { + case ss_connecting: + set_error(sock,-ECONNREFUSED); + /* fall through */ + case ss_accepting: + set_error(sock,-ECONNRESET); /* ERESTARTSYS ? */ + send_release_complete(sock->sig,sock->call_ref,0); + SEND_ERROR(sock->id,sock->error); + STOP_TIMER(sock); + free_sock(sock); + return; + case ss_rel_req: + send_close(sock); + /* fall through */ + case ss_wait_rel: + STOP_TIMER(sock); + free_sock(sock); + return; +#ifdef Q2963_1 + case ss_mod_req: +#endif + STOP_TIMER(sock); + /* fall through */ +#ifdef Q2963_1 + case ss_mod_lcl: + case ss_mod_rcv: + case ss_mod_fin_ok: + case ss_mod_fin_fail: + case ss_mod_fin_ack: +#endif + case ss_connected: + diag(COMPONENT,DIAG_INFO,"Passive close (CR 0x%06X)", + sock->call_ref); +#ifdef Q2963_1 + if (timer_handler(sock->conn_timer) == on_T361) + STOP_TIMER(sock); +#endif + send_close(sock); + new_state(sock,ss_rel_ind); + return; + case ss_indicated: + /* fall through */ + case ss_proceeding: + send_release_complete(sock->sig,sock->call_ref,0); + new_state(sock,ss_zombie); + /* fall through */ + case ss_rel_ind: + return; + default: + send_release_complete(sock->sig,sock->call_ref,0); + /* @@@ should be ATM_CV_INCOMP_MSG */ + break; + } + break; + case ATM_MSG_RESTART: + set_error(sock,-ENETRESET); + /* fall through */ + case ATM_MSG_STATUS: /* fall through when clearing */ + case ATM_MSG_REL_COMP: /* basically any state (except LISTENING and + ZOMBIE) */ + { + unsigned char cause; + + if (mid != ATM_MSG_REL_COMP || !q_present(&in_dsc,QF_cause)) + cause = 0; + else { + cause = q_fetch(&in_dsc,QF_cause); + diag(COMPONENT,DIAG_DEBUG,"Cause %d (%s)",cause, + cause > 127 ? "invalid cause" : cause_text[cause]); + } + switch (sock->state) { + case ss_connecting: + set_error(sock,cause == ATM_CV_UNALLOC ? + -EADDRNOTAVAIL : cause == ATM_CV_RES_UNAVAIL || +#if defined(UNI31) || defined(UNI40) + cause == ATM_CV_UCR_UNAVAIL || +#endif + cause == ATM_CV_NO_ROUTE_DEST ? -EHOSTUNREACH : + cause == ATM_CV_NUM_CHANGED ? -EREMCHG : + cause == ATM_CV_DEST_OOO ? -EHOSTDOWN : + -ECONNREFUSED); + /* fall through */ + case ss_accepting: + set_error(sock,-ECONNRESET); /* ERESTARTSYS ? */ + SEND_ERROR(sock->id,sock->error); + STOP_TIMER(sock); + free_sock(sock); + return; + case ss_rel_req: + send_close(sock); + /* fall through */ + case ss_wait_rel: + STOP_TIMER(sock); + free_sock(sock); + return; +#ifdef Q2963_1 + case ss_mod_req: +#endif + STOP_TIMER(sock); + /* fall through */ +#ifdef Q2963_1 + case ss_mod_lcl: + case ss_mod_rcv: + case ss_mod_fin_ok: + case ss_mod_fin_fail: + case ss_mod_fin_ack: +#endif + case ss_connected: + diag(COMPONENT,DIAG_INFO,"Passive close (CR 0x%06X)", + sock->call_ref); +#ifdef Q2963_1 + if (timer_handler(sock->conn_timer) == on_T361) + STOP_TIMER(sock); +#endif + send_close(sock); + /* fall through */ + case ss_rel_ind: + new_state(sock,ss_wait_close); + return; + case ss_indicated: + /* fall through */ + case ss_proceeding: + new_state(sock,ss_zombie); + return; + default: + break; + } + break; /* fail */ + } + case ATM_MSG_ALERTING: + /* + * We basically ignore this junk message, except for the connection + * identifier it may carry. + */ + if (q_present(&in_dsc,QG_conn_id)) { + int vpci; + + vpci = q_fetch(&in_dsc,QF_vpi); + sock->pvc.sap_family = AF_ATMPVC; + sock->pvc.sap_addr.itf = get_itf(sock->sig,&vpci); + sock->pvc.sap_addr.vpi = vpci; + sock->pvc.sap_addr.vci = q_fetch(&in_dsc,QF_vci); + diag(COMPONENT,DIAG_DEBUG,"ITF.VPI.VCI: %d.%d.%d", + sock->pvc.sap_addr.itf,sock->pvc.sap_addr.vpi, + sock->pvc.sap_addr.vci); + } + return; + case ATM_MSG_NOTIFY: + /* silently ignore this junk */ + return; +#ifdef Q2963_1 +/* + * Buglet ahead: should actually test "call_state" + */ + case ATM_MSG_MODIFY_REQ: + if (sock->state != ss_connected || sock->owner) break; + sock->new_qos = sock->qos; + if (q_present(&in_dsc,QF_fw_pcr_01)) + sock->new_qos.rxtp.max_pcr = q_fetch(&in_dsc,QF_fw_pcr_01); + if (q_present(&in_dsc,QF_bw_pcr_01)) + sock->new_qos.txtp.max_pcr = q_fetch(&in_dsc,QF_bw_pcr_01); + send_kernel(sock->id,0,as_modify,ATM_MF_INC_RSV | ATM_MF_DEC_RSV | + ATM_MF_DEC_SHP,NULL,NULL,NULL,NULL,&sock->new_qos); + new_state(sock,ss_mod_rcv); + return; + case ATM_MSG_MODIFY_ACK: + if (sock->state != ss_mod_req) break; + STOP_TIMER(sock); + sock->qos = sock->new_qos; + if (q_present(&in_dsc,QG_bbrt)) send_conn_avail(sock); + send_kernel(sock->id,0,as_modify,ATM_MF_SET,NULL,NULL,NULL,NULL, + &sock->qos); + new_state(sock,ss_mod_fin_ok); + return; + case ATM_MSG_MODIFY_REJ: + if (sock->state != ss_mod_req) break; + STOP_TIMER(sock); + sock->error = -EAGAIN; + send_kernel(sock->id,0,as_modify,ATM_MF_SET,NULL,NULL,NULL,NULL, + &sock->qos); + new_state(sock,ss_mod_fin_fail); + return; + case ATM_MSG_CONN_AVAIL: + if (sock->state != ss_connected || !sock->owner) break; + STOP_TIMER(sock); + send_kernel(sock->id,0,as_modify,ATM_MF_SET,NULL,NULL,NULL,NULL, + &sock->qos); + new_state(sock,ss_mod_fin_ack); + return; +#endif + default: + diag(COMPONENT,DIAG_WARN,"Bad signaling message %d",mid); + send_status(sock->sig,sock,0,ATM_CV_UNKNOWN_MSG_TYPE,mid); + return; + } + diag(COMPONENT,DIAG_WARN, + "Signaling message %s is incompatible with state %s/%s (%d?%d)", + mid2name(mid),state_name[sock->state],cs_name[sock->call_state], + (int) sock->state,(int) sock->call_state); + send_status(sock->sig,sock,0,ATM_CV_INCOMP_MSG,mid); +} + + +void clear_all_calls(SIG_ENTITY *sig) +{ + SOCKET *curr,*next; + + for (curr = sockets; curr; curr = next) { + next = curr->next; + if (curr->sig == sig && curr->call_state != cs_null) + uni_call(curr,ATM_MSG_RESTART); + } +} + + +void clear_all_calls_on_T309(SIG_ENTITY *sig) +{ + clear_all_calls(sig); + t309 = NULL; +} + + +void saal_failure(SIG_ENTITY *sig) +{ + SOCKET *curr,*next; + + trace_msg("SAAL went down"); + for (curr = sockets; curr; curr = next) { + next = curr->next; + if (curr->sig == sig && curr->call_state != cs_null) + if (curr->call_state != cs_active) + uni_call(curr,ATM_MSG_RESTART); + else if (!t309) t309 = start_timer(T309_TIME,on_T309,sig); + } +} + + +void saal_okay(SIG_ENTITY *sig) +{ + SOCKET *curr; + + trace_msg("SAAL came up"); +#ifdef THOMFLEX + /* + * Some versions of the Thomson Thomflex 5000 won't do any signaling before + * they get a RESTART. Whenever SAAL comes up, this may indicate that the + * switch got booted, so we send that RESTART. We also have to clear all + * pending connections, which isn't that nice ... Note that the rest of the + * RESTART state machine is not implemented, so the RESTART ACKNOWLEDGE + * will yield a warning. + */ + { + Q_DSC dsc; + int size; + + clear_all_calls(sig); + q_create(&dsc,q_buffer,MAX_Q_MSG); + q_assign(&dsc,QF_msg_type,QMSG_RESTART); + q_assign(&dsc,QF_call_ref,0); + q_assign(&dsc,QF_rst_class,ATM_RST_ALL_VC); + if ((size = q_close(&dsc)) >= 0) to_signaling(sig,q_buffer,size); + } +#endif + if (!t309) return; + stop_timer(t309); + t309 = NULL; + for (curr = sockets; curr; curr = curr->next) + if (curr->sig == sig && curr->call_state != cs_null) + send_status_enq(curr); +} + + +static void process_uni(SIG_ENTITY *sig,void *msg) +{ + SOCKET *curr; + unsigned long call_ref; + unsigned char mid; + + call_ref = q_fetch(&in_dsc,QF_call_ref)^0x800000; + mid = q_fetch(&in_dsc,QF_msg_type); + if (mid == ATM_MSG_REST_ACK) return; + if (mid == ATM_MSG_RESTART) { /* 5.5.5.2 */ + int rst_class; + + rst_class = q_fetch(&in_dsc,QF_rst_class); + switch (rst_class) { + case ATM_RST_IND_VC: + { + int vpi,vci; + + if (!q_present(&in_dsc,QG_conn_id)) { + send_status(sig,NULL,0L,ATM_CV_MAND_IE_MISSING, + ATM_IE_CONN_ID); + return; + } + vpi = q_fetch(&in_dsc,QF_vpi); + vci = q_fetch(&in_dsc,QF_vci); + for (curr = sockets; curr; curr = curr->next) + if (curr->sig == sig && curr->pvc.sap_addr.vpi == vpi && + curr->pvc.sap_addr.vci == vci) break; + if (!curr) { + send_status(sig,NULL,0L,ATM_CV_INVALID_IE, + ATM_IE_CONN_ID); + return; + } + uni_call(curr,mid); + send_restart_ack(sig,vpi,vci); + } + break; + case ATM_RST_ALL_VC: + clear_all_calls(sig); + send_restart_ack(sig,0,0); + break; + default: + send_status(sig,NULL,0L,ATM_CV_INVALID_IE,ATM_IE_RESTART); + } + return; + } + if (!(call_ref & 0x7fffff)) { + return; /* bad things happen ... @@@ */ + } + for (curr = sockets; curr; curr = curr->next) + if (curr->sig == sig && curr->call_ref == call_ref) break; + diag(COMPONENT,DIAG_DEBUG,"FROM SAAL %d.%d.%d: %s (0x%02X) CR 0x%06lx for " + "0x%lx",S_PVC(sig),mid2name(((unsigned char *) msg)[5]), + ((unsigned char *)msg)[5],call_ref,curr ? curr->id : 0); + if (mid == ATM_MSG_SETUP) { + if (!curr) setup_call(sig,call_ref); + return; + } + if (mid == ATM_MSG_STATUS_ENQ) { + send_status(curr->sig,curr,call_ref,ATM_CV_RESP_STAT_ENQ); + return; + } + if (curr && q_present(&in_dsc,QF_ep_ref) && mid != ATM_MSG_ADD_PARTY && + mid != ATM_MSG_DROP_PARTY_ACK) + if (curr->ep_ref != q_fetch(&in_dsc,QF_ep_ref)) { + send_drop_party_ack(sig,call_ref,q_fetch(&in_dsc,QF_ep_ref), + ATM_CV_INV_EPR); + return; + } + if (!curr || curr->call_state == cs_null) { + if (mid != ATM_MSG_REL_COMP) + if (mid != ATM_MSG_STATUS) + send_release_complete(sig,call_ref,ATM_CV_INV_CR); + else if (q_fetch(&in_dsc,QF_call_state) != (int) cs_null) + send_release_complete(sig,call_ref,ATM_CV_INCOMP_MSG); + return; + } + uni_call(curr,mid); +} + + +static void abort_call(SIG_ENTITY *sig,unsigned char *msg,int size) +{ + SOCKET *curr; + unsigned long call_ref; + + if (size < 6) { + diag(COMPONENT,DIAG_ERROR,"message too short (%d bytes)",size); + return; + } + /* hope that at least the call ref is okay ... */ + call_ref = ((msg[3] << 16) | (msg[4] << 8) | msg[5])^0x800000; + diag(COMPONENT,DIAG_ERROR,"can't parse message - aborting the call " + "(CR 0x%06lx)",call_ref); + for (curr = sockets; curr; curr = curr->next) + if (curr->sig == sig && curr->call_ref == call_ref) { + uni_call(curr,ATM_MSG_RESTART); + break; + } + send_release_complete(sig,call_ref,ATM_CV_PROTOCOL_ERROR); +} + + +void to_uni(SIG_ENTITY *sig,void *msg,int size) +{ + if (q_open(&in_dsc,msg,size) < 0) { + abort_call(sig,msg,size); + return; + } + process_uni(sig,msg); + if (q_close(&in_dsc) < 0) + diag(COMPONENT,DIAG_ERROR,"q_close returned <0 in to_uni"); +} .