diff -ur --new-file old/atm/CHANGES new/atm/CHANGES
--- old/atm/CHANGES	Fri Dec 20 21:09:25 1996
+++ new/atm/CHANGES	Wed Jan 29 22:12:10 1997
@@ -1,3 +1,53 @@
+Version 0.25 to 0.26 (29-JAN-1997)
+====================
+
+Bug fixes
+---------
+
+ - ATMARP VCCs could stay around forever even after timing out, because the
+   process was not woken up.
+ - the ATMARP "fix" in 0.25 introduced an infinite loop. Fixed that one too.
+ - bit 8 in octets 6 and 7 of BLLI ("ext") were set to 1 instead of 0 when
+   using ATM_L2_USER/ATM_L3_USER (reported by ukl2@rz.uni-karlsruhe.de)
+ - qlib added silly offset (which fortunately happened to be zero most of the
+   time) to dumps of large fields (fixed by Jean-Francois Moine)
+ - IP over ATM restricted MTU changes to valid Ethernet MTU sizes
+ - LANE: fixed two bugs that crashed zeppelin when the connection to the
+   servers failed (by Marko Kiiskila)
+
+New features
+------------
+
+ - lib/ans.c now properly computes the length of the country prefix of E.164
+   addresses for reverse lookups (needs file /etc/e164_cc, see USAGE)
+ - if the new -m option is set, atmarpd now merges incoming calls into the
+   ATMARP table if the ATM address is known (see atmarpd.8 for details)
+ - included an RPM spec file (this is still very experimental)
+ - NICStAR driver now also works with IP over ATM (by Stuart Daniel)
+ - usage.txt: added description of how to run ATM NICs back-to-back (by Richard
+   Jones)
+
+Other changes
+-------------
+
+ - signaling traces now also include SAAL up/down transitions
+ - qgen no longer (unnecessarily) depends on libatm.a
+ - started work on letting qgen handle items that appear at more than one
+   place, e.g. repeated IEs (after an idea by Jean-Francois Moine)
+ - did some cleanup and added comments to qlib.[ch]
+ - added -m option to atmarp in config/redhat-4.0/atm.init
+ - added new make target "filenames" to generate a list of all the files which
+   are installed
+ - config/redhat-4.0 now contains an example hosts.atm file and also a Makefile
+   for more convenient installation
+ - atmsigd now reads atmsigd.conf before parsing the command-line options,
+   thereby allowing values set in the file to be superseeded
+ - atmsigd now also logs the internal reference and the caller's address on
+   calls establishment
+ - ilmid: very dirty hack to give switches some time to process ILMI cold start
+   (by Joseph Evans)
+
+
 Version 0.24 to 0.25 (20-DEC-1996)
 ====================
 
diff -ur --new-file old/atm/Makefile new/atm/Makefile
--- old/atm/Makefile	Wed Nov 13 17:05:49 1996
+++ new/atm/Makefile	Tue Jan 14 10:29:07 1997
@@ -14,6 +14,9 @@
 uninstall:
 		for n in $(DIRS); do $(MAKE) -C $$n uninstall || exit; done
 
+filenames:
+		@for n in $(DIRS); do $(MAKE) -s -C $$n filenames || exit; done
+
 depend:
 		for n in $(DIRS); do $(MAKE) -C $$n depend || exit; done
 
diff -ur --new-file old/atm/README new/atm/README
--- old/atm/README	Fri Dec 20 21:03:14 1996
+++ new/atm/README	Wed Jan 29 22:06:57 1997
@@ -1,4 +1,4 @@
-ATM on Linux, release 0.25 (pre-alpha) by Werner Almesberger, EPFL LRC
+ATM on Linux, release 0.26 (pre-alpha) by Werner Almesberger, EPFL LRC
 ====================================== werner.almesberger@lrc.di.epfl.ch
 
 This is experimental software. There are known major bugs and certainly
@@ -12,7 +12,7 @@
 The following network devices are supported:
   ATM over TCP pseudo device for "dry runs"
   Efficient Networks ENI155p-MF/U5 155 Mbps ATM adapter
-  IDT 77201 (NICStAR) (native ATM only)
+  IDT 77201 (NICStAR)
   Rolf Fiedler's TNETA1570 board / UniNET1570
   Zeitnet ZN1221/ZN1225 155 Mbps ATM adapter
 
diff -ur --new-file old/atm/Rules.make new/atm/Rules.make
--- old/atm/Rules.make	Fri Nov 29 17:31:38 1996
+++ new/atm/Rules.make	Tue Jan 14 10:27:32 1997
@@ -96,6 +96,11 @@
 		  cd $$dir; rm -f $$*; fi }; \
 		  $(PROCLIST)
 
+filenames:
+		@process() { if [ ! -z "$$3" ]; then dir=$$2; shift 2; \
+		  for n in $$*; do echo $$dir/$$n; done; fi }; \
+		  $(PROCLIST)
+
 depend:
 		$(CPP) -M *.c $(INCLUDES) -I../lib >.tmpdepend
 		mv .tmpdepend .depend
@@ -112,7 +117,9 @@
 y.tab.o:	y.tab.c
 		$(CC) -c $(CFLAGS_YACC) y.tab.c
 
+ifneq ($(NOLIBATMDEP),yes)
 $(PGMS) dummy:	../lib/libatm.a
+endif
 
 ifeq (.depend,$(wildcard .depend))
 include .depend
diff -ur --new-file old/atm/USAGE new/atm/USAGE
--- old/atm/USAGE	Fri Dec 20 21:25:32 1996
+++ new/atm/USAGE	Wed Jan 29 22:19:00 1997
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.25 (pre-alpha)
+Usage instructions  -  ATM on Linux, release 0.26 (pre-alpha)
 -------------------------------------------------------------
 
 For updates of ATM on Linux, please check the Web page at  
@@ -17,7 +17,7 @@
 In order to install this package, you need 
 
   - the package itself  
-    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.25.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.26.tar.gz  
   - the Linux kernel, version 2.0.25, e.g. from  
     ftp://ftp.cs.helsinki.fi/pub/Software/Linux/Kernel/v2.0/linux-2.0.25.tar.gz  
   - Perl, version 4 or 5 
@@ -33,7 +33,7 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.25.tar.gz
+tar xfz atm-0.26.tar.gz
 
 and the kernel source:
 
@@ -100,10 +100,8 @@
 The TNETA1570 driver is for a board developed by Rolf Fiedler at TU 
 Chemnitz, see also  ftp://ftp.infotech.tu-chemnitz.de/pub/linux-atm .
 
-The IDT 77201 driver currently only supports native ATM, i.e. IP over ATM 
-or LANE are not possible (and should not be tried). A fix for this is being 
-worked on. Note that the file drivers/atm/nicstar.h contains a few 
-configurable settings.
+Note that the file drivers/atm/nicstar.h contains a few configurable 
+settings for the IDT 77201 driver.
 
 Support for a simple type of serial consoles is automatically added by the 
 ATM patch. If you're using LILO, you can enable it by putting a line like
@@ -482,8 +480,22 @@
 the first suitable one will be chosen, i.e. if an application explicitly 
 requests only SVC addresses, any PVC addresses will be ignored.
 
-Support for ANS, a DNS-based resolution mechanism, which is currently in 
-the process of being standardized by ATM Forum, is for further study.
+
+ANS
+---
+
+If you have access to the ATM Name Service (ANS, e.g because you've 
+installed the ANS extension), you can use it instead of or in addition to 
+the hosts file by specifying the host that runs ANS in the /etc/resolv.conf 
+file.
+
+For performing reverse lookups of E.164 addresses, the list of telephony 
+country codes needs to be known. That list can be obtained from  
+http://www.itu.ch/itudoc/itu-t/lists/  the file has a name of the form 
+tf_cc_e_*.rtf The script atm/lib/rtf2e164_cc.pl can be used to create the 
+E.164 county codes table, e.g.
+
+perl rtf2e164_cc.pl <tf_cc_e_13130.rtf >/etc/e164_cc
 
 
 Signaling demon
@@ -572,6 +584,67 @@
 you can also use addresses with a different prefix and an ESI that doesn't 
 correspond to any ESI your adapters have. The value of the selector byte 
 (SEL) is ignored.
+
+
+Running two ATM NICs back-to-back
+---------------------------------
+
+It is also possible to run with two ATM NICs connected back-to-back, and no 
+switch in between*. This is great for simple test environments.
+
+  *  This section was written by Richard Jones  rjones@imcl.com . Comments 
+    should be directed to me as well as to Werner.
+
+First, if you're using UTP or STP-5, you need a suitable cable. Our 
+experience with standard 100Base-T back-to-back cables was not good. It 
+appears that the pin-out they use is different. After some false starts, we 
+found that the following cable works:
+
+RJ45                            RJ45
+   1        ------------        7
+   2        ------------        8
+
+   7        ------------        1
+   8        ------------        2
+
+Pins 3, 4, 5, 6 unconnected.
+
+You can also make up a loopback cable with 1 - 7 and 2 - 8 connected for 
+ultra-cheap setups.
+
+Here we have two machines called "virgil" and "nestor". Substitute your own 
+names as necessary.
+
+One side of the ATM connection needs to use the network version of atmsigd 
+and the other side should use the normal user version. So here on nestor we 
+start atmsigd with:
+
+atmsigd -b -N
+
+and on virgil with:
+
+atmsigd -b
+
+Without a switch, you won't be able to use ILMI. Instead, create a 
+/etc/hosts.atm file containing two dummy addresses. Our ATM hosts file 
+contains:
+
+47.0005.80FFE1000000F21A26D8.0020EA000EE0.00    nestor-atm
+47.0005.80FFE1000000F21A26D8.0020D4102A80.00    virgil-atm
+
+These are completely spurious addresses, of course, but as long as you're 
+not connected to a public or private ATM network, I don't think it matters. 
+To set the address correctly in the driver, we use:
+
+atmaddr -a virgil-atm
+
+on virgil, and:
+
+atmaddr -a nestor-atm
+
+on nestor. Now start atmarpd on both machines in the normal way. Now you 
+(should) have a working ATM set-up. To get IP over ATM and Arequipa 
+working, just follow the instructions in section "IP over ATM".
 
 
 Q.2931 message dumper
diff -ur --new-file old/atm/VERSION new/atm/VERSION
--- old/atm/VERSION	Fri Dec 20 20:55:19 1996
+++ new/atm/VERSION	Tue Jan 14 12:57:35 1997
@@ -1 +1 @@
-0.25
+0.26
diff -ur --new-file old/atm/arpd/arp.c new/atm/arpd/arp.c
--- old/atm/arpd/arp.c	Wed Nov 27 19:08:38 1996
+++ new/atm/arpd/arp.c	Mon Jan 13 21:01:58 1997
@@ -1,6 +1,6 @@
 /* arp.c - ARP state machine */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #include <stdlib.h>
@@ -496,6 +496,17 @@
 	  "(%d -> %d)",entry->itf->number,itf->number);
     if (!entry->addr) entry->addr = alloc(sizeof(*addr));
     *entry->addr = *addr;
+    if (merge) {
+	ENTRY *incoming;
+
+	while ((incoming = lookup_incoming(addr))) {
+	    STOP_TIMER(incoming);
+	    Q_REMOVE(unknown_incoming,incoming);
+	    incoming->vccs->entry = entry;
+	    Q_INSERT_HEAD(entry->vccs,incoming->vccs);
+	    free(incoming);
+	}
+    }
     for (walk = entry->vccs; walk; walk = walk->next)
 	if (!walk->connecting)
 	    if (set_ip(walk->fd,ip) < 0)
diff -ur --new-file old/atm/arpd/atmarpd.8 new/atm/arpd/atmarpd.8
--- old/atm/arpd/atmarpd.8	Mon Oct 14 18:54:42 1996
+++ new/atm/arpd/atmarpd.8	Mon Jan 13 21:12:57 1997
@@ -1,4 +1,4 @@
-.TH ATMARPD 8 "Oct 14, 1996" "Linux" "Maintenance Commands"
+.TH ATMARPD 8 "Jan 13, 1997" "Linux" "Maintenance Commands"
 .SH NAME
 atmarpd \- ATMARP demon
 .SH SYNOPSIS
@@ -7,6 +7,7 @@
 .RB [ \-d ]
 .RB [ \-D\ \fIdirectory\fP ]
 .RB [ \-l\ \fIlogfile\fP ]
+.RB [ \-m ]
 .RB [ \-n ]
 .SH DESCRIPTION
 \fBatmarpd\fP implements the ATMARP protocol as specified in RFC1577 and
@@ -38,6 +39,14 @@
 Write diagnostic messages to the specified file instead of to standard
 error. The special name \fBsyslog\fP is used to send diagnostics to the
 system logger.
+.IP \fB\-m\fP
+Enables merging of incoming calls if the address is known. 
+An incoming connection on which no InARP reply has been received yet, but
+which originates from an ATM address for which an ATMARP entry already
+exists, is automatically added to that entry. This assumes that there is a
+1:1 mapping between IP addresses and ATMARP addresses. By default, this
+assumption is not made, which frequently results in the setup of duplicate
+connections.
 .IP \fB\-n\fP
 Prints addresses in numeric format only, i.e. no address to name translation
 is attempted.
diff -ur --new-file old/atm/arpd/atmarpd.c new/atm/arpd/atmarpd.c
--- old/atm/arpd/atmarpd.c	Thu Sep 26 21:24:39 1996
+++ new/atm/arpd/atmarpd.c	Mon Jan 13 21:13:10 1997
@@ -1,6 +1,6 @@
 /* atmarpd.c - ATMARP demon */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #include <stdlib.h>
@@ -36,11 +36,13 @@
 ENTRY *unknown_incoming = NULL;
 int debug;
 int pretty = A2T_PRETTY | A2T_NAME;
+int merge = 0;
 
 
 static void usage(const char *name)
 {
-    fprintf(stderr,"usage: %s [ -b ] [ -d ] [ -l logfile ] [ -n ]\n",name);
+    fprintf(stderr,"usage: %s [ -b ] [ -d ] [ -l logfile ] [ -m ] [ -n ]\n",
+      name);
     exit(1);
 }
 
@@ -58,7 +60,7 @@
     set_verbosity(NULL,DIAG_INFO);
     dump_dir = DUMP_DIR;
     background = 0;
-    while ((c = getopt(argc,argv,"bdD:l:np")) != EOF)
+    while ((c = getopt(argc,argv,"bdD:l:mnp")) != EOF)
 	switch (c) {
 	    case 'b':
 		background = 1;
@@ -72,6 +74,9 @@
 		break;
 	    case 'l':
 		set_logfile(optarg);
+		break;
+	    case 'm':
+		merge = 1;
 		break;
 	    case 'n': /* @@@ was planned for NSAP matching */
 		pretty = A2T_PRETTY;
diff -ur --new-file old/atm/arpd/io.c new/atm/arpd/io.c
--- old/atm/arpd/io.c	Thu Sep 26 16:50:36 1996
+++ new/atm/arpd/io.c	Mon Jan 13 21:25:07 1997
@@ -1,6 +1,6 @@
 /* io.c - I/O operations */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #include <stdio.h>
@@ -214,17 +214,25 @@
     vcc->active = 0;
     vcc->connecting = 0;
     vcc->fd = fd;
-#if 0
-    for (itf = itfs; itf; itf = itf->next) {
-	entry = lookup_addr(itf,&addr);
-	if (entry) {
-	    vcc->flags = entry->flags;
-	    vcc->entry = entry;
-	    Q_INSERT_HEAD(entry->vccs,vcc);
-	    return;
+    /* the following code probably belongs to arp.c ... */
+    if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,pretty) <
+      0) strcpy(buffer,"<atm2text error>");
+    diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer);
+    if (merge) {
+	ITF *itf;
+	for (itf = itfs; itf; itf = itf->next) {
+	    entry = lookup_addr(itf,&addr);
+	    if (entry) {
+		vcc->entry = entry;
+		Q_INSERT_HEAD(entry->vccs,vcc);
+		if (entry->state == as_valid)
+		    if (set_ip(vcc->fd,entry->ip) < 0)
+			diag(COMPONENT,DIAG_ERROR,"set_ip: %s",
+			  strerror(errno));
+		return;
+	    }
 	}
     }
-#endif
     entry = alloc_entry(1);
     entry->state = as_invalid;
     entry->addr = alloc_t(struct sockaddr_atmsvc);
@@ -232,9 +240,6 @@
     Q_INSERT_HEAD(unknown_incoming,entry);
     vcc->entry = entry;
     Q_INSERT_HEAD(entry->vccs,vcc);
-    if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *) &addr,pretty) <
-      0) strcpy(buffer,"<atm2text error>");
-    diag(COMPONENT,DIAG_DEBUG,"Incoming call from %s",buffer);
     incoming_call(vcc);
 }
 
diff -ur --new-file old/atm/arpd/table.c new/atm/arpd/table.c
--- old/atm/arpd/table.c	Sun Oct 13 19:04:39 1996
+++ new/atm/arpd/table.c	Mon Jan 13 21:02:05 1997
@@ -1,6 +1,6 @@
 /* table.c - ATMARP table */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #include <stdio.h>
@@ -55,11 +55,17 @@
     ENTRY *walk;
 
     for (walk = itf->table; walk; walk = walk->next)
-	if (walk->addr && (*walk->addr->sas_addr.prv ? *addr->sas_addr.prv &&
-	  !memcmp(addr->sas_addr.prv,walk->addr->sas_addr.prv,ATM_ESA_LEN) :
-	  !*addr->sas_addr.prv) && ((*walk->addr->sas_addr.pub ?
-	  *addr->sas_addr.pub && !strcmp(addr->sas_addr.pub,
-	  walk->addr->sas_addr.pub) : !*addr->sas_addr.pub))) break;
+	if (walk->addr && atm_equal(walk->addr,addr,0,0)) break;
+    return walk;
+}
+
+
+ENTRY *lookup_incoming(const struct sockaddr_atmsvc *addr)
+{
+    ENTRY *walk;
+
+    for (walk = unknown_incoming; walk; walk = walk->next)
+	if (walk->addr && atm_equal(walk->addr,addr,0,0)) break;
     return walk;
 }
 
diff -ur --new-file old/atm/arpd/table.h new/atm/arpd/table.h
--- old/atm/arpd/table.h	Thu Oct 10 22:25:43 1996
+++ new/atm/arpd/table.h	Mon Jan 13 21:03:08 1997
@@ -1,6 +1,6 @@
 /* table.h - ATMARP table */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #ifndef TABLE_H
@@ -70,13 +70,14 @@
 extern ITF *itfs;
 extern ENTRY *unknown_incoming;
 
-extern int pretty;
+extern int pretty,merge;
 
 
 ENTRY *alloc_entry(int svc);
 
 ENTRY *lookup_ip(const ITF *itf,unsigned long ip);
 ENTRY *lookup_addr(const ITF *itf,const struct sockaddr_atmsvc *addr);
+ENTRY *lookup_incoming(const struct sockaddr_atmsvc *addr);
 
 void dump_all(void);
 
diff -ur --new-file old/atm/atm-0.26-1.spec new/atm/atm-0.26-1.spec
--- old/atm/atm-0.26-1.spec	Thu Jan  1 01:00:00 1970
+++ new/atm/atm-0.26-1.spec	Tue Jan 14 16:03:13 1997
@@ -0,0 +1,135 @@
+Summary:	ATM (Asynchronous Transfer Mode)
+Name:		atm
+Version:	0.26
+Release:	1
+Source:		atm-0.26.tar.gz
+Source:		lrcftp.epfl.ch:/pub/linux/atm/dist/atm-0.26.tar.gz
+Copyright:	distributable
+Group:		Networking/ATM
+ExclusiveArch:	i386
+ExclusiveOS:	Linux
+
+%description
+Programs, libraries, and configuration files required for using ATM
+on Linux. Note that you still need to patch your kernel.
+
+%package ans
+Summary:	ANS (ATM Name Server)
+Group:		Networking/ATM
+
+%description ans
+ANS is a version of BIND with extensions for ATM. This package contains
+the complete BIND distribution, including tools like nslookup and dig.
+
+%prep
+
+%setup -n atm
+
+%build
+make
+cd extra
+make tcpdump
+make ans
+
+%install
+INSTPREFIX=/usr make -e
+cd config/redhat-4.0
+make install
+cd ../../extra
+INSTPREFIX=/usr make -e install
+
+%files ans
+/usr/lib/libresolv.a
+/usr/include/arpa/inet.h
+/usr/include/arpa/nameser.h
+/usr/include/netdb.h
+/usr/include/resolv.h
+/usr/include/sys/bitypes.h
+/usr/lib/lib44bsd.a
+/usr/sbin/named
+/usr/sbin/named-xfer
+/usr/sbin/named.restart
+/usr/sbin/named.reload
+/usr/sbin/ndc
+/usr/bin/nslookup
+/usr/lib/nslookup.help
+/usr/bin/host
+/usr/bin/dig
+/usr/bin/dnsquery
+/usr/bin/addr
+/usr/man/man1/dig.1
+/usr/man/man1/host.1
+/usr/man/man1/dnsquery.1
+/usr/man/man8/named.8
+/usr/man/man8/named.reload.8
+/usr/man/man8/named.restart.8
+/usr/man/man8/ndc.8
+/usr/man/man8/named-xfer.8
+/usr/man/man8/nslookup.8
+/usr/man/man3/gethostbyname.3
+/usr/man/man3/resolver.3
+/usr/man/man3/getnetent.3
+/usr/man/man5/resolver.5
+/usr/man/man7/hostname.7
+/usr/man/man7/mailaddr.7
+
+%files
+%doc README
+%doc USAGE
+%config /etc/sysconfig/atm
+%config /etc/atmsigd.conf
+%config /etc/sysconfig/network-scripts/ifcfg-atm0
+%config /etc/sysconfig/network-scripts/ifup-atm
+%config /etc/hosts.atm
+/usr/sbin/tcpdump
+/usr/lib/libatm.a
+/usr/lib/libatmd.a
+/usr/lib/libarequipa.a
+/usr/include/atm.h
+/usr/include/atmd.h
+/usr/include/atmsap.h
+/usr/include/arequipa.h
+/usr/sbin/atmaddr
+/usr/sbin/atmtcp
+/usr/sbin/zntune
+/usr/bin/atmdiag
+/usr/bin/atmdump
+/usr/bin/sonetdiag
+/usr/man/man8/atmaddr.8
+/usr/man/man8/atmdiag.8
+/usr/man/man8/atmdump.8
+/usr/man/man8/atmtcp.8
+/usr/sbin/clip
+/usr/sbin/atmarp
+/usr/man/man8/clip.8
+/usr/man/man8/atmarp.8
+/usr/bin/aping
+/usr/bin/aread
+/usr/bin/awrite
+/usr/bin/br
+/usr/bin/bw
+/usr/bin/ttcp_atm
+/usr/bin/window
+/usr/sbin/delay
+/usr/sbin/ed
+/usr/sbin/encopy
+/usr/sbin/endump
+/usr/sbin/zndump
+/usr/sbin/znth
+/usr/sbin/atmsigd
+/usr/man/man4/atmsigd.conf.4
+/usr/man/man8/atmsigd.8
+/usr/sbin/atmarpd
+/usr/man/man8/atmarpd.8
+/usr/sbin/ilmid
+/usr/sbin/zeppelin
+/usr/man/man8/zeppelin.8
+/usr/sbin/les
+/usr/sbin/bus
+/usr/sbin/lecs
+/usr/man/man8/les.8
+/usr/man/man8/lecs.8
+/usr/man/man8/bus.8
+/usr/sbin/arequipad
+/usr/man/man8/arequipad.8
+/usr/man/man7/qos.7
diff -ur --new-file old/atm/atm.patch new/atm/atm.patch
--- old/atm/atm.patch	Fri Dec 20 21:49:23 1996
+++ new/atm/atm.patch	Wed Jan 29 22:21:25 1997
@@ -20,7 +20,7 @@
  	ls *.o > .allmods; \
  	echo $$MODULES | tr ' ' '\n' | sort | comm -23 .allmods - > .misc; \
 --- ref/Documentation/Configure.help	Sat Oct  5 10:13:56 1996
-+++ work/Documentation/Configure.help	Fri Nov 15 20:52:20 1996
++++ work/Documentation/Configure.help	Wed Jan 29 22:06:37 1997
 @@ -674,6 +674,14 @@
    say "386" or "486" here even if running on a Pentium or PPro
    machine. If you don't know what to do, say "386".
@@ -36,7 +36,7 @@
  Compile the kernel into the ELF object format 
  CONFIG_ELF_KERNEL
    ELF (Executable and Linkable Format) is a format for libraries and
-@@ -1194,6 +1202,118 @@
+@@ -1194,6 +1202,116 @@
    with major number 36 and minor number 0 using mknod ("man mknod"),
    you can read some network related routing information from that
    file. Everything you write to that file will be discarded.
@@ -149,9 +149,7 @@
 +IDT 77201 (NICStAR)
 +CONFIG_ATM_NICSTAR
 +  Driver for the IDT 77201 (NICStAR) ATM adapter. Written by Matt Welsh
-+  and Stuart Daniel <stuartd@eecs.umich.edu>. Note that this driver
-+  currently only works with native ATM (i.e. not with IP over ATM or
-+  LANE).
++  and Stuart Daniel <stuartd@eecs.umich.edu>.
  
  SCSI support?
  CONFIG_SCSI
@@ -2715,7 +2713,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/drivers/atm/eni.h	Fri Dec 20 20:53:55 1996
++++ work/drivers/atm/eni.h	Wed Jan 29 19:28:19 1997
 @@ -0,0 +1,114 @@
 +/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */
 + 
@@ -3401,7 +3399,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/drivers/atm/suni.h	Fri Nov 15 21:04:20 1996
++++ work/drivers/atm/suni.h	Wed Jan 29 19:28:19 1997
 @@ -0,0 +1,219 @@
 +/* drivers/atm/suni.h - PMC SUNI (PHY) declarations */
 + 
@@ -6130,7 +6128,7 @@
 + 
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/drivers/atm/zatm.h	Fri Nov 15 21:04:48 1996
++++ work/drivers/atm/zatm.h	Wed Jan 29 19:28:56 1997
 @@ -0,0 +1,172 @@
 +/* drivers/atm/zatm.h - ZeitNet ZN122x device driver declarations */
 +
@@ -6342,7 +6340,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/drivers/atm/tneta1570.h	Fri Nov 15 21:05:09 1996
++++ work/drivers/atm/tneta1570.h	Wed Jan 29 19:29:23 1997
 @@ -0,0 +1,390 @@
 +/* drivers/atm/tneta1570.h - TI TNETA1570 (SAR) declarations */
 + 
@@ -8392,8 +8390,8 @@
 +	return 0;
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/drivers/atm/nicstar.h	Fri Dec 20 21:02:13 1996
-@@ -0,0 +1,305 @@
++++ work/drivers/atm/nicstar.h	Wed Jan 29 19:24:54 1997
+@@ -0,0 +1,319 @@
 +/* nicstar.h, M. Welsh (matt.welsh@cl.cam.ac.uk)
 + * Definitions for IDT77201.
 + *
@@ -8450,8 +8448,9 @@
 +#define NICSTAR_CBR 1    /* Constant bit rate support */
 +#undef NICSTAR_RCQ   /* Raw cell queue support; just prints out raw cells */
 +#define NICSTAR_PARANOID 1 /* adds some extra checks */
-+#undef TEST_LOOPBACK /* enables PHY-level loopback */
-+#undef ATM_013
++#undef TEST_LOOPBACK       /* enables PHY-level loopback */
++#undef ATM_013             /* should be removed soon; compat w/ ATM rel 0.13 */
++#undef NICSTAR_LBUFCNT     /* for debugging; keeps count of length of LGBUF_Q */
 +
 +/* Kernel definitions ******************************************************/
 +
@@ -8461,8 +8460,13 @@
 +#define NICSTAR_IO_SIZE (4*1024)
 +
 +#define SM_BUFSZ 52
++#define SM_BUF_DATALEN (SM_BUFSZ - 4)
 +#define NUM_SM_BUFS 170
-+#define LG_BUFSZ 2048
++#define LG_BUFSZ 16384
++#define LG_BUFMSK 0x06000000  /* 16k = 0x06000000
++				  8k = 0x04000000
++				  4k = 0x02000000
++				  2k = 0x00000000 */
 +#define RAWCELLSZ 64
 +#define NUM_LG_BUFS 32
 +#define MAX_SDU_BUFS 32  /* Max number of buffers in an AAL5 SDU */
@@ -8660,6 +8664,8 @@
 +} nicstar_rcq;
 +#endif
 +
++typedef struct sk_buff *skb_ptr;
++
 +typedef struct nicstar_dev {
 +
 +  int index;                         /* Device index */
@@ -8690,9 +8696,15 @@
 +  nicstar_scq *scq;
 +  caddr_t scq_orig;
 +
++  skb_ptr rx_lg_skb[NUM_LG_BUFS];
++
 +  struct atm_dev *dev;
 +  nicstar_vcimap vci_map[NUM_VPI * NUM_VCI];
 +  unsigned char tmp_cell[48];
++
++#ifdef NICSTAR_LBUFCNT
++  int lbuf_cnt;
++#endif
 +  
 +} nicstar_dev, *nicstar_devp;
 +
@@ -8700,8 +8712,8 @@
 +
 +#endif /* _LINUX_NICSTAR_H_ */
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/drivers/atm/nicstar.c	Fri Dec 20 21:02:26 1996
-@@ -0,0 +1,1926 @@
++++ work/drivers/atm/nicstar.c	Wed Jan 29 19:25:00 1997
+@@ -0,0 +1,2074 @@
 +/* nicstar.c, M. Welsh (matt.welsh@cl.cam.ac.uk)
 + *
 + * Linux driver for the IDT77201 NICStAR PCI ATM controller.
@@ -8710,6 +8722,22 @@
 + * 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 <small_buffer_data> 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
@@ -8729,8 +8757,9 @@
 + * M. Welsh, 6 July 1996
 + *
 + */
-+
-+static char _modversion[] = "@(#) nicstar.c, $Id: nicstar.c,v 1.31 1996/11/25 21:05:54 swdaniel Exp $";
++/*
++static char _modversion[] = "@(#) nicstar.c, $Id: nicstar.c,v 1.35 1997/01/08 22:27:12 swdaniel Exp $";
++*/
 +
 +#include <linux/fs.h>
 +#include <asm/system.h>
@@ -8756,13 +8785,14 @@
 +#include <linux/atmdev.h>
 +#include <linux/sonet.h>
 +
-+
 +#include <linux/version.h>
 +char kernel_version[] = UTS_RELEASE;
 +
 +#include "nicstar.h"
++#include "../../net/atm/protocols.h" /* hack! */
 +
 +#undef NICSTAR_DEBUG              /* Define for verbose debugging output */
++#define NICSTAR_RC_FLAG 1
 +
 +#ifdef NICSTAR_DEBUG
 +#define PRINTK(args...) printk(args)
@@ -8825,6 +8855,8 @@
 +static void close_cbr_final (nicstar_devp node, struct atm_vcc *vcc);
 +#endif
 +static int create_scq (struct nicstar_scq **scq, int size);
++static u32 linearize_buffer (struct nicstar_dev *node, struct sk_buff *skb,
++			     struct atm_vcc *vcc);
 +
 +/* Module entry points *****************************************************/
 +
@@ -9687,24 +9719,32 @@
 +    return -ENOMEM;
 +  }
 +
-+  node->lg_bufs = (caddr_t)kmalloc(LG_BUFSZ * NUM_LG_BUFS, GFP_KERNEL);
++#if 0
++  /* Old code for handling large buffers */
++  node->lg_bufs = (caddr_t)kmalloc((SM_BUFSZ + LG_BUFSZ) * NUM_LG_BUFS, GFP_KERNEL);
 +  if (!node->lg_bufs) {
 +    printk("nicstar%d: Can't allocate %d %d-byte large buffers!\n",
 +	   node->index, NUM_LG_BUFS, LG_BUFSZ);
 +    return -ENOMEM;
 +  }
-+
-+  printk("nicstar%d: Small buffers at 0x%x, Large buffers at 0x%x.\n",
-+	 node->index, (u32)node->sm_bufs, (u32)node->lg_bufs);
++  p = node->lg_bufs + SM_BUFSZ;
++#endif
++  for (i = 0; i < NUM_LG_BUFS; i++) {
++    node->rx_lg_skb[i] = alloc_skb(SM_BUFSZ + LG_BUFSZ, GFP_KERNEL);
++    skb_reserve(node->rx_lg_skb[i],SM_BUFSZ);
++  }
 +
 +#if NICSTAR_RCQ
-+  node->rcq.rcq_head = (u32) node->lg_bufs;
-+  node->rcq.rcq_base = (u32) node->lg_bufs;
++  node->rcq.rcq_head = (u32) rx_lg_skb[0]->data;
++  node->rcq.rcq_base = (u32) rx_lg_skb[0]->data;
 +  /* Manual says we should check head vs tail here; but tail reg
 +     isn't updated until after we turn on RX path. Moved check to
 +     end of init_module... */
 +#endif
 +
++#if NICSTAR_LBUFCNT
++  node->lbuf_cnt = NUM_LG_BUFS - 1;
++#endif
 +  for (i = 0; i < NUM_SM_BUFS; i+=2) {
 +    push_rxbufs(node, 0, 
 +		(u32)(node->sm_bufs + (i*SM_BUFSZ) + 4),
@@ -9715,16 +9755,19 @@
 +
 +  for (i = 0; i < NUM_LG_BUFS; i+=2) {
 +    push_rxbufs(node, 1, 
-+		(u32)(node->lg_bufs + (i*LG_BUFSZ)),
-+		(u32)(node->lg_bufs + (i*LG_BUFSZ)),
-+		(u32)(node->lg_bufs + ((i+1)*LG_BUFSZ)),
-+		(u32)(node->lg_bufs + ((i+1)*LG_BUFSZ)));
++		(u32)(node->rx_lg_skb[i]->data),
++		(u32)(node->rx_lg_skb[i]->data),
++		(u32)(node->rx_lg_skb[i+1]->data),
++		(u32)(node->rx_lg_skb[i+1]->data));
 +  }
 +
 +  skb_queue_head_init(&node->rx_skb_queue);
 +  for (i = 0; i < NUM_RX_SKB; i++) {
 +    struct sk_buff *skb;
-+    skb = alloc_skb(MAX_SDU_BUFS * sizeof(struct iovec), GFP_KERNEL);
++    skb = alloc_skb(
++		    SM_BUFSZ > MAX_SDU_BUFS * sizeof(struct iovec) ?
++		    SM_BUFSZ :  MAX_SDU_BUFS * sizeof(struct iovec),
++		    GFP_KERNEL);
 +    if (!skb) {
 +      printk("nicstar%d: Can't allocate Rx skb!\n",node->index);
 +      return -ENOMEM;
@@ -9789,12 +9832,12 @@
 +
 +#if NICSTAR_RCQ
 +  /* Turn on everything */
-+  writel(0x21801c38 | CFG_VPIVCI | 0x200 | 0x800, node->membase+CFG);
++  writel(0x21801c38 | LG_BUFMSK | CFG_VPIVCI | 0x200 | 0x800, node->membase+CFG);
 +  /* | 0x800 turns on raw cell interrupts */
 +  /* | 0x200 for raw cell receive */
 +  /* | 0x8000 to receive cells that don't map via RCT (not there/closed) */
 +#else
-+  writel(0x21801c38 | CFG_VPIVCI, node->membase+CFG); /* Turn on everything */
++  writel(0x21801c38 | LG_BUFMSK | CFG_VPIVCI, node->membase+CFG); /* Turn on everything */
 +#endif
 +
 +  while(CMD_BUSY(node));
@@ -10276,6 +10319,10 @@
 +      lg_buf = buf_addr;
 +    } else {
 +      u32 buf;
++
++#ifdef NICSTAR_LBUFCNT
++      node->lbuf_cnt += 2;
++#endif
 +      buf = buf_addr;
 +      push_rxbufs(node, 1, lg_buf, lg_buf, buf, buf);
 +      lg_buf = 0x0;
@@ -10347,7 +10394,12 @@
 +    skb->atm.iovcnt++;
 +    vcc->stats->rx++;
 +    skb->atm.timestamp = xtime;
-+    vcc->push(vcc, skb);
++    if ((u32) vcc->push != (u32) atm_push_raw) {
++      skb = (struct sk_buff *) linearize_buffer(node,skb,vcc);
++    }
++    if (skb) {
++      vcc->push(vcc, skb);
++    }
 +    mapval->rx_skb = NULL;
 +    return;
 +  }
@@ -10364,9 +10416,22 @@
 +    iov->iov_len = aal5_len - skb->len;
 +    skb->len = aal5_len;
 +    skb->atm.iovcnt++;
++#ifdef NICSTAR_LBUFCNT
++    node->lbuf_cnt -= (skb->atm.iovcnt - 1);
++#endif
++    PRINTK("Received %d-buffer message %x\n",skb->atm.iovcnt,skb);
 +    vcc->stats->rx++;
-+    
-+    vcc->push(vcc, skb);
++
++
++    if ((u32) vcc->push != (u32) atm_push_raw) {
++      skb = (struct sk_buff *) linearize_buffer(node,skb,vcc);
++      PRINTK("rcv %d-buf len %d\n",skb->atm.iovcnt,skb->len);
++    }
++    PRINTK("after is rcv %d-buf len %d\n",skb->atm.iovcnt,skb->len);
++
++    if (skb) {
++      vcc->push(vcc, skb);
++    }
 +    mapval->rx_skb = NULL;
 +  } else {
 +    iov->iov_len = (entry->status & 0x1ff) * 48;
@@ -10390,6 +10455,15 @@
 +  return;
 +}
 +
++static void nicstar_rx_skb_destructor (struct sk_buff *skb) {
++  if (skb->count > 1) return;
++#if NICSTAR_RC_FLAG
++  nicstar_free_rx_skb((struct atm_vcc *) skb->atm.recycle_buffer,skb);
++#else
++  nicstar_free_rx_skb((struct atm_vcc *) skb->atm.vcc,skb);
++#endif
++}
++
 +static void nicstar_free_rx_skb(struct atm_vcc *vcc, struct sk_buff *skb) {
 +  nicstar_devp node = vcc->dev->dev_data;
 +  struct iovec *iov;
@@ -10397,37 +10471,52 @@
 +  u32 ptr_chk;
 +
 +  if (!skb->atm.iovcnt) {
-+    /* XXX mdw: Eventually support this for single-cell transfers */
-+    printk("nicstar%d: free_rx_skb on non-iovec skb?\n",
-+	   node->index);
-+    return;
++    for (i = 0; i < NUM_LG_BUFS; i++) {
++      if (node->rx_lg_skb[i]->head == skb->head) break;
++    }
++    if (i < NUM_LG_BUFS) {
++      /* this had better be a large buffer on the rebound (SWD) */
++      /* restore data pointers */
++      (node->rx_lg_skb[i])->data = (node->rx_lg_skb[i])->head + SM_BUF_DATALEN;
++      (node->rx_lg_skb[i])->len = 0;
++      (node->rx_lg_skb[i])->tail = (node->rx_lg_skb[i])->data;
++      free_rx_buf(node, 1, (u32)((node->rx_lg_skb[i])->data));
++      return;
++    }
++    else {
++      /* i > NUM_LG_BUFS ==> small buffer, which has already */
++      /*   been recycled; just requeue this skb after resetting */
++      /*   tail */
++      return;
++    }
 +  }
-+
-+  iov = (struct iovec *)skb->data;
-+  for (i = 0; i < skb->atm.iovcnt; i++) {
-+    if (((unsigned char *)iov[i].iov_base >= (unsigned char *)node->sm_bufs) &&
-+	((unsigned char *)iov[i].iov_base < (unsigned char *)node->sm_bufs + SM_BUFSZ * NUM_SM_BUFS)) {
-+      ptr_chk = (u32)iov[i].iov_base == (u32)node->sm_bufs ? 48 :
-+	((u32)iov[i].iov_base - ((u32)node->sm_bufs + 4)) % SM_BUFSZ;
-+      if (ptr_chk) {
-+	iov[0].iov_base += 4;
-+#ifdef NICSTAR_PARANOID
-+	if (ptr_chk != 48) {
-+	  printk ("nicstar%d: Misaligned buffer pointer (offset %d)!\n",
-+		  node->index,ptr_chk);
-+	}
++  else { /* handle s-g buffer */
++    iov = (struct iovec *)skb->data;
++    for (i = 0; i < skb->atm.iovcnt; i++) {
++      if (((unsigned char *)iov[i].iov_base >= (unsigned char *)node->sm_bufs) &&
++	  ((unsigned char *)iov[i].iov_base < (unsigned char *)node->sm_bufs + SM_BUFSZ * NUM_SM_BUFS)) {
 +	ptr_chk = (u32)iov[i].iov_base == (u32)node->sm_bufs ? 48 :
 +	  ((u32)iov[i].iov_base - ((u32)node->sm_bufs + 4)) % SM_BUFSZ;
 +	if (ptr_chk) {
-+	  printk("nicstar%d: Misaligned pointer STILL NOT FIXED!!! %x %d\n",
-+		 node->index,(u32)iov[i].iov_base,ptr_chk);
-+	}
++	  iov[0].iov_base += 4;
++#ifdef NICSTAR_PARANOID
++	  if (ptr_chk != 48) {
++	    printk ("nicstar%d: Misaligned buffer pointer (offset %d)!\n",
++		    node->index,ptr_chk);
++	  }
++	  ptr_chk = (u32)iov[i].iov_base == (u32)node->sm_bufs ? 48 :
++	    ((u32)iov[i].iov_base - ((u32)node->sm_bufs + 4)) % SM_BUFSZ;
++	  if (ptr_chk) {
++	    printk("nicstar%d: Misaligned pointer STILL NOT FIXED!!! %x %d\n",
++		   node->index,(u32)iov[i].iov_base,ptr_chk);
++	  }
 +#endif
++	}
++	free_rx_buf(node, 0, (u32)(iov[i].iov_base));
++      } else {
++	/* Large buffer */
++	free_rx_buf(node, 1, (u32)(iov[i].iov_base));
 +      }
-+      free_rx_buf(node, 0, (u32)(iov[i].iov_base));
-+    } else {
-+      /* Large buffer */
-+      free_rx_buf(node, 1, (u32)(iov[i].iov_base));
 +    }
 +  }
 +
@@ -10536,15 +10625,6 @@
 +    }
 +    slots--;
 +  }
-+#if 0 /* dumps TST */
-+  for (i = 0; i < TST_SIZE; i+=4) {
-+    printk("%d %08x %08x %08x %08x\n",i,
-+	   read_sram(node, NICSTAR_TST_REGION + i),
-+	   read_sram(node, NICSTAR_TST_REGION + i + 1),
-+	   read_sram(node, NICSTAR_TST_REGION + i + 2),
-+	   read_sram(node, NICSTAR_TST_REGION + i + 3));
-+  }
-+#endif
 +}
 +
 +/* Closes down a CBR connection. */
@@ -10608,7 +10688,7 @@
 +  kfree(((nicstar_vcimap *)vcc->dev_data)->scq->orig);
 +  kfree(((nicstar_vcimap *)vcc->dev_data)->scq);
 +}
-+#endif
++#endif /* NICSTAR_CBR */
 +
 +/* General function for creating transmission SCQs */
 +static int create_scq (struct nicstar_scq **scq, int size) {
@@ -10628,6 +10708,86 @@
 +	  (SCQ_ENTRIES) * (sizeof(struct nicstar_scq_shadow)));
 +  return 0;
 +}
++
++static u32 linearize_buffer (struct nicstar_dev *node, struct sk_buff *skb, struct atm_vcc *vcc) {
++  struct iovec *iov = (struct iovec *)skb->data;
++  struct sk_buff *skb_new;
++
++  if (skb->atm.iovcnt > 2) {     /* huge buffer */
++    int el, cnt;
++    unsigned char *p;
++
++    /* copy data to new skb (brute force), only for huge SDUs */
++    skb_new = alloc_skb(skb->len,GFP_ATOMIC);
++    if (!skb_new) {
++      printk("nicstar%d: Can't get skbuff for s-g ip receive of huge buffer (%d bytes)\n",node->index,(u32)skb->len);
++      nicstar_free_rx_skb(vcc, skb);
++      return 0;
++    }
++    p = skb_new->data;
++    el = skb->len;
++    skb_put(skb_new,skb->len);
++    skb_new->free = 1;
++    for (cnt = 0; (cnt < skb->atm.iovcnt) && el; cnt++) {
++      memcpy(p, iov->iov_base,
++	     (iov->iov_len > el) ? el : iov->iov_len);
++      p += iov->iov_len;
++      el -= (iov->iov_len > el)?el:iov->iov_len;
++      iov++;
++    }
++    nicstar_free_rx_skb(vcc, skb);
++    return (u32) skb_new;
++  }
++  else if (skb->atm.iovcnt == 2) {
++    /* simply copy small buffer to free space at start of large */
++    int i;
++
++    /* which large buffer are we? */
++    iov++;
++    for (i = 0; i < NUM_LG_BUFS ; i++) {
++      if ((node->rx_lg_skb[i])->data == iov->iov_base) {
++	break;
++      }
++    }
++    if (i >= NUM_LG_BUFS) {
++      /* should probably copy instead */
++      printk("nicstar%d: Corresponding large buffer not found! dropping packet. i %d vcc %x skb %x data %x\n",
++	     node->index, i,vcc,skb,skb->data);
++      nicstar_free_rx_skb(vcc,skb);
++      return 0;
++    }
++    skb_put(node->rx_lg_skb[i],iov->iov_len);
++    iov--;
++    skb_push(node->rx_lg_skb[i],SM_BUF_DATALEN);
++    memcpy((node->rx_lg_skb[i])->data,iov->iov_base,SM_BUF_DATALEN);
++    skb->atm.iovcnt--;
++#ifdef NICSTAR_LBUFCNT
++    PRINTK("2-buffer receive (IP?) len %d old %x new %x node %x cnt %d vcc %x\n",skb->len,skb,node->rx_lg_skb[i],node,node->lbuf_cnt,vcc);
++#endif
++    nicstar_free_rx_skb(vcc,skb);
++    skb_new = skb_clone(node->rx_lg_skb[i], GFP_ATOMIC);
++#ifdef NICSTAR_RC_FLAG
++    skb_new->atm.recycle_buffer = (void *) vcc;
++#endif
++    skb_new->destructor = nicstar_rx_skb_destructor;
++    return (u32) skb_new;
++  }
++  else {
++    caddr_t p = iov->iov_base; /* make a copy of pointer to data */
++
++    /* put small buffer contents in skb->data */
++    memcpy(skb->data,p,skb->len);
++    free_rx_buf(node,0,(u32)p);
++    skb->atm.iovcnt = 0;
++    skb->tail = skb->data + skb->len;
++    skb_new = skb_clone(skb,GFP_ATOMIC);
++    skb_new->destructor = nicstar_rx_skb_destructor;
++#ifdef NICSTAR_RC_FLAG
++    skb_new->atm.recycle_buffer = (void *) vcc;
++#endif
++    return (u32) skb_new;
++  }
++}
 --- ref/include/linux/pci.h	Wed Nov  6 11:12:35 1996
 +++ work/include/linux/pci.h	Fri Nov 15 19:58:19 1996
 @@ -241,6 +241,9 @@
@@ -10860,7 +11020,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/include/linux/atmclip.h	Fri Nov 15 21:36:38 1996
++++ work/include/linux/atmclip.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,37 @@
 +/* atmclip.h - Classical IP over ATM */
 + 
@@ -10900,7 +11060,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/include/linux/atmdev.h	Fri Nov 15 20:55:15 1996
++++ work/include/linux/atmdev.h	Wed Jan 29 19:27:24 1997
 @@ -0,0 +1,257 @@
 +/* atmdev.h - ATM device driver declarations */
 + 
@@ -11287,8 +11447,8 @@
 +
 +#endif
 --- ref/include/linux/skbuff.h	Thu Oct 31 11:06:31 1996
-+++ work/include/linux/skbuff.h	Fri Nov 15 20:55:15 1996
-@@ -112,6 +112,21 @@
++++ work/include/linux/skbuff.h	Wed Jan 29 18:47:57 1997
+@@ -112,6 +112,25 @@
  	unsigned char 	*end;			/* End pointer					*/
  	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
  	__u16		redirport;		/* Redirect port				*/
@@ -11299,6 +11459,10 @@
 +		struct atm_vcc	*vcc;		/* ATM VCC */
 +		int		iovcnt;		/* 0 for "normal" operation */
 +		struct timeval	timestamp;	/* timestamp or 0,x */
++#ifdef CONFIG_ATM_NICSTAR
++	        void            *recycle_buffer; /* set when buffer should be */
++                                                 /*  recycled; points to vcc */
++#endif
 +		int 		encap;		/* non-zero if encapsulated */
 +						/* for ATMARP */
 +#ifdef CONFIG_AREQUIPA
@@ -11654,8 +11818,8 @@
 +	return CLIP(dev)->number;
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/common.c	Fri Nov 15 20:12:51 1996
-@@ -0,0 +1,953 @@
++++ work/net/atm/common.c	Wed Jan 29 18:47:27 1997
+@@ -0,0 +1,954 @@
 +/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */
 +
 +/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
@@ -12081,7 +12245,8 @@
 +	        }          
 +#endif      
 +		memcpy_tofs(buff,skb->data,eff_len);
-+		kfree_skb(skb,FREE_READ);
++		if (!vcc->dev->ops->free_rx_skb) kfree_skb(skb,FREE_READ);
++		else vcc->dev->ops->free_rx_skb(vcc, skb);
 +	}
 +	return eff_len;
 +}
@@ -12981,7 +13146,7 @@
 +		if (atm_vcc[i].family) fn(atm_vcc+i);
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/static.h	Fri Nov 15 21:36:38 1996
++++ work/net/atm/static.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,29 @@
 +/* net/atm/static.h - Staticly allocated resources */
 +
@@ -13838,7 +14003,7 @@
 +	return 0;
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/signaling.h	Fri Nov 15 21:36:38 1996
++++ work/net/atm/signaling.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,25 @@
 +/* net/atm/signaling.h - ATM signaling */
 + 
@@ -13866,8 +14031,8 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/atmarp.c	Fri Dec 20 20:43:26 1996
-@@ -0,0 +1,608 @@
++++ work/net/atm/atmarp.c	Wed Jan 22 16:25:09 1997
+@@ -0,0 +1,609 @@
 +/* atmarp.c - RFC1577 ATM ARP */
 +
 +/* Written 1995,1996 by Werner Almesberger, EPFL LRC */
@@ -14011,7 +14176,7 @@
 +}
 +
 +
-+static inline void time_out_entry(struct atmarp_entry **entry)
++static inline int time_out_entry(struct atmarp_entry **entry)
 +{
 +	struct atmarp_entry *next;
 +
@@ -14019,18 +14184,19 @@
 +	if ((*entry)->vcc) {
 +		(*entry)->vcc->flags |= ATM_VF_RELEASED;
 +		(*entry)->vcc->flags &= ~ATM_VF_READY;
++		wake_up(&(*entry)->vcc->sleep);
++		return 0;
 +	}
-+	else {
-+		if ((*entry)->queued) {
-+			dev_kfree_skb((*entry)->queued,FREE_WRITE);
-+			DPRINTK("discarding queued skb\n");
-+		}
-+		else printk(KERN_CRIT "atmarp: weird - incomplete entry, but "
-+			    "nothing queued\n");
-+		next = (*entry)->next;
-+		kfree(*entry);
-+		*entry = next;
-+	}
++	if ((*entry)->queued) {
++		dev_kfree_skb((*entry)->queued,FREE_WRITE);
++		DPRINTK("discarding queued skb\n");
++	}
++	else printk(KERN_CRIT "atmarp: weird - incomplete entry, but "
++		    "nothing queued\n");
++	next = (*entry)->next;
++	kfree(*entry);
++	*entry = next;
++	return 1;
 +}
 +
 +
@@ -14048,8 +14214,7 @@
 +				expire = (*entry)->last_use+(*entry)->
 +				    idle_timeout;
 +				if (expire < jiffies) {
-+					time_out_entry(entry);
-+					continue;
++					if (time_out_entry(entry)) continue;
 +				}
 +				else if (expire < idle_timer.expires)
 +						idle_timer.expires = expire;
@@ -14345,6 +14510,7 @@
 +	dev->tbusy = 0; /* @@@ check */
 +	dev->hard_header = clip_hard_header;
 +	dev->do_ioctl = NULL;
++	dev->change_mtu = NULL;
 +	dev->ip_arp = atmarp_ioctl;
 +	dev->rebuild_header = clip_rebuild_header;
 +	dev->get_stats = atm_clip_get_stats;
@@ -14477,7 +14643,7 @@
 +	return 0;
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/atmarp.h	Fri Nov 15 21:36:38 1996
++++ work/net/atm/atmarp.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,53 @@
 +/* net/atm/atmarp.h - RFC1577 ATM ARP */
 + 
@@ -14533,7 +14699,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/ipcommon.h	Fri Nov 15 21:37:21 1996
++++ work/net/atm/ipcommon.h	Wed Jan 29 19:36:15 1997
 @@ -0,0 +1,73 @@
 +/* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */
 +
@@ -14609,8 +14775,8 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/ipcommon.c	Fri Nov 15 19:06:36 1996
-@@ -0,0 +1,214 @@
++++ work/net/atm/ipcommon.c	Wed Jan 22 16:28:02 1997
+@@ -0,0 +1,215 @@
 +/* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */
 +
 +/* Written 1996 by Werner Almesberger, EPFL LRC */
@@ -14782,6 +14948,7 @@
 +	dev->tbusy = 0; /* @@@ check */
 +	dev->hard_header = clip_hard_header;
 +	dev->do_ioctl = clip_ioctl;
++	dev->change_mtu = NULL;
 +	dev->rebuild_header = clip_rebuild_header;
 +	dev->get_stats = atm_clip_get_stats;
 +	dev->hard_header_len = RFC1483LLC_LEN;
@@ -14826,11 +14993,11 @@
 +	return number;
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/arequipa.c	Fri Nov 15 19:06:36 1996
-@@ -0,0 +1,438 @@
++++ work/net/atm/arequipa.c	Wed Jan 29 19:33:39 1997
+@@ -0,0 +1,442 @@
 +/* net/atm/arequipa.c - Application requested IP over ATM */
 + 
-+/* Written 1996 by Jean-Michel Pittet and Werner Almesberger, EPFL LRC */
++/* Written 1996,1997 by Jean-Michel Pittet and Werner Almesberger, EPFL LRC */
 +
 +
 +#include <linux/config.h>
@@ -14962,7 +15129,7 @@
 +void arequipa_close_vcc(struct atm_vcc *vcc)
 +{
 +	if (!(vcc->flags & ATM_VF_AQREL)) {
-+		printk(KERN_CRIT "arequipa_close_3rd_party: VCC %p doesn't "
++		printk(KERN_CRIT "arequipa_close_vcc: VCC %p doesn't "
 +		    "have ATM_VF_AQREL set\n",vcc);
 +		return;
 +	}
@@ -15263,6 +15430,10 @@
 +	vcc->peek = NULL; /* crash */
 +	vcc->pop = NULL; /* crash */
 +	vcc->push_oam = NULL; /* crash */
++	/*
++	 * Now that we have an arequipad, we should check for stale Arequipa
++	 * connections and prune them. @@@
++	 */
 +        return 0;
 +
 +}
@@ -15847,7 +16018,7 @@
 +
 +#endif
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/include/linux/mmuio.h	Fri Nov 15 21:36:38 1996
++++ work/include/linux/mmuio.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,25 @@
 +/* mmuio.h - MMU-supported high-speed I/O */
 +
@@ -16037,7 +16208,7 @@
 +
 +#endif
 --- ref/include/linux/netdevice.h	Thu Oct 31 11:06:31 1996
-+++ work/include/linux/netdevice.h	Fri Nov 15 20:55:15 1996
++++ work/include/linux/netdevice.h	Wed Jan 29 19:27:24 1997
 @@ -187,6 +187,8 @@
    int			  (*set_mac_address)(struct device *dev, void *addr);
  #define HAVE_PRIVATE_IOCTL
@@ -17872,7 +18043,7 @@
 +        return i;
 +}
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/lec.h	Wed Nov 27 15:15:46 1996
++++ work/net/atm/lec.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,107 @@
 +/*
 + *
@@ -19029,7 +19200,7 @@
 +}
 +
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/net/atm/lec_arpc.h	Wed Nov 27 15:15:46 1996
++++ work/net/atm/lec_arpc.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,101 @@
 +/*
 + * Lec arp cache
@@ -19133,7 +19304,7 @@
 +
 +#endif
 --- ref/include/net/route.h	Wed Oct 30 03:58:23 1996
-+++ work/include/net/route.h	Fri Nov 15 21:32:57 1996
++++ work/include/net/route.h	Wed Jan 29 19:33:42 1997
 @@ -26,6 +26,7 @@
  #define _ROUTE_H
  
@@ -19197,7 +19368,7 @@
  
  #endif	/* _ROUTE_H */
 --- ref/include/net/sock.h	Thu Nov  7 10:35:28 1996
-+++ work/include/net/sock.h	Fri Nov 15 20:55:15 1996
++++ work/include/net/sock.h	Wed Jan 29 19:27:24 1997
 @@ -190,6 +190,9 @@
  				zapped,	/* In ax25 & ipx means not linked */
  				broadcast,
@@ -19250,7 +19421,7 @@
  /*
   *	This part is used for the timeout functions (timer.c). 
 --- /dev/null	Mon Jul 18 01:46:18 1994
-+++ work/include/linux/arequipa.h	Fri Nov 15 21:36:38 1996
++++ work/include/linux/arequipa.h	Wed Jan 29 19:35:20 1997
 @@ -0,0 +1,46 @@
 +/* arequipa.h - Arequipa interface definitions */
 + 
@@ -19459,19 +19630,48 @@
  bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
 +bool 'IP: Window scale option' CONFIG_SCALED_WINDOWS
 --- ref/net/core/skbuff.c	Thu Jun  6 08:45:39 1996
-+++ work/net/core/skbuff.c	Fri Nov 15 21:59:44 1996
-@@ -702,6 +702,9 @@
++++ work/net/core/skbuff.c	Wed Jan 29 18:47:11 1997
+@@ -702,6 +702,12 @@
  	skb->end=bptr+len;
  	skb->len=0;
  	skb->destructor=NULL;
 +#ifdef CONFIG_ATM
 +	skb->atm.iovcnt = 0;
++#ifdef CONFIG_ATM_NICSTAR
++	skb->atm.recycle_buffer = NULL;
++#endif
 +#endif
  	return skb;
  }
  
+@@ -762,6 +768,12 @@
+ 	n->tries = 0;
+ 	n->lock = 0;
+ 	n->users = 0;
++#ifdef CONFIG_ATM_NICSTAR
++        if (n->atm.recycle_buffer) {
++	  n->atm.recycle_buffer = NULL;
++	  n->destructor = NULL;
++	}
++#endif
+ 	return n;
+ }
+ 
+@@ -820,6 +832,12 @@
+ 	n->users=0;
+ 	n->pkt_type=skb->pkt_type;
+ 	n->stamp=skb->stamp;
++#ifdef CONFIG_ATM_NICSTAR
++        if (n->atm.recycle_buffer) {
++	  n->atm.recycle_buffer = NULL;
++	  n->destructor = NULL;
++	}
++#endif
+ 	
+ 	IS_SKB(n);
+ 	return n;
 --- ref/include/net/tcp.h	Wed Oct 30 03:58:24 1996
-+++ work/include/net/tcp.h	Fri Nov 15 21:35:29 1996
++++ work/include/net/tcp.h	Wed Jan 29 19:34:00 1997
 @@ -23,16 +23,26 @@
  
  /*
diff -ur --new-file old/atm/config/Makefile new/atm/config/Makefile
--- old/atm/config/Makefile	Thu Jan  1 01:00:00 1970
+++ new/atm/config/Makefile	Fri Jan 17 15:40:03 1997
@@ -0,0 +1,12 @@
+noall:
+	@echo "Please specify the type of distribution you're using as the"; \
+	echo "make target, e.g. make RedHat-4.0  The following Linux"; \
+	echo "distribution are currently supported:"; \
+	echo; \
+	echo "  RedHat-4.0"; \
+	echo; \
+	echo "(Note that names are case-sensitive)"
+
+RedHat-4.0:
+	cd common && make install
+	cd redhat-4.0 && make install
diff -ur --new-file old/atm/config/README new/atm/config/README
--- old/atm/config/README	Thu Jan  1 01:00:00 1970
+++ new/atm/config/README	Fri Jan 17 15:42:05 1997
@@ -0,0 +1,8 @@
+The subdirectories of this directory contain sample configuration files
+for various Linux distribution. Please see the respective READMEs for
+details. The configuration files for a given distribution can be installed
+by issuing the command
+
+  make distribution_name
+
+from this directory.
diff -ur --new-file old/atm/config/common/Makefile new/atm/config/common/Makefile
--- old/atm/config/common/Makefile	Thu Jan  1 01:00:00 1970
+++ new/atm/config/common/Makefile	Fri Jan 17 15:30:11 1997
@@ -0,0 +1,3 @@
+install:	hosts.atm e164_cc
+		[ -r /etc/hosts.atm ] || install -c -m 0644 hosts.atm /etc
+		[ -r /etc/e164_cc ] || install -c -m 0644 e164_cc /etc
diff -ur --new-file old/atm/config/common/README new/atm/config/common/README
--- old/atm/config/common/README	Thu Jan  1 01:00:00 1970
+++ new/atm/config/common/README	Fri Jan 17 15:35:03 1997
@@ -0,0 +1,8 @@
+Distribution-independent sample configuration files
+===================================================
+
+/etc/hosts.atm
+    Local list of ATM hosts. (Like /etc/hosts)
+
+/etc/e164_cc
+    List of E.164 (telephony) country codes.
diff -ur --new-file old/atm/config/common/e164_cc new/atm/config/common/e164_cc
--- old/atm/config/common/e164_cc	Thu Jan  1 01:00:00 1970
+++ new/atm/config/common/e164_cc	Fri Jan 17 15:28:45 1997
@@ -0,0 +1,320 @@
+93  Afghanistan (Islamic State of)
+355 Albania (Republic of)
+21  Algeria (People's Democratic Republic of)
+684 American Samoa
+376 Andorra (Principality of)
+244 Angola (Republic of)
+1   Anguilla
+1   Antigua and Barbuda
+54  Argentine Republic
+374 Armenia (Republic of)
+297 Aruba
+247 Ascension
+61  Australia
+672 Australian External Territories
+43  Austria
+994 Azerbaijani Republic
+1   Bahamas (Commonwealth of the)
+973 Bahrain (State of)
+880 Bangladesh (People's Republic of)
+1   Barbados
+375 Belarus (Republic of)
+32  Belgium
+501 Belize
+229 Benin (Republic of)
+1   Bermuda
+975 Bhutan (Kingdom of)
+591 Bolivia (Republic of)
+387 Bosnia and Herzegovina (Republic of)
+267 Botswana (Republic of)
+55  Brazil (Federative Republic of)
+1   British Virgin Islands
+673 Brunei Darussalam
+359 Bulgaria (Republic of)
+226 Burkina Faso
+257 Burundi (Republic of)
+855 Cambodia (Kingdom of)
+237 Cameroon (Republic of)
+1   Canada
+238 Cape Verde (Republic of)
+1   Cayman Islands
+236 Central African Republic
+235 Chad (Republic of)
+56  Chile
+86  China (People's Republic of)
+57  Colombia (Republic of)
+269 Comoros (Islamic Federal Republic of the)
+242 Congo (Republic of the)
+682 Cook Islands
+506 Costa Rica
+225 C\'f4te d'Ivoire (Republic of)
+385 Croatia (Republic of)
+53  Cuba
+357 Cyprus (Republic of)
+42  Czech Republic
+850 Democratic People's Republic of Korea
+45  Denmark
+246 Diego Garcia
+253 Djibouti (Republic of)
+1   Dominica (Commonwealth of)
+1   Dominican Republic
+593 Ecuador
+20  Egypt (Arab Republic of)
+503 El Salvador (Republic of)
+240 Equatorial Guinea (Republic of)
+291 Eritrea
+372 Estonia (Republic of)
+251 Ethiopia (Federal Democratic Republic of)
+500 Falkland Islands (Malvinas)
+298 Faroe Islands (Denmark)
+679 Fiji (Republic of)
+358 Finland
+33  France
+689 French Polynesia (Territoire fran\'e7ais d'outre-mer)
+241 Gabonese Republic
+220 Gambia (Republic of the)
+995 Georgia
+49  Germany (Federal Republic of)
+233 Ghana
+350 Gibraltar
+881 Global Mobile Satellite System (GMSS), shared code
+30  Greece
+299 Greenland (Denmark)
+1   Grenada
+590 Guadeloupe (French Department of)
+671 Guam
+502 Guatemala (Republic of)
+594 Guiana (French Department of)
+224 Guinea (Republic of)
+245 Guinea-Bissau (Republic of)
+592 Guyana
+509 Haiti (Republic of)
+504 Honduras (Republic of)
+852 Hongkong
+36  Hungary (Republic of)
+354 Iceland
+91  India (Republic of)
+62  Indonesia (Republic of)
+871 Inmarsat (Atlantic Ocean-East)
+874 Inmarsat (Atlantic Ocean-West)
+873 Inmarsat (Indian Ocean)
+872 Inmarsat (Pacific Ocean)
+870 Inmarsat SNAC
+800 International Freephone Service
+98  Iran (Islamic Republic of)
+964 Iraq (Republic of)
+353 Ireland
+972 Israel (State of)
+39  Italy
+1   Jamaica
+81  Japan
+962 Jordan (Hashemite Kingdom of)
+7   Kazakstan (Republic of)
+254 Kenya (Republic of)
+686 Kiribati (Republic of)
+82  Korea (Republic of)
+965 Kuwait (State of)
+7   Kyrgyz Republic
+996 Kyrgyz Republic
+856 Lao People's Democratic Republic
+371 Latvia (Republic of)
+961 Lebanon
+266 Lesotho (Kingdom of)
+231 Liberia (Republic of)
+21  Libya (Socialist People's Libyan Arab Jamahiriya)
+41  Liechtenstein (Principality of)
+370 Lithuania (Republic of)
+352 Luxembourg
+853 Macau
+261 Madagascar (Republic of)
+265 Malawi
+60  Malaysia
+960 Maldives (Republic of)
+223 Mali (Republic of)
+356 Malta
+692 Marshall Islands (Republic of the)
+596 Martinique (French Department of)
+222 Mauritania (Islamic Republic of)
+230 Mauritius (Republic of)
+269 Mayotte (Collectivit\'e9 territoriale de la R\'e9publique fran\'e7aise)
+52  Mexico
+691 Micronesia (Federated States of)
+373 Moldova (Republic of)
+377 Monaco (Principality of)
+976 Mongolia
+1   Montserrat
+21  Morocco (Kingdom of)
+258 Mozambique (Republic of)
+95  Myanmar (Union of)
+264 Namibia (Republic of)
+674 Nauru (Republic of)
+977 Nepal
+31  Netherlands (Kingdom of the)
+599 Netherlands Antilles
+687 New Caledonia (Territoire fran\'e7ais d'outre-mer)
+64  New Zealand
+505 Nicaragua
+227 Niger (Republic of the)
+234 Nigeria (Federal Republic of)
+683 Niue
+670 Northern Mariana Islands (Commonwealth of the)
+47  Norway
+968 Oman (Sultanate of)
+92  Pakistan (Islamic Republic of)
+680 Palau (Republic of)
+507 Panama (Republic of)
+675 Papua New Guinea
+595 Paraguay (Republic of)
+51  Peru
+63  Philippines (Republic of the)
+48  Poland (Republic of)
+351 Portugal
+1   Puerto Rico
+974 Qatar (State of)
+262 Reunion (French Department of)
+40  Romania
+7   Russian Federation
+250 Rwandese Republic
+290 Saint Helena
+1   Saint Kitts and Nevis
+1   Saint Lucia
+508 Saint Pierre and Miquelon (Collectivit\'e9 territoriale de la 
+1   Saint Vincent and the Grenadines
+378 San Marino (Republic of)
+239 Sao Tome and Principe (Democratic Republic of)
+966 Saudi Arabia (Kingdom of)
+221 Senegal (Republic of)
+248 Seychelles (Republic of)
+232 Sierra Leone
+65  Singapore (Republic of)
+42  Slovak Republic
+386 Slovenia (Republic of)
+677 Solomon Islands
+252 Somali Democratic Republic
+27  South Africa (Republic of)
+34  Spain
+94  Sri Lanka (Democratic Socialist Republic of)
+249 Sudan (Republic of the)
+597 Suriname (Republic of)
+268 Swaziland (Kingdom of)
+46  Sweden
+41  Switzerland (Confederation of)
+963 Syrian Arab Republic
+7   Tajikistan (Republic of)
+255 Tanzania (United Republic of)
+66  Thailand
+389 The Former Yugoslav Republic of Macedonia
+228 Togolese Republic
+690 Tokelau
+676 Tonga (Kingdom of)
+1   Trinidad and Tobago
+21  Tunisia
+90  Turkey
+7   Turkmenistan
+993 Turkmenistan
+1   Turks and Caicos Islands
+688 Tuvalu
+256 Uganda (Republic of)
+380 Ukraine
+971 United Arab Emirates
+44  United Kingdom of Great Britain and Northern Ireland
+1   United States of America
+1   United States Virgin Islands
+598 Uruguay (Eastern Republic of)
+7   Uzbekistan (Republic of)
+998 Uzbekistan (Republic of)
+678 Vanuatu (Republic of)
+379 Vatican City State
+58  Venezuela (Republic of)
+84  Viet Nam (Socialist Republic of)
+681 Wallis and Futuna (Territoire fran\'e7ais d'outre-mer)
+685 Western Samoa (Independent State of)
+967 Yemen (Republic of)
+381 Yugoslavia (Federal Republic of)
+243 Zaire (Republic of)
+260 Zambia (Republic of)
+259 Zanzibar (Tanzania)
+263 Zimbabwe (Republic of)
+0   Reserved
+886 Reserved
+875 Reserved - Maritime Mobile Service Applications
+876 Reserved - Maritime Mobile Service Applications
+877 Reserved - Maritime Mobile Service Applications
+969 Reserved - reservation currently under investigation
+878 Reserved - Universal Personal Telecommunication Service (UPT)
+879 Reserved for national purposes
+388 Temporarily unassignable
+888 Temporarily unassignable
+280 Spare code
+281 Spare code
+282 Spare code
+283 Spare code
+284 Spare code
+285 Spare code
+286 Spare code
+287 Spare code
+288 Spare code
+289 Spare code
+292 Spare code
+293 Spare code
+294 Spare code
+295 Spare code
+296 Spare code
+382 Spare code
+383 Spare code
+384 Spare code
+693 Spare code
+694 Spare code
+695 Spare code
+696 Spare code
+697 Spare code
+698 Spare code
+699 Spare code
+801 Spare code
+802 Spare code
+803 Spare code
+804 Spare code
+805 Spare code
+806 Spare code
+807 Spare code
+808 Spare code
+809 Spare code
+830 Spare code
+831 Spare code
+832 Spare code
+833 Spare code
+834 Spare code
+835 Spare code
+836 Spare code
+837 Spare code
+838 Spare code
+839 Spare code
+851 Spare code
+854 Spare code
+857 Spare code
+858 Spare code
+859 Spare code
+882 Spare code
+883 Spare code
+884 Spare code
+885 Spare code
+887 Spare code
+889 Spare code
+890 Spare code
+891 Spare code
+892 Spare code
+893 Spare code
+894 Spare code
+895 Spare code
+896 Spare code
+897 Spare code
+898 Spare code
+899 Spare code
+970 Spare code
+978 Spare code
+979 Spare code
+990 Spare code
+991 Spare code
+992 Spare code
+997 Spare code
+999 Spare code
diff -ur --new-file old/atm/config/common/hosts.atm new/atm/config/common/hosts.atm
--- old/atm/config/common/hosts.atm	Thu Jan  1 01:00:00 1970
+++ new/atm/config/common/hosts.atm	Tue Jan 14 10:43:10 1997
@@ -0,0 +1,5 @@
+#
+# hosts.atm - Example
+#
+#
+## 47.0005.80FFE1000000F21A26D8.0020ea001146.00 lrcpcs-a.epfl.ch lrcpcs-a
diff -ur --new-file old/atm/config/redhat-4.0/Makefile new/atm/config/redhat-4.0/Makefile
--- old/atm/config/redhat-4.0/Makefile	Thu Jan  1 01:00:00 1970
+++ new/atm/config/redhat-4.0/Makefile	Fri Jan 17 15:30:33 1997
@@ -0,0 +1,6 @@
+install:	atm atm.init atmsigd.conf ifcfg-atm0 ifup-atm
+		install -c -m 0644 atm /etc/sysconfig
+		install -c -m 0644 atm.init /etc/rc.d/init.d/atm
+		install -c -m 0644 atmsigd.conf /etc
+		install -c -m 0755 ifcfg-atm0 ifup-atm \
+		  /etc/sysconfig/network-scripts
diff -ur --new-file old/atm/config/redhat-4.0/README new/atm/config/redhat-4.0/README
--- old/atm/config/redhat-4.0/README	Wed Nov 27 16:13:32 1996
+++ new/atm/config/redhat-4.0/README	Fri Jan 17 15:33:36 1997
@@ -33,6 +33,9 @@
     configures it, adds the route, and creates all the necessary ATMARP
     information.
 
+In addition to the files listed above, files from atm/config/common need to
+be installed. See atm/config/common/README for details.
+
 
 Installation
 ------------
@@ -46,13 +49,12 @@
 
 Then copy the configuration files:
 
-# cd config/redhat-4.0
-# cp atm /etc/sysconfig
-# cp atm.init /etc/rc.d/init.d/atm
-# cp atmsigd.conf /etc
-# cp ifcfg-atm0 ifup-atm /etc/sysconfig/network-scripts
+# cd config
+# make redhat-4.0
+
+Finally, you may have to edit /etc/sysconfig/atm, /etc/atmsigd.conf,
+and /etc/hosts.atm
 
-Finally, you may have to edit /etc/sysconfig/atm and /etc/atmsigd.conf
 If you're using IP over ATM, you _must_ change
 /etc/sysconfig/network-scripts/ifcfg-atm0 as follows:
 
diff -ur --new-file old/atm/config/redhat-4.0/atm.init new/atm/config/redhat-4.0/atm.init
--- old/atm/config/redhat-4.0/atm.init	Wed Nov 27 15:54:03 1996
+++ new/atm/config/redhat-4.0/atm.init	Mon Jan 13 21:39:02 1997
@@ -21,7 +21,7 @@
 	echo -n "Starting ATM demons: "
 	daemon atmsigd -b -l syslog -D /var/tmp -t 20
 	daemon ilmid -b -l syslog `[ -z "$ILMIQOS" ] || echo -q $ILMIQOS`
-	[ "$IPATM" = yes ] && daemon atmarpd -b -l syslog
+	[ "$IPATM" = yes ] && daemon atmarpd -b -l syslog -m
 	# launch LANE demon(s) ...
 	[ "$AREQUIPA" = yes ] && daemon arequipad -b -l syslog
 	echo
diff -ur --new-file old/atm/doc/usage.tex new/atm/doc/usage.tex
--- old/atm/doc/usage.tex	Fri Dec 20 21:11:32 1996
+++ new/atm/doc/usage.tex	Wed Jan 29 22:11:08 1997
@@ -1,7 +1,7 @@
 %%def%:=
 
 %:\begin{verbatim}
-%:Usage instructions  -  ATM on Linux, release 0.25 (pre-alpha)
+%:Usage instructions  -  ATM on Linux, release 0.26 (pre-alpha)
 %:-------------------------------------------------------------
 %:
 %:\end{verbatim}
@@ -38,7 +38,7 @@
 
 \title{ATM on Linux \\
   User's guide \\
-  Release 0.25 (pre-alpha)}
+  Release 0.26 (pre-alpha)}
 \author{Werner Almesberger \\
   {\tt werner.almesberger@lrc.di.epfl.ch} \\
   \\
@@ -81,7 +81,7 @@
 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.25.tar.gz}
+    \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.26.tar.gz}
   \item the Linux kernel, version 2.0.25, e.g. from
 \url{ftp://ftp.cs.helsinki.fi/pub/Software/Linux/Kernel/v2.0/linux-2.0.25.tar.gz}
   \item Perl, version 4 or 5
@@ -98,7 +98,7 @@
 distribution:
 
 \begin{verbatim}
-tar xfz atm-0.25.tar.gz
+tar xfz atm-0.26.tar.gz
 \end{verbatim}
 
 and the kernel source:
@@ -176,10 +176,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}.
 
-The IDT 77201 driver currently only supports native ATM, i.e. IP over ATM
-or LANE are not possible (and should not be tried). A fix for this is being
-worked on. Note that the file \path{drivers/atm/nicstar.h} contains a few
-configurable settings.
+Note that the file \path{drivers/atm/nicstar.h} contains a few configurable
+settings for the IDT 77201 driver.
 
 Support for a simple type of serial consoles is automatically added by the
 ATM patch. If you're using \name{LILO}, you can enable it by putting a line
@@ -567,6 +565,7 @@
 \section{Signaling}
 
 \subsection{ATM hosts file}
+\label{hosts}
 
 Because ATM addresses are inconvenient to use, most ATM tools also
 accept names instead of numeric addresses. The mapping between names and
@@ -595,8 +594,23 @@
 same name, the first suitable one will be chosen, i.e. if an application
 explicitly requests only SVC addresses, any PVC addresses will be ignored.
 
-Support for ANS, a DNS-based resolution mechanism, which is currently
-in the process of being standardized by ATM Forum, is for further study.
+
+\subsection{ANS}
+
+If you have access to the ATM Name Service (ANS, e.g because you've installed
+the ANS extension), you can use it instead of or in addition to the hosts
+file by specifying the host that runs ANS in the \path{/etc/resolv.conf}
+file.
+
+For performing reverse lookups of E.164 addresses, the list of telephony
+country codes needs to be known. That list can be obtained from
+\url{http://www.itu.ch/itudoc/itu-t/lists/} the file has a name of the
+form \path{tf\_cc\_e\_*.rtf} The script \path{atm/lib/rtf2e164\_cc.pl} can
+be used to create the E.164 county codes table, e.g.
+
+\begin{verbatim}
+perl rtf2e164_cc.pl <tf_cc_e_13130.rtf >/etc/e164_cc
+\end{verbatim}
 
 
 \subsection{Signaling demon}
@@ -694,6 +708,78 @@
 selector byte (SEL) is ignored.
 
 
+\subsection{Running two ATM NICs back-to-back}
+
+It is also possible to run with two ATM NICs connected back-to-back,
+and no switch in between\footnote{This section was written by
+Richard Jones \raw{rjones@imcl.com}. Comments should be directed to me as well
+as to Werner.}. This is great for simple test environments.
+
+First, if you're using UTP or STP-5, you need a suitable cable.  Our
+experience with standard 100Base-T back-to-back cables was not
+good. It appears that the pin-out they use is different. After some
+false starts, we found that the following cable works:
+
+\begin{verbatim}
+RJ45                            RJ45
+   1        ------------        7
+   2        ------------        8
+
+   7        ------------        1
+   8        ------------        2
+
+Pins 3, 4, 5, 6 unconnected.
+\end{verbatim}
+
+You can also make up a loopback cable with 1 -- 7 and 2 -- 8 connected for
+ultra-cheap setups.
+
+Here we have two machines called ``virgil'' and ``nestor''.
+Substitute your own names as necessary.
+
+One side of the ATM connection needs to use the network version of
+\name{atmsigd} and the other side should use the normal user version.
+So here on nestor we start \name{atmsigd} with:
+
+\begin{verbatim}
+atmsigd -b -N
+\end{verbatim}
+
+and on virgil with:
+
+\begin{verbatim}
+atmsigd -b
+\end{verbatim}
+
+Without a switch, you won't be able to use ILMI. Instead, create a
+\path{/etc/hosts.atm} file containing two dummy addresses.
+Our ATM hosts file contains:
+
+\begin{verbatim}
+47.0005.80FFE1000000F21A26D8.0020EA000EE0.00    nestor-atm
+47.0005.80FFE1000000F21A26D8.0020D4102A80.00    virgil-atm
+\end{verbatim}
+
+These are completely spurious addresses, of course, but as long as you're
+not connected to a public or private ATM network, I don't think it matters.
+To set the address correctly in the driver, we use:
+
+\begin{verbatim}
+atmaddr -a virgil-atm
+\end{verbatim}
+
+on virgil, and:
+
+\begin{verbatim}
+atmaddr -a nestor-atm
+\end{verbatim}
+
+on nestor. Now start \name{atmarpd} on both machines
+in the normal way. Now you (should) have a working ATM set-up. To get
+IP over ATM and Arequipa working, just follow the instructions in
+section \ref{ipoveratm}.
+
+
 \subsection{Q.2931 message dumper}
 
 The Q.2931 message compiler can also generate a pretty-printer for Q.2931
@@ -732,6 +818,7 @@
 %------------------------------------------------------------------------------
 
 \section{IP over ATM}
+\label{ipoveratm}
 
 IP over ATM is supported using two modules:\footnote{The term ``module''
 is used here for a functional software component, not for a loadable kernel
diff -ur --new-file old/atm/doc/usage.txt new/atm/doc/usage.txt
--- old/atm/doc/usage.txt	Fri Dec 20 21:25:32 1996
+++ new/atm/doc/usage.txt	Wed Jan 29 22:19:00 1997
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.25 (pre-alpha)
+Usage instructions  -  ATM on Linux, release 0.26 (pre-alpha)
 -------------------------------------------------------------
 
 For updates of ATM on Linux, please check the Web page at  
@@ -17,7 +17,7 @@
 In order to install this package, you need 
 
   - the package itself  
-    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.25.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.26.tar.gz  
   - the Linux kernel, version 2.0.25, e.g. from  
     ftp://ftp.cs.helsinki.fi/pub/Software/Linux/Kernel/v2.0/linux-2.0.25.tar.gz  
   - Perl, version 4 or 5 
@@ -33,7 +33,7 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.25.tar.gz
+tar xfz atm-0.26.tar.gz
 
 and the kernel source:
 
@@ -100,10 +100,8 @@
 The TNETA1570 driver is for a board developed by Rolf Fiedler at TU 
 Chemnitz, see also  ftp://ftp.infotech.tu-chemnitz.de/pub/linux-atm .
 
-The IDT 77201 driver currently only supports native ATM, i.e. IP over ATM 
-or LANE are not possible (and should not be tried). A fix for this is being 
-worked on. Note that the file drivers/atm/nicstar.h contains a few 
-configurable settings.
+Note that the file drivers/atm/nicstar.h contains a few configurable 
+settings for the IDT 77201 driver.
 
 Support for a simple type of serial consoles is automatically added by the 
 ATM patch. If you're using LILO, you can enable it by putting a line like
@@ -482,8 +480,22 @@
 the first suitable one will be chosen, i.e. if an application explicitly 
 requests only SVC addresses, any PVC addresses will be ignored.
 
-Support for ANS, a DNS-based resolution mechanism, which is currently in 
-the process of being standardized by ATM Forum, is for further study.
+
+ANS
+---
+
+If you have access to the ATM Name Service (ANS, e.g because you've 
+installed the ANS extension), you can use it instead of or in addition to 
+the hosts file by specifying the host that runs ANS in the /etc/resolv.conf 
+file.
+
+For performing reverse lookups of E.164 addresses, the list of telephony 
+country codes needs to be known. That list can be obtained from  
+http://www.itu.ch/itudoc/itu-t/lists/  the file has a name of the form 
+tf_cc_e_*.rtf The script atm/lib/rtf2e164_cc.pl can be used to create the 
+E.164 county codes table, e.g.
+
+perl rtf2e164_cc.pl <tf_cc_e_13130.rtf >/etc/e164_cc
 
 
 Signaling demon
@@ -572,6 +584,67 @@
 you can also use addresses with a different prefix and an ESI that doesn't 
 correspond to any ESI your adapters have. The value of the selector byte 
 (SEL) is ignored.
+
+
+Running two ATM NICs back-to-back
+---------------------------------
+
+It is also possible to run with two ATM NICs connected back-to-back, and no 
+switch in between*. This is great for simple test environments.
+
+  *  This section was written by Richard Jones  rjones@imcl.com . Comments 
+    should be directed to me as well as to Werner.
+
+First, if you're using UTP or STP-5, you need a suitable cable. Our 
+experience with standard 100Base-T back-to-back cables was not good. It 
+appears that the pin-out they use is different. After some false starts, we 
+found that the following cable works:
+
+RJ45                            RJ45
+   1        ------------        7
+   2        ------------        8
+
+   7        ------------        1
+   8        ------------        2
+
+Pins 3, 4, 5, 6 unconnected.
+
+You can also make up a loopback cable with 1 - 7 and 2 - 8 connected for 
+ultra-cheap setups.
+
+Here we have two machines called "virgil" and "nestor". Substitute your own 
+names as necessary.
+
+One side of the ATM connection needs to use the network version of atmsigd 
+and the other side should use the normal user version. So here on nestor we 
+start atmsigd with:
+
+atmsigd -b -N
+
+and on virgil with:
+
+atmsigd -b
+
+Without a switch, you won't be able to use ILMI. Instead, create a 
+/etc/hosts.atm file containing two dummy addresses. Our ATM hosts file 
+contains:
+
+47.0005.80FFE1000000F21A26D8.0020EA000EE0.00    nestor-atm
+47.0005.80FFE1000000F21A26D8.0020D4102A80.00    virgil-atm
+
+These are completely spurious addresses, of course, but as long as you're 
+not connected to a public or private ATM network, I don't think it matters. 
+To set the address correctly in the driver, we use:
+
+atmaddr -a virgil-atm
+
+on virgil, and:
+
+atmaddr -a nestor-atm
+
+on nestor. Now start atmarpd on both machines in the normal way. Now you 
+(should) have a working ATM set-up. To get IP over ATM and Arequipa 
+working, just follow the instructions in section "IP over ATM".
 
 
 Q.2931 message dumper
diff -ur --new-file old/atm/ilmid/ilmid.c new/atm/ilmid/ilmid.c
--- old/atm/ilmid/ilmid.c	Wed Oct 16 20:52:12 1996
+++ new/atm/ilmid/ilmid.c	Wed Jan 29 20:58:34 1997
@@ -88,6 +88,7 @@
 	  diag(COMPONENT, DIAG_DEBUG, "sending cold-start");
 	  diag(COMPONENT, DIAG_DEBUG, "sending get-next");
 	  send_message(fd, coldstart_message);
+	  sleep(1); /* @@@@@ HACK */
 	  poll_message->data->a.get_next_request->request_id = ++requestID;
 	  send_message(fd, poll_message);
 	  break;
diff -ur --new-file old/atm/led/conn.c new/atm/led/conn.c
--- old/atm/led/conn.c	Thu Nov 28 10:53:38 1996
+++ new/atm/led/conn.c	Mon Jan 27 22:09:16 1997
@@ -558,6 +558,7 @@
   Sap_client_t *sap;
   
   conn = connlist;
+  EVENT(EM_DEBUG,("Conn_call_callbacks %p\n",connlist));
   while(conn) {
     if (conn->status == CONNECTED) {
       /* Connection made, not notified yet */
@@ -579,6 +580,8 @@
 			     0);
       conn = conn->next;
     } else if (conn->status == RELEASED) {
+      EVENT(EM_DEBUG,("Conn_call_callbacks, released conn %p\n",conn));
+#if 0
       conn->status = NOTIFIED;
       sap = (Sap_client_t *)conn->sap_handle;
       sap->vc_notify_callback(conn->conn_context,
@@ -595,6 +598,17 @@
 	mem_free(EINST, conn->conn_info);
       mem_free(EINST,conn);
       conn=c_tmp;
+#endif
+      conn->status = NOTIFIED;
+      c_tmp = conn->next;
+      sap = (Sap_client_t *)conn->sap_handle;
+      sap->vc_notify_callback(conn->conn_context,
+			      conn,
+			      CONN_RELEASE,
+			      CAUSE_NORMAL,
+			      0,
+			      (conn->type==WEMADE)?TRUE:FALSE);
+      conn=c_tmp;
     } else {
       conn = conn->next;
     }
@@ -806,6 +820,7 @@
   }
   conn=connlist;
   while (conn) {
+    tmpconn = conn->next;
     if (FD_ISSET(conn->fd, fds)) {
       EVENT(EM_DEBUG,("Event in fd:%d\n",conn->fd));
       if (conn->type == KERNEL_SOCK) { /* Message from kernel */
@@ -870,7 +885,7 @@
 	}
       }
     }
-    conn = conn->next;
+    conn = tmpconn;
   }
   return reset;
 }
diff -ur --new-file old/atm/led/lec_ctrl.c new/atm/led/lec_ctrl.c
--- old/atm/led/lec_ctrl.c	Thu Nov 28 12:34:48 1996
+++ new/atm/led/lec_ctrl.c	Mon Jan 27 21:45:14 1997
@@ -2376,6 +2376,7 @@
       proxy_vcc_del (p_elan->lport_handle, vpi, vci);
 #endif
       EVENT (EM_EVENT, ("Data Direct VCC Torn Down\n"));
+      cm_sap_svc_teardown(conn_handle);
       break;
       
     case LEC_MCAST :
diff -ur --new-file old/atm/lib/Makefile new/atm/lib/Makefile
--- old/atm/lib/Makefile	Sat Nov 16 00:23:35 1996
+++ new/atm/lib/Makefile	Tue Jan 14 13:23:25 1997
@@ -10,6 +10,7 @@
 PGMS=#test
 GENLIBS=libatm.a libatmd.a libarequipa.a
 SYSHDR=atm.h atmd.h atmsap.h arequipa.h
+# TRASH=cc.inc  keep it around for distribution
 
 all:			libatm.a libatmd.a libarequipa.a
 
@@ -30,3 +31,9 @@
 
 ans_l.o:		ans.o
 			ld -r -o ans_l.o ans.o -L/usr/lib -lresolv
+
+ans.o:			cc.inc
+
+cc.inc:			rtf2cc.pl
+			perl rtf2cc.pl <tf_cc_e_*.rtf >cc.inc || \
+			  { rm cc.inc; exit 1; }
diff -ur --new-file old/atm/lib/ans.c new/atm/lib/ans.c
--- old/atm/lib/ans.c	Wed Nov 27 20:11:39 1996
+++ new/atm/lib/ans.c	Fri Jan 17 13:44:41 1997
@@ -12,6 +12,8 @@
  */
 
 
+#include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 
 #include <netinet/in.h>
@@ -27,6 +29,9 @@
 #define MAX_ANSWER 2048
 #define MAX_NAME   1024
 
+#define MAX_LINE		2048	/* in /etc/e164_cc */
+#define E164_CC_DEFAULT_LEN	   2
+#define E164_CC_FILE		"/etc/e164_cc"
 
 #define GET16(pos) (((pos)[0] << 8) | (pos)[1])
 
@@ -101,82 +106,115 @@
 		    return 0;
 		default:
 		    continue;
-	    }
 	}
-	if (!found) return TRY_OTHER;
-	memcpy(((struct sockaddr_atmsvc *) result)->sas_addr.pub,found,found_len);
-	((struct sockaddr_atmsvc *) result)->sas_addr.pub[found_len] = 0;
-	return 0;
     }
+    if (!found) return TRY_OTHER;
+    memcpy(((struct sockaddr_atmsvc *) result)->sas_addr.pub,found,
+      found_len);
+    ((struct sockaddr_atmsvc *) result)->sas_addr.pub[found_len] = 0;
+    return 0;
+}
+
+
+int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length,
+  int flags)
+{
+    if (!(flags & T2A_SVC) || length != sizeof(*addr)) return TRY_OTHER; 
+    memset(addr,0,sizeof(*addr));
+    addr->sas_family = AF_ATMSVC;
+    return ans(text,T_ATMA,addr,length);
+}
 
 
-    int ans_byname(const char *text,struct sockaddr_atmsvc *addr,int length,
-      int flags)
-    {
-	if (!(flags & T2A_SVC) || length != sizeof(*addr)) return TRY_OTHER; 
-	memset(addr,0,sizeof(*addr));
-	addr->sas_family = AF_ATMSVC;
-	return ans(text,T_ATMA,addr,length);
+static int encode_nsap(char *buf,const unsigned char *addr)
+{
+    static int fmt_dcc[] = { 2,12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+      4,2,0 };
+    static int fmt_e164[] = { 2,12,1,1,1,1,1,1,1,1,16,2,0 };
+    int *fmt;
+    int pos,i,j;
+
+    switch (*addr) {
+	case ATM_AFI_DCC:
+	case ATM_AFI_ICD:
+	    fmt = fmt_dcc;
+	    break;
+	case ATM_AFI_E164:
+	    fmt = fmt_e164;
+	    break;
+	default:
+	    return TRY_OTHER;
+    }
+    pos = 2*ATM_ESA_LEN;
+    for (i = 0; fmt[i]; i++) {
+	pos -= fmt[i];
+	for (j = 0; j < fmt[i]; j++)
+	    sprintf(buf++,"%x",
+	      (addr[(pos+j) >> 1] >> 4*(1-((pos+j) & 1))) & 0xf);
+	*buf++ = '.';
     }
+    strcpy(buf,"AESA.ATMA.INT.");
+    return 0;
+}
 
 
-    static int encode_nsap(char *buf,const unsigned char *addr)
-    {
-	static int fmt_dcc[] = { 2,12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-	  4,2,0 };
-	static int fmt_e164[] = { 2,12,1,1,1,1,1,1,1,1,16,2,0 };
-	int *fmt;
-	int pos,i,j;
-
-	switch (*addr) {
-	    case ATM_AFI_DCC:
-	    case ATM_AFI_ICD:
-		fmt = fmt_dcc;
-		break;
-	    case ATM_AFI_E164:
-		fmt = fmt_e164;
-		break;
-	    default:
-		return TRY_OTHER;
+int cc_len(int p0,int p1)
+{
+    static char *cc_table = NULL;
+    FILE *file;
+    char buffer[MAX_LINE];
+    char *here;
+    int cc;
+
+    if (!cc_table) {
+	if (!(cc_table = malloc(100))) {
+	    perror("malloc");
+	    return E164_CC_DEFAULT_LEN;
 	}
-	pos = 2*ATM_ESA_LEN;
-	for (i = 0; fmt[i]; i++) {
-	    pos -= fmt[i];
-	    for (j = 0; j < fmt[i]; j++)
-		sprintf(buf++,"%x",
-		  (addr[(pos+j) >> 1] >> 4*(1-((pos+j) & 1))) & 0xf);
-	    *buf++ = '.';
+	memset(cc_table,E164_CC_DEFAULT_LEN,100);
+	if (!(file = fopen(E164_CC_FILE,"r")))
+	    perror(E164_CC_FILE);
+	else {
+	    while (fgets(buffer,MAX_LINE,file)) {
+		here = strchr(buffer,'#');
+		if (here) *here = 0;
+		if (sscanf(buffer,"%d",&cc) == 1)
+		    if (cc < 10) cc_table[cc] = 1;
+		    else if (cc < 100) cc_table[cc] = 2;
+			else cc_table[cc/10] = 3;
+	    }
+	    fclose(file);
 	}
-	strcpy(buf,"AESA.ATMA.INT.");
-	return 0;
     }
+    if (cc_table[p0] == 1) return 1;
+    return cc_table[p0*10+p1];
+}
 
 
-    static int encode_e164(char *buf,const char *addr)
-    {
-	char *here;
+static int encode_e164(char *buf,const char *addr)
+{
+    const char *prefix,*here;
 
-	/*
-	 * @@@ This is wrong. It should actually look up the country prefix in a
-	 * table to determine how many characters have to be copied "en block".
-	 */
-	for (here = strchr(addr,0); here > addr;) {
-	    *buf++ = *--here;
-	    *buf++ = '.';
-	}
-	strcpy(buf,"E164.ATMA.INT.");
-	return 0;
+    prefix = addr+cc_len(addr[0]-48,addr[1]-48);
+    here = strchr(addr,0);
+    while (here > prefix) {
+	*buf++ = *--here;
+	*buf++ = '.';
     }
+    while (here > addr) *buf++ = *addr++;
+    strcpy(buf,".E164.ATMA.INT.");
+    return 0;
+}
 
 
-    int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr,
-      int flags)
-    {
-	char tmp[MAX_NAME]; /* could be smaller ... */
-	int res;
-
-	if (addr->sas_addr.prv) res = encode_nsap(tmp,addr->sas_addr.prv);
-	else res = encode_e164(tmp,addr->sas_addr.pub);
-	if (res < 0) return res;
-	return ans(tmp,T_PTR,buffer,length);
-    }
+int ans_byaddr(char *buffer,int length,const struct sockaddr_atmsvc *addr,
+  int flags)
+{
+    char tmp[MAX_NAME]; /* could be smaller ... */
+    int res;
+
+    if (addr->sas_addr.prv) res = encode_nsap(tmp,addr->sas_addr.prv);
+    else res = encode_e164(tmp,addr->sas_addr.pub);
+    if (res < 0) return res;
+    return ans(tmp,T_PTR,buffer,length);
+}
diff -ur --new-file old/atm/lib/cc.inc new/atm/lib/cc.inc
--- old/atm/lib/cc.inc	Thu Jan  1 01:00:00 1970
+++ new/atm/lib/cc.inc	Fri Jan 10 17:38:34 1997
@@ -0,0 +1,3 @@
+static const char bits[] =
+  "001001111101110000010110100000000001100000000110000000111011"
+  "00101011110000001101";
diff -ur --new-file old/atm/lib/rtf2cc.pl new/atm/lib/rtf2cc.pl
--- old/atm/lib/rtf2cc.pl	Thu Jan  1 01:00:00 1970
+++ new/atm/lib/rtf2cc.pl	Fri Jan 10 17:14:44 1997
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+#
+# The E.164 country code listing can be obtained from
+# http://www.itu.ch/itudoc/itu-t/lists/tf_cc_e_*.rtf
+#
+# This program generates the following coding:
+# 
+# 0: this is the last digit
+# 1: more digits follow; the next bits describe the ten digits
+#
+# The hierarchy is limited to two digits (i.e. the bit indicating a third
+# digit is present but no bits for further digits are there)
+
+while (<>) {
+    next unless
+      /{\\fs18\\cf1\s(\d+)}{\\f7\\fs24\s\\tab\s}{\\fs18\\cf1\s([^}]+)}/;
+    $more[$1/10] = 1;
+}
+$more[0] = 0; # anomaly
+for $a (0..9) {
+    if (!$more[$a]) { $bits .= "0"; }
+    else {
+	$bits .= "1";
+	for $b (0..9) {
+	    $bits .= sprintf("%d",$more[$a*10+$b]);
+	}
+    }
+}
+print "static const char bits[] =" || die "write: $!";
+while (length($bits)) {
+    print "\n" || die "write: $!";
+    print "  \"".substr($bits,0,60)."\"" || die "write: $!";
+    $bits = substr($bits,60);
+}
+print ";\n" || die "write: $!";
diff -ur --new-file old/atm/mkdist new/atm/mkdist
--- old/atm/mkdist	Fri Dec 20 21:49:01 1996
+++ new/atm/mkdist	Fri Jan 17 15:32:16 1997
@@ -72,7 +72,7 @@
     atm/lib/atmd.h atm/lib/common.c atm/lib/diag.c \
     atm/lib/timer.c atm/lib/arequipa.h atm/lib/arequipa.c \
     atm/lib/text2qos.c atm/lib/qos2text.c atm/lib/qosequal.c \
-    atm/lib/atmres.h atm/lib/ans.c \
+    atm/lib/atmres.h atm/lib/ans.c atm/lib/rtf2cc.pl atm/lib/cc.inc \
   atm/led/USAGE atm/led/COPYRIGHT.DEC atm/led/COPYRIGHT.TUT \
     atm/led/lec.h atm/led/lec_arp.h atm/led/lec_ctrl.h atm/led/emask.h \
     atm/led/le_disp.h atm/led/g_event.h \
@@ -102,10 +102,13 @@
     atm/aqd/arequipad.8 \
   atm/extra/extra.html atm/extra/Makefile atm/extra/tcpdump-3.0.4-1.patch \
     atm/extra/bind-4.9.5-REL.patch atm/extra/hosts2ans.pl \
-  atm/config/redhat-4.0/README \
+  atm/config/README atm/config/Makefile atm/config\
+    atm/config/common/README atm/config/common/Makefile \
+    atm/config/common/hosts.atm atm/config/common/e164_cc \
+    atm/config/redhat-4.0/README atm/config/redhat-4.0/Makefile \
     atm/config/redhat-4.0/atm atm/config/redhat-4.0/atm.init \
     atm/config/redhat-4.0/atmsigd.conf atm/config/redhat-4.0/ifcfg-atm0 \
     atm/config/redhat-4.0/ifup-atm \
-  atm/atm.patch atm/mpr.patch |
+  atm/atm.patch atm/mpr.patch atm/atm-$VERSION-1.spec |
   gzip -9 >$ARCHDIR/atm-$VERSION.tar.gz
 #atm/bind-4.9.4.T4B.ATM.patch
diff -ur --new-file old/atm/qgen/Makefile new/atm/qgen/Makefile
--- old/atm/qgen/Makefile	Fri Dec 20 21:39:16 1996
+++ new/atm/qgen/Makefile	Fri Jan 10 12:34:01 1997
@@ -10,6 +10,7 @@
 SYMFILES=q2931.h /usr/include/linux/atmsap.h
 TRASH=default.nl
 
+NOLIBATMDEP=yes
 include ../Rules.make
 
 qgen:				$(OBJS)
diff -ur --new-file old/atm/qgen/first.c new/atm/qgen/first.c
--- old/atm/qgen/first.c	Tue Jan 30 21:25:21 1996
+++ new/atm/qgen/first.c	Wed Jan 15 13:56:27 1997
@@ -1,6 +1,6 @@
 /* first.c - Phase I, input data preprocessing */
 
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 
 
 #include <stdlib.h>
@@ -39,7 +39,7 @@
     } })
 
 
-static void process(FIELD *start)
+static void process(FIELD *start,int defines)
 {
     FIELD *walk;
     TAG *scan;
@@ -47,10 +47,21 @@
     int tmp,shift,next;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    int start_field,start_group;
+	    start_field = field;
+	    start_group = group;
+	    process(walk->structure->block,walk->structure->instances > 0);
+	    if (walk->structure->instances > 1)
+		walk->structure->instances = -1;
+	    walk->structure->fields = field-start_field;
+	    walk->structure->groups = group-start_group;
+	    continue;
+	}
 	/* set field number */
 	walk->seq = seq++;
 	if (*walk->id != '_') {
-	    to_h("#define QF_%s %d\n",walk->id,field);
+	    if (defines) to_h("#define QF_%s %d\n",walk->id,field);
 	    to_test("    \"%s\",\n",walk->id);
 	    walk->field = field++;
 	}
@@ -102,14 +113,14 @@
 			orig_mask = mask;
 			for (scan = walk->value->tags; scan; scan = scan->next)
 			  {
-			    if (scan->id)
+			    if (scan->id && defines)
 				to_h("#define QG_%s %d\n",scan->id,-group-1);
 			    scan->group = group++;
 			    scan->pos = walk->pos;
 			    mask = orig_mask;
 			    to_c("    q_put(q_initial,%d,%d,%s); /* %s */\n",
 			      walk->pos,walk->size,scan->value,walk->id);
-			    process(scan->block);
+			    process(scan->block,defines);
 			    if (mask) offset += 8;
 			}
 			break;
@@ -119,7 +130,7 @@
 		case vt_multi:
 		    orig_mask = mask;
 		    for (scan = walk->value->tags; scan; scan = scan->next) {
-			if (scan->id)
+			if (scan->id && defines)
 			    to_h("#define QG_%s %d\n",scan->id,-group-1);
 			scan->group = group++;
 			scan->pos = walk->pos;
@@ -136,14 +147,14 @@
 			    to_c("    q_put(q_initial,%d,%d,%s); /* %s */\n",
 			      scan->pos,walk->size,scan->value,walk->id);
 			}
-			process(scan->block);
+			process(scan->block,defines);
 			if (*walk->id != '_' && mask) die("EF129");
 		    }
 		    break;
 		case vt_length:
 		    ALLOC(walk);
 		    offset += walk->jump*8;
-		    process(walk->value->block);
+		    process(walk->value->block,defines);
 		    break;
 		default:
 		    abort();
@@ -164,7 +175,7 @@
     to_c("static void q_init_global(void)\n{\n");
     to_c("    memset(q_initial,0,sizeof(q_initial));\n");
     to_test("static const char *fields[] = {\n");
-    process(def);
+    process(def,1);
     to_test("    NULL\n};\n\n");
     to_c("}\n\n");
     if (mask) offset += 8;
diff -ur --new-file old/atm/qgen/q2931.h new/atm/qgen/q2931.h
--- old/atm/qgen/q2931.h	Fri Dec 13 12:59:04 1996
+++ new/atm/qgen/q2931.h	Wed Jan  8 16:13:18 1997
@@ -25,6 +25,7 @@
 
 /* Message types */
 
+#define QMSG_NATIONAL		0x00	/* National specific message escape */
 #define QMSG_SETUP		0x05	/* SETUP */
 #define QMSG_ALERTING		0x01	/* ALERTING */
 #define QMSG_CALL_PROC		0x02	/* CALL PROCEEDING */
@@ -43,6 +44,7 @@
 #define QMSG_PARTY_ALERT	0x85	/* PARTY ALERTING */
 #define QMSG_DROP_PARTY		0x83	/* DROP PARTY */
 #define QMSG_DROP_PARTY_ACK	0x84	/* DROP PARTY ACKNOWLEDGE */
+#define QMSG_RESERVED		0xff	/* reserved for EVEN MORE msg types */
 
 /* Information element identifiers */
 
diff -ur --new-file old/atm/qgen/qgen.h new/atm/qgen/qgen.h
--- old/atm/qgen/qgen.h	Fri Sep 27 20:39:52 1996
+++ new/atm/qgen/qgen.h	Wed Jan 15 13:56:25 1997
@@ -1,6 +1,6 @@
 /* qgen.h - constructor/parser generator for Q.2931-like data structures */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
  
 #ifndef QGEN_H
@@ -8,7 +8,20 @@
 
 typedef enum { vt_id,vt_case,vt_multi,vt_length } VALUE_TYPE;
 
+typedef struct _macro {
+    /* --- the following fields are initialized by the parser */
+    const char *id;
+    struct _field *block; /* NULL after first use */
+    int instances; /* this is later modified in the first pass
+		      (> 1 becomes -1) */
+    struct _macro *next;
+    /* --- the following fields are initialized in the first phase */
+    int fields; /* number of fields covered by this structure */
+    int groups; /* number of groups covered by this structure */
+} STRUCTURE;
+
 typedef struct _name {
+    /* --- the following fields are initialized by the parser */
     const char *value;
     const char *name;
     struct _name *next;
@@ -42,10 +55,10 @@
     int var_len;
     int pos; /* modified in the first phase */
     int flush;
-    int repetitions;
     VALUE *value;
-    int brk; /* may break before this field */
+    int brk; /* != 0: may break before this field */
     struct _field *next;
+    STRUCTURE *structure; /* NULL if this entry isn't a structure */
     /* --- the following fields are initialized in the first phase */
     int field; /* field number */
     int jump; /* move by that many bytes before writing that field */
diff -ur --new-file old/atm/qgen/ql.y new/atm/qgen/ql.y
--- old/atm/qgen/ql.y	Fri Sep 27 21:39:09 1996
+++ new/atm/qgen/ql.y	Wed Jan 15 13:51:08 1997
@@ -1,7 +1,7 @@
 %{
 /* ql.y - Q.2931 data structures description language */
 
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 
 
 #include <stdlib.h>
@@ -19,18 +19,8 @@
 #define DEFAULT_NAMELIST_FILE "default.nl"
 
 
-typedef struct _macro {
-    const char *id;
-    FIELD *block;
-    int repeated;
-    struct _macro *next;
-} MACRO;
-
-
 FIELD *def = NULL;
-static MACRO *macros = NULL;
-static int repeated = 0; /* current macro contains repeatitions */
-static int repeating = 0; /* ancestor is repeated */
+static STRUCTURE *structures = NULL;
 static const char *abort_id; /* indicates abort flag */
 
 
@@ -106,7 +96,7 @@
 %token <str>	TOK_ID TOK_INCLUDE TOK_STRING
 
 %type <field>	rep_block block fields field field_cont
-%type <num>	repetition opt_break opt_pos decimal opt_more
+%type <num>	opt_break opt_pos decimal opt_more
 %type <value>	opt_val value
 %type <tag>	tags rep_tags
 %type <list>	list
@@ -116,13 +106,14 @@
 %%
 
 all:
-    includes macros block
+    includes structures block
 	{
-	    MACRO *walk;
+	    STRUCTURE *walk;
 
 	    def = $3;
-	    for (walk = macros; walk; walk = walk->next)
-		fprintf(stderr,"unused macro: %s\n",walk->id);
+	    for (walk = structures; walk; walk = walk->next)
+		if (!walk->instances)
+		    fprintf(stderr,"unused structure: %s\n",walk->id);
 	}
     ;
 
@@ -135,69 +126,50 @@
 	}
     ;
 
-macros:
-    | macros macro
+structures:
+    | structures structure
     ;
 
-macro:
+structure:
     TOK_DEF TOK_ID '=' block
 	{
-	    MACRO *n;
+	    STRUCTURE *n;
 
-	    n = alloc_t(MACRO);
+	    n = alloc_t(STRUCTURE);
 	    n->id = $2;
 	    n->block = $4;
-	    n->repeated = repeated;
-	    n->next = macros;
-	    macros = n;
-	    repeated = 0;
+	    n->instances = 0;
+	    n->next = structures;
+	    structures = n;
 	}
     ;
 
 rep_block:
-    repetition
 	{
-	    if ($1 > 1) {
-		if (repeating) yyerror("can't nest repetitions");
-		repeating = repeated = 1;
-	    }
 	    abort_id = NULL;
 	}
       block
 	{
-	    repeating = 0;
-	    $$ = $3;
-	    if ($$) $$->repetitions = $1;
-	}
-    ;
-
-repetition:
-	{
-	    $$ = 1;
-	}
-    | '[' decimal ']'
-	{
 	    $$ = $2;
-	    if ($2 < 1) yyerror("invalid repetition count");
 	}
     ;
 
 block:
     TOK_ID
 	{
-	    MACRO **walk,*next;
+	    STRUCTURE *walk;
 
-	    for (walk = &macros; *walk; walk = &(*walk)->next)
-		if ((*walk)->id == $1) break;
-	    if (!*walk) yyerror("no such macro");
-	    $$ = (*walk)->block;
-	    if ((*walk)->repeated) {
-		if (repeating) yyerror("can't nest repetitions");
-		repeated = 1;
-	    }
-	    next = (*walk)->next;
-	    free(*walk);
-	    *walk = next;
+	    for (walk = structures; walk; walk = walk->next)
+		if (walk->id == $1) break;
+	    if (!walk) yyerror("no such structure");
+	    walk->instances++;
+	    $$ = alloc_t(FIELD);
+	    $$->id = NULL;
+	    $$->name_list = NULL;
+	    $$->value = NULL;
+	    $$->brk = 0;
+	    $$->structure = walk;
+	    $$->next = NULL;
 	    abort_id = NULL;
 	}
     | '{' fields '}'
@@ -265,6 +237,7 @@
 	    $$->pos = 0;
 	    $$->flush = 1;
 	    $$->value = NULL;
+	    $$->structure = NULL;
 	    $$->next = NULL;
 	}
      | decimal opt_pos opt_more '>' opt_val
@@ -279,6 +252,7 @@
 		    yyerror("position required for small fields");
 		else $$->pos = 0;
 	    $$->value = $5;
+	    $$->structure = NULL;
 	    $$->next = NULL;
 	}
     ;
diff -ur --new-file old/atm/qgen/qlib.c new/atm/qgen/qlib.c
--- old/atm/qgen/qlib.c	Thu Nov 28 20:21:06 1996
+++ new/atm/qgen/qlib.c	Wed Jan 15 13:41:24 1997
@@ -1,6 +1,6 @@
 /* qlib.c - run-time library */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #ifdef DUMP_MODE
@@ -158,8 +158,19 @@
 static int q_init(Q_DSC *dsc)
 {
     size_t bytes;
+    int i;
 
+    /* initialize verything in case anything goes wrong during allocations. */
+    dsc->errors = NULL;
+    dsc->field_present = NULL;
+    dsc->group_present = NULL;
+    dsc->data = NULL;
+    dsc->required = NULL;
+    dsc->length = NULL;
+    dsc->field_map = NULL;
+    dsc->group_map = NULL;
     dsc->data = malloc((size_t) Q_DATA_BYTES);
+    dsc->error = 1;
     if (!dsc->data) {
 	perror("out of memory");
 	return -1;
@@ -194,8 +205,19 @@
 	}
 	memset(dsc->length,0,sizeof(int)*Q_VARLEN_FIELDS);
     }
+    dsc->field_map = malloc(sizeof(int)*Q_FIELDS);
+    if (!dsc->field_map) {
+	perror("out of memory");
+	return -1;
+    }
+    for (i = 0; i < Q_FIELDS; i++) dsc->field_map[i] = i;
+    dsc->group_map = malloc(sizeof(int)*Q_GROUPS);
+    if (!dsc->group_map) {
+	perror("out of memory");
+	return -1;
+    }
+    for (i = 0; i < Q_GROUPS; i++) dsc->group_map[i] = i;
     dsc->error = 0;
-    dsc->errors = NULL;
     return 0;
 }
 
@@ -221,6 +243,7 @@
 
     if (field < 0 || field >= Q_FIELDS)
 	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
+    field = dsc->field_map[field];
     if (!fields[field].values) {
 	if (q_test(dsc->field_present,field)) /* probably an error ... */
 	    PREFIX(report)(Q_ERROR,"changing field %d",field);
@@ -248,6 +271,7 @@
 {
     if (field < 0 || field >= Q_FIELDS)
 	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
+    field = dsc->field_map[field];
     if (fields[field].pos & 7)
 	PREFIX(report)(Q_FATAL,"invalid use of q_write (%d)",field);
     if (fields[field].actual >= 0) {
@@ -273,11 +297,15 @@
     if (field < 0) {
 	if (field < -Q_GROUPS)
 	    PREFIX(report)(Q_FATAL,"invalid group number (%d)",field);
-	return q_test((unsigned char *) dsc->group_present,-field-1);
+	if (!dsc->group_present) return 0;
+	field = dsc->group_map[-field-1];
+	return q_test((unsigned char *) dsc->group_present,field);
     }
     else {
 	if (field >= Q_FIELDS)
 	    PREFIX(report)(Q_FATAL,"invalid field number (%d)",field);
+	if (!dsc->field_present) return 0;
+	field = dsc->field_map[field];
 	if (q_test(dsc->field_present,field)) return 1;
 	return q_test((unsigned char *) dsc->group_present,
 	  fields[field].parent);
@@ -289,6 +317,7 @@
 {
     if (field < 0 || field >= Q_FIELDS)
 	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
+    field = dsc->field_map[field];
     return q_get(dsc->data,fields[field].pos,fields[field].size);
 }
 
@@ -297,6 +326,7 @@
 {
     if (field < 0 || field >= Q_FIELDS)
 	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
+    field = dsc->field_map[field];
     if (fields[field].pos & 7)
 	PREFIX(report)(Q_FATAL,"invalid use of q_length (%d)",field);
     if (fields[field].actual < 0)
@@ -311,6 +341,7 @@
 
     if (field < 0 || field >= Q_FIELDS)
 	PREFIX(report)(Q_FATAL,"invalid field value (%d)",field);
+    field = dsc->field_map[field];
     if (fields[field].pos & 7)
 	PREFIX(report)(Q_FATAL,"invalid use of q_read (%d)",field);
     if (fields[field].actual >= 0) len = dsc->length[fields[field].actual];
@@ -328,6 +359,11 @@
 }
 
 
+void q_instance(Q_DSC *dsc,int group)
+{
+}
+
+
 static int q_compose(Q_DSC *dsc,unsigned char *buf,int size)
 {
     LEN_BUF stack[LENGTH_STACK];
@@ -461,6 +497,10 @@
       *sp,*rp,*pos-buf,*end-buf);
     error = alloc_t(Q_ERR_DSC);
     error->type = type;
+    for (last = &dsc->errors; *last; last = &(*last)->next);
+    *last = error;
+    error->next = NULL;
+    if (!pc) return;
     error->pc = *pc-parse;
     error->offset = *pos-buf;
     error->value = value;
@@ -483,9 +523,6 @@
 	*pc = parse+sizeof(parse)/sizeof(*parse)-1;
 	*pos = buf+size;
     }
-    for (last = &dsc->errors; *last; last = &(*last)->next);
-    *last = error;
-    error->next = NULL;
 }
 
 
@@ -533,7 +570,7 @@
 		    }
 		    else {
 			for (i = 0; i < len/8; i++)
-			    DUMP(" %02x",pos[pc[1]/8+i]);
+			    DUMP(" %02x",pos[i]);
 			DUMP("\n");
 		    }
 		}
@@ -617,6 +654,11 @@
 			if (*pc != value && *pc != -1) pc += 3;
 			else {
 			    pc++;
+			    if (*pc != -1 && q_test((unsigned char *)
+			      dsc->group_present,*pc)) {
+				pc += 2;
+				continue;
+			    }
 			    for (group = *pc++; group != -1; group =
 			      groups[group].parent)
 				q_set((unsigned char *) dsc->group_present,
@@ -717,8 +759,15 @@
 
 int PREFIX(open)(Q_DSC *dsc,void *buf,int size)
 {
+    int error;
+
     dsc->buffer = NULL;
-    q_init(dsc);
+    error = q_init(dsc);
+    if (error) {
+	handle_error(dsc,size,buf,NULL,NULL,NULL,NULL,NULL,NULL,NULL,qet_init,
+	  0);
+	return error;
+    }
     return q_parse(dsc,buf,size);
 }
 
@@ -729,8 +778,7 @@
 {
    dsc->buffer = buf;
    dsc->buf_size = size;
-   q_init(dsc);
-   return 0;
+   return q_init(dsc);
 }
 
 #endif
@@ -744,11 +792,13 @@
     if (dsc->buffer && !dsc->error)
 	size = q_compose(dsc,dsc->buffer,dsc->buf_size);
 #endif
-    free(dsc->data);
-    free(dsc->required);
-    free(dsc->field_present);
-    free(dsc->group_present);
+    if (dsc->data) free(dsc->data);
+    if (dsc->required) free(dsc->required);
+    if (dsc->field_present) free(dsc->field_present);
+    if (dsc->group_present) free(dsc->group_present);
     if (dsc->length) free(dsc->length);
+    if (dsc->field_map) free(dsc->field_map);
+    if (dsc->group_map) free(dsc->group_map);
     while (dsc->errors) {
 	Q_ERR_DSC *next;
 
diff -ur --new-file old/atm/qgen/qlib.h new/atm/qgen/qlib.h
--- old/atm/qgen/qlib.h	Fri Dec 13 12:50:43 1996
+++ new/atm/qgen/qlib.h	Wed Jan 15 13:41:39 1997
@@ -1,6 +1,6 @@
 /* qlib.h - run-time library */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
  
 #ifndef QLIB_H
@@ -41,7 +41,13 @@
 #endif
 
 
-typedef enum { qet_catch_zero,qet_space,qet_case,qet_abort } Q_ERR_TYPE;
+typedef enum {
+    qet_catch_zero,
+    qet_space,	/* length of message area exceeds length of surroundings */
+    qet_case,	/* case value not found */
+    qet_abort,	/* user abort */
+    qet_init	/* fatal initialization error. DO NOT PROCEED ! */
+} Q_ERR_TYPE;
 
 typedef struct _q_err_dsc {
     Q_ERR_TYPE type; /* error type code */
@@ -61,6 +67,8 @@
     unsigned char *field_present;
     unsigned long *group_present;
     int *length;
+    int *field_map;
+    int *group_map;
     void *buffer;
     int buf_size;
     int error;
@@ -68,17 +76,97 @@
 } Q_DSC;
  
 
+/*
+ * *_START intializes global data structures and needs to be invoked before any
+ * other function of qlib.
+ */
+
 void PREFIX(start)(void);
+
+/*
+ * *_OPEN opens an existing Q.2931 message (pointed to be BUF and of size
+ * SIZE), parses its contents, and initializes the descriptor for further use
+ * by Q_PRESENT, etc. *_OPEN returns zero on success, -1 if any problem has
+ * been discovered with the message. In the latter case, the error list on
+ * DSC.errors should be examined. Note that parts of the message may still be
+ * valid and can be accessed with the usual functions. (Actually, because
+ * invalid or unrecognized fields will simply be treated as absent, code that
+ * tests for presence of mandatory elements does not need to examine the
+ * error list. Not that, however, an error report of type QET_INIT carries
+ * incomplete information and must not be examined beyond the TYPE element.
+ * Also, _all_ calls to Q_PRESENT will return zero in this case.) The
+ * descriptor must be closed with *_CLOSE even if *_OPEN fails.
+ */
+
 int PREFIX(open)(Q_DSC *dsc,void *buf,int size);
+
+/*
+ * Q_CREATE initializes the descriptor DSC points to. It also registers the
+ * message area (to be used by Q_CLOSE) starting at BUF and of size SIZE in the
+ * descriptor. Q_CREATE returns zero on success, -1 on failure. *_CLOSE does
+ * not need to be called if Q_CREATE fails.
+ */
+
 int q_create(Q_DSC *dsc,void *buf,int size);
+
+/*
+ * Deallocates resources from the specified descriptor. If the descriptor was
+ * initialized by Q_CREATE and if no error occurred, the message is composed
+ * and *_CLOSE returns its size in bytes. In all other cases, *_CLOSE returns
+ * zero on success, -1 on failure. Note that *_CLOSE also clears the error
+ * list.
+ */
+
 int PREFIX(close)(Q_DSC *dsc);
 
+/*
+ * Q_ASSIGN sets the specified field to VALUE. Q_ASSIGN can only be used with
+ * fixed-length fields of a size less than sizeof(unsigned long)*8 bits. Note
+ * that the values of fields with associated groups can't be changed.
+ */
+
 void q_assign(Q_DSC *dsc,int field,unsigned long value);
+
+/*
+ * Like Q_ASSIGN, but for all fields (without associated groups) whose size is
+ * a multiple of one byte.
+ */
+
 void q_write(Q_DSC *dsc,int field,const void *buf,int size);
 
+/*
+ * Checks the presence of the specified field (QF_*) or group (QG_*). Returns
+ * one if the field or group is present, zero otherwise.
+ */
+
 int q_present(const Q_DSC *dsc,int field);
+
+/*
+ * Returns the value of the specified fields. Be warned that Q_FETCH does not
+ * check if the fields is present or if its content fits in an unsigned long.
+ */
+
 unsigned long q_fetch(const Q_DSC *dsc,int field);
+
+/*
+ * Like Q_FETCH, but for byte-aligned fields of arbitrary size (as long as it's
+ * a multiple of one byte). BUF points to a buffer and SIZE is its size. Q_READ
+ * returns how many bytes were written to that buffer.
+ */
+
 int q_read(Q_DSC *dsc,int field,void *buf,int size);
+
+/*
+ * Returns the size (in bytes) of the specified variable-length field.
+ */
+
 int q_length(const Q_DSC *dsc,int field);
+
+/*
+ * Enables all instances which are controlled by the specified group.
+ * It is an error to use a groups that controls no instances.
+ */
+
+void q_instance(Q_DSC *dsc,int group);
 
 #endif
diff -ur --new-file old/atm/qgen/second.c new/atm/qgen/second.c
--- old/atm/qgen/second.c	Sat Aug 31 14:42:19 1996
+++ new/atm/qgen/second.c	Wed Jan 15 12:37:58 1997
@@ -1,6 +1,6 @@
 /* second.c - Phase II, table generation */
 
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 
 
 #include <stdlib.h>
@@ -19,6 +19,10 @@
     FIELD *walk;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    dump_required(walk->structure->block,leader,group);
+	    continue;
+	}
 	if (walk->brk) break;
 	if (!walk->value) {
 	    if (!leader->has_required) {
@@ -40,7 +44,11 @@
     FIELD *walk;
     TAG *scan;
 
-    for (walk = start; walk; walk = walk->next)
+    for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    find_required(walk->structure->block);
+	    continue;
+	}
 	if (walk->value) 
 	    switch  (walk->value->type) {
 		case vt_id:
@@ -60,6 +68,7 @@
 		default:
 		    abort();
 	    }
+    }
 }
 
 
@@ -69,6 +78,10 @@
     TAG *scan;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    groups(walk->structure->block,group);
+	    continue;
+	}
 	if (walk->value)
 	    switch  (walk->value->type) {
 		case vt_id:
@@ -100,7 +113,10 @@
     TAG *scan;
     VALUE_LIST *tag;
 
-    for (walk = start; walk; walk = walk->next)
+    for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    values(walk->structure->block);
+	}
 	if (walk->value)
 	    switch (walk->value->type) {
 		case vt_id:
@@ -131,6 +147,7 @@
 		default:
 		    abort();
 	    }
+    }
 }
 
 
@@ -141,6 +158,10 @@
     VALUE_LIST *tag;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    fields(walk->structure->block,group);
+	    continue;
+	}
 	if (*walk->id != '_')
 	    if (walk->value && walk->value->type == vt_case)
 		to_c("    { %d, %d, %d, values_%d, %d }, /* %s */\n",group,
@@ -182,6 +203,10 @@
     VALUE_LIST *tag;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    symbolic_names(walk->structure->block);
+	    continue;
+	}
 	if (walk->name_list ? walk->name_list->id == -1 : !!walk->value) {
 	    if (walk->name_list) {
 		to_dump("static SYM_NAME dump_sym_%d[] = { /* %s */\n",
diff -ur --new-file old/atm/qgen/third.c new/atm/qgen/third.c
--- old/atm/qgen/third.c	Fri Sep 27 20:44:35 1996
+++ new/atm/qgen/third.c	Wed Jan 15 12:38:17 1997
@@ -1,6 +1,6 @@
 /* third.c - Phase III, code generation */
 
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 
 
 #include <stdlib.h>
@@ -29,6 +29,10 @@
     int patch_here;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    construct(walk->structure->block);
+	    continue;
+	}
 	if (!walk->value)
 	    if (walk->var_len == -1)
 		code("%s%d%d%d/* %s */\n","OP_COPY",walk->jump,walk->pos,
@@ -101,6 +105,10 @@
     BREAK *brk,*tmp_brks,*next;
 
     for (walk = start; walk; walk = walk->next) {
+	if (walk->structure) {
+	    parser(walk->structure->block,level,group);
+	    continue;
+	}
 	if (walk->brk) {
 	    code("%s%s\n","OP_IFEND","?");
 	    brk = alloc_t(BREAK);
diff -ur --new-file old/atm/qgen/uni3x new/atm/qgen/uni3x
--- old/atm/qgen/uni3x	Tue Oct  1 17:25:58 1996
+++ new/atm/qgen/uni3x	Fri Jan 17 19:57:00 1997
@@ -170,7 +170,7 @@
 			}
 		    }
 		    ATM_L2_USER { # User specified
-			_ext <1@8> = 1
+			_ext <1@8> = 0
 			user_l2 <7@1>
 		    }
 		    default ATM_L2_ISO1745,ATM_L2_Q291,ATM_L2_LAPB,
@@ -229,7 +229,7 @@
 			}
 		    }
 		    ATM_L3_USER { # User specified
-			_ext <1@8> = 1
+			_ext <1@8> = 0
 			user_l3 <7@1>
 		    }
 		}
@@ -466,13 +466,20 @@
 	    td:		ATM_IE_TD		ie_td
 	    bbcap:	ATM_IE_BBCAP		ie_bbcap
 	    bhli:	ATM_IE_BHLI		ie_bhli
-	    blli:	ATM_IE_BLLI		[3] ie_blli
+	    blli:	ATM_IE_BLLI		ie_blli
+/*
+	    blli2:	ATM_IE_BLLI		ie_blli
+	    blli3:	ATM_IE_BLLI		ie_blli
+*/
 	    call_state:	ATM_IE_CALL_STATE	ie_call_state
 	    cdpn:	ATM_IE_CDPN		ie_cdpn
 	    cdps:	ATM_IE_CDPS		ie_cdps
 	    cgpn:	ATM_IE_CGPN		ie_cgpn
 	    cgps:	ATM_IE_CGPS		ie_cgps
-	    cause:	ATM_IE_CAUSE		[2] ie_cause
+	    cause:	ATM_IE_CAUSE		ie_cause
+/*
+	    cause2:	ATM_IE_CAUSE		ie_cause
+*/
 	    conn_id:	ATM_IE_CONN_ID		ie_conn_id
 	    qos:	ATM_IE_QOS		ie_qos
 	    bbrep:	ATM_IE_BBREP		ie_bbrep
diff -ur --new-file old/atm/qgen/uni40 new/atm/qgen/uni40
--- old/atm/qgen/uni40	Wed Nov  6 19:12:55 1996
+++ new/atm/qgen/uni40	Wed Jan 29 12:20:36 1997
@@ -494,13 +494,13 @@
 	    td:		ATM_IE_TD		ie_td
 	    bbcap:	ATM_IE_BBCAP		ie_bbcap
 	    bhli:	ATM_IE_BHLI		ie_bhli
-	    blli:	ATM_IE_BLLI		[3] ie_blli
+	    blli:	ATM_IE_BLLI		ie_blli
 	    call_state:	ATM_IE_CALL_STATE	ie_call_state
 	    cdpn:	ATM_IE_CDPN		ie_cdpn
 	    cdps:	ATM_IE_CDPS		ie_cdps
 	    cgpn:	ATM_IE_CGPN		ie_cgpn
 	    cgps:	ATM_IE_CGPS		ie_cgps
-	    cause:	ATM_IE_CAUSE		[2] ie_cause
+	    cause:	ATM_IE_CAUSE		ie_cause
 	    conn_id:	ATM_IE_CONN_ID		ie_conn_id
 	    e2e_td:	ATM_IE_E2E_TD		ie_e2e_td
 	    qos:	ATM_IE_QOS		ie_qos
diff -ur --new-file old/atm/sigd/atmsigd.8 new/atm/sigd/atmsigd.8
--- old/atm/sigd/atmsigd.8	Thu Oct 17 14:52:55 1996
+++ new/atm/sigd/atmsigd.8	Mon Jan 20 13:27:04 1997
@@ -1,4 +1,4 @@
-.TH ATMSIGD 8 "Oct 17, 1996" "Linux" "Maintenance Commands"
+.TH ATMSIGD 8 "Jan 20, 1997" "Linux" "Maintenance Commands"
 .SH NAME
 atmsigd \- ATM signaling demon
 .SH SYNOPSIS
@@ -29,6 +29,8 @@
 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.
@@ -57,7 +59,7 @@
 .PD 0
 .TP 25
 .B /etc/atmsigd.conf
-configuration file
+default configuration file
 .TP 25
 .B /var/tmp/atmsigd.\fIpid\fB.status.\fIversion\fP
 default location of status dumps
diff -ur --new-file old/atm/sigd/atmsigd.c new/atm/sigd/atmsigd.c
--- old/atm/sigd/atmsigd.c	Thu Oct 10 18:39:55 1996
+++ new/atm/sigd/atmsigd.c	Wed Jan 29 21:08:31 1997
@@ -1,6 +1,6 @@
 /* atmsigd.c - ATM signaling demon */
 
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
 
 
 #include <stdlib.h>
@@ -349,13 +349,24 @@
     background = 0;
     memset(&signaling_pvc,0,sizeof(signaling_pvc));
     signaling_pvc.sap_addr.vci = 5;
+    /* 1st pass to get the -c option */
+    while ((c = getopt(argc,argv,"bc:dD:l: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.");
+	    return 1;
+	}
+    /* process all other options but -c */
+    optind = 0;
     while ((c = getopt(argc,argv,"bc:dD:l:nNP:q:t:")) != EOF)
 	switch (c) {
 	    case 'b':
 		background = 1;
 		break;
 	    case 'c':
-		config_file = optarg;
+		/* already handled */
 		break;
 	    case 'd':
 		set_verbosity(NULL,DIAG_DEBUG);
@@ -405,12 +416,6 @@
 #endif
 #endif
       "/AAL5, version " VERSION);
-    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.");
-	    return 1;
-	}
     if (dump_dir)
 	if (chdir(dump_dir) < 0)
 	    diag(COMPONENT,DIAG_ERROR,"chdir %s: %s",dump_dir,strerror(errno));
diff -ur --new-file old/atm/sigd/atmsigd.conf.4 new/atm/sigd/atmsigd.conf.4
--- old/atm/sigd/atmsigd.conf.4	Thu Oct 10 20:34:07 1996
+++ new/atm/sigd/atmsigd.conf.4	Mon Jan 20 13:26:45 1997
@@ -1,4 +1,4 @@
-.TH ATMSIGD.CONF 4 "Oct 10, 1996" "Linux" "File Formats"
+.TH ATMSIGD.CONF 4 "Jan 20, 1997" "Linux" "File Formats"
 .SH NAME
 atmsigd.conf \- configuration file for the ATM signaling demon
 .SH SYNOPSIS
@@ -83,9 +83,9 @@
 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.
-.SH BUGS
+.P
 If an option is specified in \fBatmsigd.conf\fP and on the command
-line, \fBatmsigd.conf\fP wins.
+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
diff -ur --new-file old/atm/sigd/q2931.c new/atm/sigd/q2931.c
--- old/atm/sigd/q2931.c	Fri Nov 29 17:28:45 1996
+++ new/atm/sigd/q2931.c	Tue Jan 21 04:08:04 1997
@@ -1,6 +1,6 @@
 /* q2931.c - Processing of incoming Q.2931 messages */
  
-/* Written 1995,1996 by Werner Almesberger, EPFL-LRC */
+/* Written 1995-1997 by Werner Almesberger, EPFL-LRC */
  
 
 #include <stdarg.h>
@@ -18,6 +18,7 @@
 #include "sap.h"
 #include "io.h"
 #include "timeout.h"
+#include "trace.h"
 
 #define __KERNEL__ /* since that's what we effectively are */
 #include <linux/errno.h>
@@ -229,6 +230,7 @@
 
 static void q2931_call(SOCKET *sock,unsigned char mid)
 {
+    char buffer[MAX_ATM_ADDR_LEN+1];
     int error;
 
     switch (mid) {
@@ -312,8 +314,10 @@
 	    send_kernel(sock->id,0,as_okay,0,&sock->pvc,NULL,sock->local,
 	      &sock->qos);
 	    new_state(sock,ss_connected);
-	    diag(COMPONENT,DIAG_INFO,"Active open succeeded (CR 0x%06X)",
-	      sock->call_ref);
+	    if (atm2text(buffer,MAX_ATM_ADDR_LEN+1,(struct sockaddr *)
+	      sock->local,0) < 0) strcpy(buffer,"<invalid>");
+	    diag(COMPONENT,DIAG_INFO,"Active open succeeded (CR 0x%06X, "
+	      "ID 0x%08x, addr %s)",sock->call_ref,sock->id,buffer);
 	    return;
 	case QMSG_CONN_ACK: /* ACCEPTING, WAIT_REL, REL_REQ */
 	    diag(COMPONENT,DIAG_DEBUG,"CA vpi.vci=%d.%d",
@@ -326,8 +330,10 @@
 	    STOP_TIMER(sock);
 	    send_kernel(sock->id,0,as_okay,0,NULL,NULL,sock->local,NULL);
 	    new_state(sock,ss_connected);
-	    diag(COMPONENT,DIAG_INFO,"Passive open succeeded (CR 0x%06X)",
-	      sock->call_ref);
+	    if (atm2text(buffer,MAX_ATM_ADDR_LEN+1, (struct sockaddr *)
+	      sock->local,0) < 0) strcpy(buffer,"<invalid>");
+	    diag(COMPONENT,DIAG_INFO,"Passive open succeeded (CR 0x%06X, "
+	      "ID 0x%08x, addr %s)",sock->call_ref,sock->id,buffer);
 	    return;
 	case QMSG_RELEASE: /* all states */
 	    {
@@ -462,6 +468,7 @@
 {
     SOCKET *curr,*next;
 
+    trace_msg("SAAL went down");
     for (curr = sockets; curr; curr = next) {
 	next = curr->next;
 	if (curr->q2931_state != qs_null)
@@ -476,6 +483,7 @@
 {
     SOCKET *curr;
 
+    trace_msg("SAAL came up");
     if (!t309) return;
     stop_timer(t309);
     t309 = NULL;